C#:
using System;
using System.Collections.Generic;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using UnityEngine;
using Newtonsoft.Json;
namespace Oxide.Plugins
{
/// <summary>
/// Определение класса CuiRawImageComponent для использования изображений по URL
/// </summary>
public class CuiRawImageComponent : ICuiComponent, ICuiColor
{
public string Type => "UnityEngine.UI.RawImage";
[JsonProperty("sprite")]
public string? Sprite { get; set; }
public string? Color { get; set; }
[JsonProperty("material")]
public string? Material { get; set; }
[JsonProperty("url")]
public string? Url { get; set; }
[JsonProperty("png")]
public string? Png { get; set; }
[JsonProperty("fadeIn")]
public float FadeIn { get; set; }
[JsonProperty("fitToParent")]
public bool FitToParent { get; set; }
}
[Info("TeleportMenu", "YourName", "1.0.0")]
[Description("Provides a UI teleport menu for players to teleport to different locations")]
public class TeleportMenu : RustPlugin
{
#region Fields
private const string UIMainPanel = "TeleportMenu.MainPanel";
private const string UICloseButton = "TeleportMenu.CloseButton";
private const string UIHomePanel = "TeleportMenu.HomePanel";
private const string UIFriendsPanel = "TeleportMenu.FriendsPanel";
private const string UIHomeButtonsPanel = "TeleportMenu.HomeButtonsPanel";
private const string UITownButton = "TeleportMenu.TownButton";
private const string UICampButton = "TeleportMenu.CampButton";
private const string UIAcceptButton = "TeleportMenu.AcceptButton";
private const string UIDeclineButton = "TeleportMenu.DeclineButton";
private readonly Dictionary<ulong, Timer> pendingTeleports = new();
private readonly Dictionary<ulong, Dictionary<string, Vector3>> homes;
private readonly Dictionary<ulong, List<TeleportRequest>> teleportRequests = new();
private readonly Dictionary<ulong, TeleportData> teleportData = new();
private PluginConfig config;
/// <summary>
/// Добавляем словарь для хранения координат монументов
/// </summary>
private readonly Dictionary<string, Vector3> monumentPositions = new();
private static readonly string[] stringArray = new[] { "дом1" };
private static readonly string[] stringArray0 = new[] { "дом2" };
#endregion Fields
#region Configuration
private sealed class PluginConfig
{
public float TeleportDelay { get; set; } = 5.0f;
public float TeleportCooldown { get; set; } = 60.0f;
public int MaxHomesPerPlayer { get; set; } = 999;
public bool EnableTPToTown { get; set; } = true;
public bool EnableTPToCamp { get; set; } = true;
public bool EnableTPToFriends { get; set; } = true;
public Vector3 TownLocation { get; set; } = new Vector3(0, 0, 0);
public Vector3 CampLocation { get; set; } = new Vector3(0, 0, 0);
public string MainColor { get; set; } = "0.4 0.34 0.6 0.98";
public string BackgroundColor { get; set; } = "0.22 0.22 0.29 0.9";
public string ButtonColor { get; set; } = "0.42 0.42 0.55 0.8";
public string TextColor { get; set; } = "1 1 1 0.9";
public string AcceptColor { get; set; } = "0.2 0.6 0.2 0.9";
public string DeclineColor { get; set; } = "0.6 0.2 0.2 0.9";
public bool AutoDetectMonuments { get; set; } = true;
/// <summary>
/// Настройки привилегий по телепортациям
/// </summary>
public Dictionary<string, TeleportLimits> PermissionLimits { get; set; } = new Dictionary<string, TeleportLimits>
{
["default"] = new TeleportLimits { Home = 20, Town = 15, Camp = 15, Friend = 25 },
["vip"] = new TeleportLimits { Home = 30, Town = 25, Camp = 25, Friend = 35 },
["premium"] = new TeleportLimits { Home = 50, Town = 50, Camp = 50, Friend = 50 }
};
}
public class TeleportLimits
{
public int Home { get; set; }
public int Town { get; set; }
public int Camp { get; set; }
public int Friend { get; set; }
}
protected override void LoadDefaultConfig()
{
config = new PluginConfig();
Config.WriteObject(config, true);
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null)
{
LoadDefaultConfig();
}
}
catch (Exception ex)
{
PrintError($"Error loading config: {ex.Message}");
LoadDefaultConfig();
}
}
protected override void SaveConfig()
{
Config.WriteObject(config);
}
#endregion Configuration
#region Data Models
private sealed class TeleportData
{
public DateTime LastTeleport { get; set; } = DateTime.MinValue;
public Dictionary<string, int> TeleportCounts { get; set; } = new Dictionary<string, int>
{
["home"] = 0,
["town"] = 0,
["camp"] = 0,
["friend"] = 0
};
public int TeleportCount { get; set; }
}
private sealed class TeleportRequest
{
public ulong RequesterId { get; set; }
public ulong TargetId { get; set; }
public DateTime RequestTime { get; set; }
public Timer? ExpiryTimer { get; set; }
}
#endregion Data Models
#region Localization
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["TPHome"] = "ТП ДОМОЙ",
["TPToFriend"] = "ТП К ДРУГУ",
["Town"] = "В ГОРОД",
["Camp"] = "В ЛАГЕРЬ",
["Save"] = "Нажми, чтобы сохранить ДОМ",
["Empty"] = "ПУСТО",
["Accept"] = "ПРИНЯТЬ",
["Decline"] = "ОТКЛОНИТЬ",
["MenuTitle"] = "ТЕЛЕПОРТ МЕНЮ",
["CooldownMessage"] = "Подождите {0} секунд перед следующим телепортом",
["TeleportCountdown"] = "Телепортация через {0} секунд",
["TeleportSuccess"] = "Телепортация успешна!",
["TeleportFailed"] = "Телепортация отменена",
["NoPermission"] = "У вас нет разрешения на использование этой команды",
["HomeSet"] = "Дом {0} установлен",
["HomeLimitReached"] = "Вы достигли лимита домов ({0})",
["RequestSent"] = "Запрос на телепортацию отправлен игроку {0}",
["RequestReceived"] = "Игрок {0} хочет телепортироваться к вам",
["NoActiveRequests"] = "У вас нет активных запросов",
["TPRequestAccepted"] = "Запрос на телепортацию принят",
["TPRequestDeclined"] = "Запрос на телепортацию отклонен",
["RequestExpired"] = "Запрос на телепортацию истек",
["TooCloseToHostile"] = "Нельзя телепортироваться рядом с враждебной постройкой"
}, this);
}
private string GetMsg(string key, string userId = null)
{
if (key == null)
{
return "MISSING_KEY";
}
string message = lang.GetMessage(key, this, userId);
return message ?? key;
}
#endregion Localization
#region Hooks
private void Init()
{
LoadConfig();
permission.RegisterPermission("teleportmenu.use", this);
permission.RegisterPermission("teleportmenu.admin", this);
permission.RegisterPermission("teleportmenu.default", this);
permission.RegisterPermission("teleportmenu.vip", this);
permission.RegisterPermission("teleportmenu.premium", this);
cmd.AddChatCommand("tp", this, CmdTeleportMenu);
cmd.AddChatCommand("home", this, CmdHome);
cmd.AddChatCommand("town", this, CmdTown);
cmd.AddChatCommand("camp", this, CmdCamp);
cmd.AddChatCommand("tpr", this, CmdTeleportRequest);
cmd.AddChatCommand("tpa", this, CmdAcceptTeleport);
cmd.AddChatCommand("tpdecline", this, CmdDeclineTeleport);
cmd.AddChatCommand("sethome", this, CmdSetHome);
cmd.AddChatCommand("delhome", this, CmdDelHome);
cmd.AddChatCommand("savehome", this, CmdSaveHome);
cmd.AddChatCommand("savehome2", this, CmdSaveHome2);
// Добавляем консольные команды для прямого вызова из UI без чата
cmd.AddConsoleCommand("teleportmenu.sethome1", this, "ConSaveHome1");
cmd.AddConsoleCommand("teleportmenu.sethome2", this, "ConSaveHome2");
cmd.AddConsoleCommand("teleportmenu.sethome", this, "ConSetHome");
cmd.AddConsoleCommand("teleportmenu.delhome", this, "ConDelHome");
cmd.AddConsoleCommand("teleportmenu.home", this, "ConGoHome"); // Новая команда для телепортации к дому
cmd.AddConsoleCommand("teleportmenu.close", this, "ConCloseMenu"); // Новая команда для закрытия интерфейса
if (config.AutoDetectMonuments)
{
FindMonuments();
}
}
private void OnServerSave()
{
SaveData();
}
private void Unload()
{
SaveData();
foreach (BasePlayer? player in BasePlayer.activePlayerList)
{
DestroyUI(player);
}
}
private void OnPlayerDisconnected(BasePlayer player)
{
DestroyUI(player);
if (pendingTeleports.ContainsKey(player.userID))
{
pendingTeleports[player.userID]?.Destroy();
_ = pendingTeleports.Remove(player.userID);
}
}
#endregion Hooks
#region Data Management
private void SaveData()
{
Interface.Oxide.DataFileSystem.WriteObject($"{Name}/homes", homes);
}
private void LoadData()
{
// Ничего не делаем, так как поле инициализируется в конструкторе
}
#endregion Data Management
#region Commands
private void CmdTeleportMenu(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
ShowTeleportMenu(player);
}
private void CmdHome(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
if (args.Length == 0)
{
ShowTeleportMenu(player);
return;
}
string homeName = args[0];
if (!homes.ContainsKey(player.userID) || !homes[player.userID].TryGetValue(homeName, out Vector3 value))
{
player.ChatMessage($"Дом '{homeName}' не найден");
return;
}
InitiateTeleport(player, value, "home");
}
private void CmdTown(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
if (!config.EnableTPToTown)
{
player.ChatMessage("Телепортация в город отключена");
return;
}
InitiateTeleport(player, config.TownLocation, "town");
}
private void CmdCamp(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
if (!config.EnableTPToCamp)
{
player.ChatMessage("Телепортация в лагерь отключена");
return;
}
InitiateTeleport(player, config.CampLocation, "camp");
}
private void CmdTeleportRequest(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
if (!config.EnableTPToFriends)
{
player.ChatMessage("Телепортация к друзьям отключена");
return;
}
if (args.Length == 0)
{
player.ChatMessage("Укажите имя игрока или ID");
return;
}
BasePlayer target = FindPlayer(args[0], player);
if (target == null)
{
player.ChatMessage($"Игрок '{args[0]}' не найден");
return;
}
if (target == player)
{
player.ChatMessage("Вы не можете телепортироваться к себе");
return;
}
SendTeleportRequest(player, target);
}
private void CmdAcceptTeleport(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
if (!teleportRequests.ContainsKey(player.userID) || teleportRequests[player.userID].Count == 0)
{
player.ChatMessage(GetMsg("NoActiveRequests", player.UserIDString));
return;
}
TeleportRequest request = teleportRequests[player.userID][0];
BasePlayer requester = BasePlayer.FindByID(request.RequesterId);
if (requester == null)
{
player.ChatMessage("Игрок оффлайн");
RemoveTeleportRequest(player.userID, request);
return;
}
// Проверка лимитов телепортации для запрашивающего
if (!CheckTeleportLimits(requester, "friend"))
{
requester.ChatMessage("Вы достигли лимита телепортаций к друзьям");
player.ChatMessage($"Игрок {requester.displayName} достиг лимита телепортаций");
RemoveTeleportRequest(player.userID, request);
return;
}
player.ChatMessage(GetMsg("TPRequestAccepted", player.UserIDString));
requester.ChatMessage(GetMsg("TPRequestAccepted", requester.UserIDString));
InitiateTeleport(requester, player.transform.position, "friend");
RemoveTeleportRequest(player.userID, request);
}
private void CmdDeclineTeleport(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
if (!teleportRequests.ContainsKey(player.userID) || teleportRequests[player.userID].Count == 0)
{
player.ChatMessage(GetMsg("NoActiveRequests", player.UserIDString));
return;
}
TeleportRequest request = teleportRequests[player.userID][0];
BasePlayer requester = BasePlayer.FindByID(request.RequesterId);
player.ChatMessage(GetMsg("TPRequestDeclined", player.UserIDString));
requester?.ChatMessage(GetMsg("TPRequestDeclined", requester.UserIDString));
RemoveTeleportRequest(player.userID, request);
}
private void CmdSetHome(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
// Проверяем, указал ли игрок название дома
if (args.Length == 0)
{
player.ChatMessage("Ошибка: Вы должны указать название дома. Использование: /sethome [название]");
return;
}
string homeName = args[0];
// Проверяем, находится ли игрок на фундаменте
bool onFoundation = IsPlayerOnFoundation(player);
if (!onFoundation)
{
player.ChatMessage("Дом можно сохранить только стоя на фундаменте!");
return;
}
// Проверяем, находится ли игрок в зоне своего шкафа (имеет права строительства)
bool hasBuildingPrivilege = HasBuildingPrivilege(player);
if (!hasBuildingPrivilege)
{
player.ChatMessage("Дом можно сохранить только в зоне действия вашего шкафа!");
return;
}
// Сохраняем дом
if (!homes.ContainsKey(player.userID))
{
homes[player.userID] = new Dictionary<string, Vector3>();
}
// Сохраняем позицию дома
homes[player.userID][homeName] = player.transform.position;
SaveData();
player.ChatMessage($"Дом '{homeName}' установлен");
// Перезапускаем интерфейс, чтобы отобразить новый дом
if (player.IsReceivingSnapshot)
{
// Показываем UI только если игрок не в процессе загрузки
_ = timer.Once(0.5f, () => ShowTeleportMenu(player));
}
else
{
ShowTeleportMenu(player);
}
}
/// <summary>
/// Новая чат-команда для сохранения дома с именем "дом1"
/// </summary>
private void CmdSaveHome(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
// Вызываем существующий метод с предопределенным именем дома
CmdSetHome(player, "sethome", stringArray);
}
/// <summary>
/// Команда для сохранения второго дома
/// </summary>
private void CmdSaveHome2(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
// Вызываем существующий метод с именем для второго дома
CmdSetHome(player, "sethome", stringArray0);
}
/// <summary>
/// Команда для удаления дома по имени
/// </summary>
private void CmdDelHome(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "teleportmenu.use"))
{
player.ChatMessage(GetMsg("NoPermission", player.UserIDString));
return;
}
if (args.Length == 0)
{
player.ChatMessage("Укажите название дома для удаления. Использование: /delhome [название]");
return;
}
string homeName = args[0];
if (!homes.ContainsKey(player.userID) || !homes[player.userID].ContainsKey(homeName))
{
player.ChatMessage($"Дом '{homeName}' не найден");
return;
}
_ = homes[player.userID].Remove(homeName);
SaveData();
player.ChatMessage($"Дом '{homeName}' удален");
// Обновляем интерфейс после удаления дома
ShowTeleportMenu(player);
}
#endregion Commands
#region UI Methods
private void ShowTeleportMenu(BasePlayer player)
{
DestroyUI(player);
CuiElementContainer container = new();
// Панель-подложка
_ = container.Add(new CuiPanel
{
RectTransform = { AnchorMin = "0.25 0.3", AnchorMax = "0.75 0.8" },
Image = { Color = "0 0 0 0" },
CursorEnabled = true
}, "Overlay", UIMainPanel);
// Фоновое изображение для главной панели
container.Add(new CuiElement
{
Parent = UIMainPanel,
Components =
{
new CuiRawImageComponent
{
Url = "https://i.imgur.com/qvVhltR.png",
Color = "1 1 1 1",
FitToParent = true
},
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" }
}
});
// Кнопка закрытия интерфейса
_ = container.Add(new CuiButton
{
Button = { Color = "0.0 0.0 0.0 0.0", Command = "teleportmenu.close" },
RectTransform = { AnchorMin = "0.95 0.94", AnchorMax = "1 1" },
Text = { Text = "", Align = TextAnchor.MiddleCenter, Color = config.TextColor, FontSize = 18 }
}, UIMainPanel);
// Добавляем кнопки принять/отклонить телепортацию в верхней части интерфейса
_ = container.Add(new CuiButton
{
Button = { Color = "0.0 0.0 0.0 0.0", Command = "chat.say /tpa", FadeIn = 0.2f },
RectTransform = { AnchorMin = "0.75 0.8", AnchorMax = "0.9 0.88" },
Text = { Text = "", Align = TextAnchor.MiddleCenter, Color = config.TextColor, FontSize = 14 }
}, UIMainPanel, "AcceptTpButton");
_ = container.Add(new CuiButton
{
Button = { Color = "0.0 0.0 0.0 0.0", Command = "chat.say /tpdecline", FadeIn = 0.2f },
RectTransform = { AnchorMin = "0.519 0.275", AnchorMax = "0.703 0.325" },
Text = { Text = "", Align = TextAnchor.MiddleCenter, Color = config.TextColor, FontSize = 14 }
}, UIMainPanel, "DeclineTpButton");
// Пустая нижняя панель (только для сохранения структуры и корректной работы)
_ = container.Add(new CuiPanel
{
RectTransform = { AnchorMin = "0 0.1", AnchorMax = "1 0.63" },
Image = { Color = "0 0 0 0" } // Полностью прозрачная
}, UIMainPanel, "HomesPanel");
// Получаем информацию о телепортациях для отображения в нижней части меню
string townLimitText = GetTeleportLimitText(player, "town");
string homeLimitText = GetTeleportLimitText(player, "home");
string campLimitText = GetTeleportLimitText(player, "camp");
string friendLimitText = GetTeleportLimitText(player, "friend");
// Добавляем кнопку телепортации в город (Outpost)
const string townPanelName = "TownButton";
CuiPanel townPanel = new()
{
RectTransform = { AnchorMin = "0.295 0.60", AnchorMax = "0.485 0.65" },
Image = { Color = "0 0 0 0" },
CursorEnabled = true
};
_ = container.Add(townPanel, UIMainPanel, townPanelName);
// Добавляем прозрачную кнопку для обработки клика в город с подсветкой
_ = container.Add(new CuiButton
{
Button = { Color = "0.0 0.0 0.0 0.0", Command = "chat.say /town", FadeIn = 0.2f },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "", Align = TextAnchor.MiddleCenter, Color = config.TextColor, FontSize = 14 }
}, townPanelName);
// Добавляем кнопку телепортации в лагерь бандитов (Bandit Camp)
const string campPanelName = "CampButton";
CuiPanel campPanel = new()
{
RectTransform = { AnchorMin = "0.519 0.60", AnchorMax = "0.704 0.65" },
Image = { Color = "0 0 0 0" },
CursorEnabled = true
};
_ = container.Add(campPanel, UIMainPanel, campPanelName);
// Добавляем прозрачную кнопку для обработки клика в лагерь с подсветкой
_ = container.Add(new CuiButton
{
Button = { Color = "0.0 0.0 0.0 0.0", Command = "chat.say /camp", FadeIn = 0.2f },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "", Align = TextAnchor.MiddleCenter, Color = config.TextColor, FontSize = 14 }
}, campPanelName);
// Создаем кнопки быстрого доступа к домам с фиксированным положением
if (homes.ContainsKey(player.userID) && homes[player.userID].Count > 0)
{
int count = 0;
foreach (KeyValuePair<string, Vector3> home in homes[player.userID])
{
if (count >= 10)
{
break; // Увеличиваем лимит с 3 до 10
}
// Используем фиксированные координаты для кнопок, расположенных вертикально
string anchorMin, anchorMax;
if (count == 0)
{
// Первая кнопка на месте "Сохранить текущее место как дом"
anchorMin = "0.06 0.70";
anchorMax = "0.265 0.77";
}
else if (count == 1)
{
// Вторая кнопка ниже первой
anchorMin = "0.06 0.62";
anchorMax = "0.265 0.69";
}
else if (count == 2)
{
// Третья кнопка еще ниже
anchorMin = "0.06 0.54";
anchorMax = "0.265 0.61";
}
else if (count == 3)
{
// Четвертая кнопка
anchorMin = "0.06 0.46";
anchorMax = "0.265 0.53";
}
else if (count == 4)
{
// Пятая кнопка
anchorMin = "0.06 0.38";
anchorMax = "0.265 0.45";
}
else if (count == 5)
{
// Шестая кнопка
anchorMin = "0.06 0.30";
anchorMax = "0.265 0.37";
}
else if (count == 6)
{
// Седьмая кнопка
anchorMin = "0.06 0.22";
anchorMax = "0.265 0.29";
}
else if (count == 7)
{
// Восьмая кнопка
anchorMin = "0.06 0.14";
anchorMax = "0.265 0.21";
}
else if (count == 8)
{
// Девятая кнопка
anchorMin = "0.39 0.70";
anchorMax = "0.59 0.77";
}
else // count == 9
{
// Десятая кнопка
anchorMin = "0.39 0.62";
anchorMax = "0.59 0.69";
}
string panelName = $"QuickHome_{count}";
// Создаем кнопку телепортации к дому
CuiPanel homePanel = new()
{
RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax },
Image = { Color = "0 0 0 0" }
};
_ = container.Add(homePanel, UIMainPanel, panelName);
// Добавляем фоновое изображение
container.Add(new CuiElement
{
Parent = panelName,
Components =
{
new CuiRawImageComponent
{
Url = "https://i.imgur.com/rNv8SmY.png",
Color = "1 1 1 1",
FitToParent = true
},
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" }
}
});
// Добавляем текст кнопки
container.Add(new CuiElement
{
Parent = panelName,
Components =
{
new CuiTextComponent
{
Text = $"Телепорт: {home.Key}",
Font = "robotocondensed-bold.ttf",
FontSize = 14,
Align = TextAnchor.MiddleCenter,
Color = config.TextColor
},
new CuiRectTransformComponent { AnchorMin = "0.05 0.1", AnchorMax = "0.95 0.9" }
}
});
// Добавляем прозрачную кнопку для обработки клика
_ = container.Add(new CuiButton
{
Button = { Color = "0 0 0 0", Command = $"teleportmenu.home {home.Key}" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "" }
}, panelName);
// Создаем кнопку удаления дома справа от кнопки телепортации
string deleteAnchorMin, deleteAnchorMax;
if (count == 0)
{
deleteAnchorMin = "0.27 0.70";
deleteAnchorMax = "0.37 0.77";
}
else if (count == 1)
{
deleteAnchorMin = "0.27 0.62";
deleteAnchorMax = "0.37 0.69";
}
else if (count == 2)
{
deleteAnchorMin = "0.27 0.54";
deleteAnchorMax = "0.37 0.61";
}
else if (count == 3)
{
deleteAnchorMin = "0.27 0.46";
deleteAnchorMax = "0.37 0.53";
}
else if (count == 4)
{
deleteAnchorMin = "0.27 0.38";
deleteAnchorMax = "0.37 0.45";
}
else if (count == 5)
{
deleteAnchorMin = "0.27 0.30";
deleteAnchorMax = "0.37 0.37";
}
else if (count == 6)
{
deleteAnchorMin = "0.27 0.22";
deleteAnchorMax = "0.37 0.29";
}
else if (count == 7)
{
deleteAnchorMin = "0.27 0.14";
deleteAnchorMax = "0.37 0.21";
}
else if (count == 8)
{
deleteAnchorMin = "0.39 0.70";
deleteAnchorMax = "0.49 0.77";
}
else // count == 9
{
deleteAnchorMin = "0.39 0.62";
deleteAnchorMax = "0.49 0.69";
}
// Добавляем кнопку удаления
_ = container.Add(new CuiButton
{
Button = { Color = "0.6 0.2 0.2 0.8", Command = $"teleportmenu.delhome {home.Key}", FadeIn = 0.2f },
RectTransform = { AnchorMin = deleteAnchorMin, AnchorMax = deleteAnchorMax },
Text = { Text = "X", Align = TextAnchor.MiddleCenter, Color = config.TextColor, FontSize = 14 }
}, UIMainPanel, $"DeleteHome_{count}");
count++;
}
// Определяем позицию для кнопки создания дома - всегда ниже последней кнопки телепортации
string saveButtonAnchorMin, saveButtonAnchorMax;
// В зависимости от количества домов, смещаем вниз
if (count == 0)
{
saveButtonAnchorMin = "0.06 0.70";
saveButtonAnchorMax = "0.265 0.77";
}
else if (count == 1)
{
saveButtonAnchorMin = "0.06 0.62";
saveButtonAnchorMax = "0.265 0.69";
}
else if (count == 2)
{
saveButtonAnchorMin = "0.06 0.54";
saveButtonAnchorMax = "0.265 0.61";
}
else if (count == 3)
{
saveButtonAnchorMin = "0.06 0.46";
saveButtonAnchorMax = "0.265 0.53";
}
else if (count == 4)
{
saveButtonAnchorMin = "0.06 0.38";
saveButtonAnchorMax = "0.265 0.45";
}
else if (count == 5)
{
saveButtonAnchorMin = "0.06 0.30";
saveButtonAnchorMax = "0.265 0.37";
}
else if (count == 6)
{
saveButtonAnchorMin = "0.06 0.22";
saveButtonAnchorMax = "0.265 0.29";
}
else if (count == 7)
{
saveButtonAnchorMin = "0.06 0.14";
saveButtonAnchorMax = "0.265 0.21";
}
else if (count == 8)
{
saveButtonAnchorMin = "0.39 0.70";
saveButtonAnchorMax = "0.59 0.77";
}
else // count == 9
{
saveButtonAnchorMin = "0.39 0.62";
saveButtonAnchorMax = "0.59 0.69";
}
// Создаем кнопку "Сохранить текущее место как дом" на новой позиции
CuiPanel saveHomePanel = new()
{
RectTransform = { AnchorMin = saveButtonAnchorMin, AnchorMax = saveButtonAnchorMax },
Image = { Color = "0 0 0 0" },
CursorEnabled = true
};
_ = container.Add(saveHomePanel, UIMainPanel, "SaveHomePanel");
// Добавляем фоновое изображение для кнопки
container.Add(new CuiElement
{
Parent = "SaveHomePanel",
Components =
{
new CuiRawImageComponent
{
Url = "https://i.imgur.com/rNv8SmY.png",
Color = "1 1 1 1",
FitToParent = true
},
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" }
}
});
// Добавляем текст
container.Add(new CuiElement
{
Parent = "SaveHomePanel",
Components =
{
new CuiTextComponent
{
Text = "Сохранить текущее место как дом",
Font = "robotocondensed-bold.ttf",
FontSize = 14,
Align = TextAnchor.MiddleCenter,
Color = config.TextColor
},
new CuiRectTransformComponent { AnchorMin = "0.05 0.1", AnchorMax = "0.95 0.9" }
}
});
// Генерируем уникальное имя для нового дома
string newHomeName = $"дом_{DateTime.Now:HHmmss}";
// Добавляем кнопку поверх (прозрачную, только для обработки клика)
_ = container.Add(new CuiButton
{
Button = { Color = "0.4 0.4 0.4 0.1", Command = $"teleportmenu.sethome {newHomeName}", FadeIn = 0.2f },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "" }
}, "SaveHomePanel");
}
else
{
// Если нет домов, размещаем кнопку создания дома на стандартной позиции
CuiPanel saveHomePanel = new()
{
RectTransform = { AnchorMin = "0.06 0.70", AnchorMax = "0.265 0.77" },
Image = { Color = "0 0 0 0" }
};
_ = container.Add(saveHomePanel, UIMainPanel, "SaveHomePanel");
// Добавляем фоновое изображение для кнопки
container.Add(new CuiElement
{
Parent = "SaveHomePanel",
Components =
{
new CuiRawImageComponent
{
Url = "https://i.imgur.com/rNv8SmY.png",
Color = "1 1 1 1",
FitToParent = true
},
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" }
}
});
// Добавляем текст
container.Add(new CuiElement
{
Parent = "SaveHomePanel",
Components =
{
new CuiTextComponent
{
Text = "Сохранить текущее место как дом",
Font = "robotocondensed-bold.ttf",
FontSize = 14,
Align = TextAnchor.MiddleCenter,
Color = config.TextColor
},
new CuiRectTransformComponent { AnchorMin = "0.05 0.1", AnchorMax = "0.95 0.9" }
}
});
// Генерируем уникальное имя для нового дома
string newHomeName = $"дом_{DateTime.Now:HHmmss}";
// Добавляем кнопку поверх (прозрачную, только для обработки клика)
_ = container.Add(new CuiButton
{
Button = { Color = "0 0 0 0", Command = $"teleportmenu.sethome {newHomeName}" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "" }
}, "SaveHomePanel");
}
// Отображение запросов телепортации
if (teleportRequests.ContainsKey(player.userID) && teleportRequests[player.userID].Count > 0)
{
TeleportRequest request = teleportRequests[player.userID][0];
BasePlayer requester = BasePlayer.FindByID(request.RequesterId);
if (requester != null)
{
// Панель запроса - делаем полупрозрачной
_ = container.Add(new CuiPanel
{
RectTransform = { AnchorMin = "0.2 0.4", AnchorMax = "0.8 0.7" },
Image = { Color = "0.1 0.1 0.1 0.9" }
}, UIMainPanel, "RequestPanel");
// Текст запроса
container.Add(new CuiElement
{
Parent = "RequestPanel",
Components =
{
new CuiTextComponent
{
Text = $"Игрок {requester.displayName} хочет телепортироваться к вам",
Font = "robotocondensed-regular.ttf",
FontSize = 14,
Align = TextAnchor.MiddleCenter,
Color = config.TextColor
},
new CuiRectTransformComponent { AnchorMin = "0.1 0.6", AnchorMax = "0.9 0.9" }
}
});
// Кнопки принять/отклонить - делаем полупрозрачными
_ = container.Add(new CuiButton
{
Button = { Color = "0.2 0.6 0.2 0.8", Command = "tpa" },
RectTransform = { AnchorMin = "0.1 0.2", AnchorMax = "0.45 0.4" },
Text = { Text = "ПРИНЯТЬ", Align = TextAnchor.MiddleCenter, Color = config.TextColor, FontSize = 14 }
}, "RequestPanel", UIAcceptButton);
_ = container.Add(new CuiButton
{
Button = { Color = "0.6 0.2 0.2 0.8", Command = "tpdecline" },
RectTransform = { AnchorMin = "0.55 0.2", AnchorMax = "0.9 0.4" },
Text = { Text = "ОТКЛОНИТЬ", Align = TextAnchor.MiddleCenter, Color = config.TextColor, FontSize = 14 }
}, "RequestPanel", UIDeclineButton);
}
}
// Добавляем информационную панель внизу меню с лимитами телепортаций
// Создаем 4 панели для отображения лимитов телепортаций
// Панель для лимита телепортаций в город
const string townLimitPanelName = "TownLimitPanel";
CuiPanel townLimitPanel = new()
{
RectTransform = { AnchorMin = "0.43 0.112", AnchorMax = "0.47 0.162" },
Image = { Color = "0.0 0.0 0.0 0.0" }
};
_ = container.Add(townLimitPanel, UIMainPanel, townLimitPanelName);
// Добавляем текст с информацией о лимитах телепортаций в город
container.Add(new CuiElement
{
Parent = townLimitPanelName,
Components =
{
new CuiTextComponent
{
Text = $" {townLimitText}",
Font = "robotocondensed-bold.ttf",
FontSize = 16,
Align = TextAnchor.MiddleCenter,
Color = "1 1 1 0.9"
},
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" }
}
});
// Панель для лимита телепортаций домой
const string homeLimitPanelName = "HomeLimitPanel";
CuiPanel homeLimitPanel = new()
{
RectTransform = { AnchorMin = "0.625 0.112", AnchorMax = "0.675 0.162" },
Image = { Color = "0.0 0.0 0.0 0.0" }
};
_ = container.Add(homeLimitPanel, UIMainPanel, homeLimitPanelName);
// Добавляем текст с информацией о лимитах телепортаций домой
container.Add(new CuiElement
{
Parent = homeLimitPanelName,
Components =
{
new CuiTextComponent
{
Text = $" {homeLimitText}",
Font = "robotocondensed-bold.ttf",
FontSize = 16,
Align = TextAnchor.MiddleCenter,
Color = "1 1 1 0.9"
},
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" }
}
});
// Панель для лимита телепортаций в лагерь
const string campLimitPanelName = "CampLimitPanel";
CuiPanel campLimitPanel = new()
{
RectTransform = { AnchorMin = "0.43 0.025", AnchorMax = "0.47 0.075" },
Image = { Color = "0.0 0.0 0.0 0.0" }
};
_ = container.Add(campLimitPanel, UIMainPanel, campLimitPanelName);
// Добавляем текст с информацией о лимитах телепортаций в лагерь
container.Add(new CuiElement
{
Parent = campLimitPanelName,
Components =
{
new CuiTextComponent
{
Text = $" {campLimitText}",
Font = "robotocondensed-bold.ttf",
FontSize = 16,
Align = TextAnchor.MiddleCenter,
Color = "1 1 1 0.9"
},
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" }
}
});
// Панель для лимита телепортаций к другу
const string friendLimitPanelName = "FriendLimitPanel";
CuiPanel friendLimitPanel = new()
{
RectTransform = { AnchorMin = "0.625 0.025", AnchorMax = "0.675
// ... [Code preview truncated due to length. Full source in attached file] ...