C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("TPMenu", "SARO", "1.0.0")]
public class TPMenu : RustPlugin
{
private readonly PluginConfig config;
private const string MainLayer = "tpmenu";
private const float CACHE_TIMEOUT = 5f;
private const float BUTTON_HEIGHT = 0.04f;
private const float BUTTON_SPACING = 0.005f;
private const float BUTTON_WIDTH = 0.075f;
private const float PANEL_BG_COLOR = 0.12f;
private const float BUTTON_BG_COLOR = 0.19f;
private const float OUTLINE_COLOR = 0.30f;
private const float TEXT_COLOR = 0.8f;
private const int FONT_SIZE = 12;
private const string FONT_NAME = "robotocondensed-regular.ttf";
private const string BOLD_FONT_NAME = "robotocondensed-bold.ttf";
private const float BASE_FONT_SIZE = 12f;
private const float MIN_INTERFACE_SCALE = 0.6f;
private const float MAX_INTERFACE_SCALE = 1.0f;
private const float MAX_FONT_SIZE = 24f;
private const int BUTTONS_PER_PAGE = 5;
private const float SCROLL_UP_Y = 0.715f;
private const float SCROLL_DOWN_Y = 0.515f;
private const int TITLE_FONT_SIZE = 20;
private const int SMALL_FONT_SIZE = 10;
[PluginReference]
private readonly Plugin? ImageLibrary;
[PluginReference]
private readonly Plugin? Friends;
[PluginReference]
private readonly Plugin? MutualPermission;
[PluginReference]
private readonly Plugin? NTeleportation;
[PluginReference]
private readonly Plugin? Teleport;
[PluginReference]
private readonly Plugin? Teleportation;
[PluginReference]
private readonly Plugin? HomesGUI;
[PluginReference]
private readonly Plugin? Home;
[PluginReference]
private readonly Plugin? IQTeleportation;
private readonly Dictionary<string, string> imageCache;
private readonly Dictionary<ulong, Dictionary<string, Vector3>> homesCache;
private readonly Dictionary<ulong, List<ulong>> friendsCache;
private readonly Dictionary<ulong, DateTime> lastHomesUpdate;
private readonly Dictionary<ulong, DateTime> lastFriendsUpdate;
private readonly Dictionary<ulong, bool> PlayersList;
private readonly Dictionary<ulong, int> menuPageByUser;
private readonly Dictionary<ulong, bool> actionsInProgress;
private const float LEFT_PANEL_X = 0.377f;
private const float CENTER_PANEL_X = 0.453f;
private const float RIGHT_PANEL_X = 0.547f;
private const float PANEL_WIDTH = 0.075f;
private const float TOP_PANEL_Y = 0.915f;
private const float PANEL_HEIGHT = 0.15f;
private const float BASE_BUTTON_HEIGHT = 0.04f;
private const float BASE_BUTTON_SPACING = 0.005f;
private const float BASE_BUTTON_WIDTH = 0.075f;
public TPMenu()
{
config = new PluginConfig();
imageCache = new Dictionary<string, string>();
homesCache = new Dictionary<ulong, Dictionary<string, Vector3>>();
friendsCache = new Dictionary<ulong, List<ulong>>();
lastHomesUpdate = new Dictionary<ulong, DateTime>();
lastFriendsUpdate = new Dictionary<ulong, DateTime>();
PlayersList = new Dictionary<ulong, bool>();
menuPageByUser = new Dictionary<ulong, int>();
actionsInProgress = new Dictionary<ulong, bool>();
ImageLibrary = plugins.Find("ImageLibrary");
Friends = plugins.Find("Friends");
MutualPermission = plugins.Find("MutualPermission");
NTeleportation = plugins.Find("NTeleportation");
Teleport = plugins.Find("Teleport");
Teleportation = plugins.Find("Teleportation");
HomesGUI = plugins.Find("HomesGUI");
Home = plugins.Find("Home");
IQTeleportation = plugins.Find("IQTeleportation");
}
private void Loaded()
{
LoadData();
permission.RegisterPermission("tpmenu.use", this);
LoadConfig();
cmd.RemoveConsoleCommand("tpmenu", this);
cmd.AddConsoleCommand("tpmenu", this, nameof(cmdOpentpmenuConsole));
if (config?.MainSettings?.Commands != null)
{
foreach (string command in config.MainSettings.Commands)
{
cmd.AddChatCommand(command, this, nameof(cmdOpentpmenu));
}
}
}
[HookMethod("OnPluginLoaded")]
private void OnPluginLoaded(Plugin plugin)
{
// Method intentionally left empty.}
protected override void LoadDefaultConfig()
{
PrintWarning("Создание новой конфигурации по умолчанию...");
if (config == null)
{
return;
}
config.MainSettings = new MainSettings
{
Commands = new List<string> { "menu", "tpmenu" }.Distinct().ToList(),
ShowOnConnect = false,
ShowOnlyFirstConnect = false,
};
config.Homes = new ButtonConfig
{
Title = "ДОМА",
ButtonColor = "0.19 0.19 0.21 0.75",
TextColor = "1 1 1 1",
ButtonText = new Dictionary<string, string> { { "ru", "ДОМА" } },
};
config.Friends = new ButtonConfig
{
Title = "ДРУЗЬЯ",
ButtonColor = "0.19 0.19 0.21 0.75",
TextColor = "1 1 1 1",
ButtonText = new Dictionary<string, string> { { "ru", "ДРУЗЬЯ" } },
};
config.Home = new HomeSettings();
config.FriendsSettings = new FriendSettings();
}
private void LoadConfig()
{
try
{
PluginConfig loadedConfig = null;
if (Config.Exists())
{
loadedConfig = Config.ReadObject<PluginConfig>();
}
if (loadedConfig != null)
{
if (loadedConfig.MainSettings != null)
{
config.MainSettings = loadedConfig.MainSettings;
if (config.MainSettings.Commands != null)
{
config.MainSettings.Commands = config.MainSettings.Commands.Distinct().ToList();
}
if (config.MainSettings.ButtonSettings?.Buttons != null)
{
config.MainSettings.ButtonSettings.Buttons = config.MainSettings.ButtonSettings.Buttons.GroupBy(b => new
{
b.Command,
b.Text,
}).Select(g => g.First()).ToList();
}
}
if (loadedConfig.Homes != null)
{
config.Homes = loadedConfig.Homes;
}
if (loadedConfig.Friends != null)
{
config.Friends = loadedConfig.Friends;
}
if (loadedConfig.Home != null)
{
config.Home = loadedConfig.Home;
}
if (loadedConfig.FriendsSettings != null)
{
config.FriendsSettings = loadedConfig.FriendsSettings;
}
if (loadedConfig.ConfigVersion != default)
{
config.ConfigVersion = loadedConfig.ConfigVersion;
}
}
else
{
LoadDefaultConfig();
SaveConfig();
}
config.MainSettings ??= new MainSettings();
config.MainSettings.ButtonSettings ??= new ButtonSettings();
if (config.MainSettings.Commands == null || config.MainSettings.Commands.Count == 0)
{
config.MainSettings.Commands = new List<string> { "menu", "tpmenu" };
}
else
{
config.MainSettings.Commands = config.MainSettings.Commands.Distinct().ToList();
}
if (
config.MainSettings.ButtonSettings.Buttons == null
|| config.MainSettings.ButtonSettings.Buttons.Count == 0
)
{
config.MainSettings.ButtonSettings.Buttons = new List<MenuButton>
{
new() { Command = "chat.say /block", Text = "БЛОКИРОВКА" },
new() { Command = "chat.say /help", Text = "ПОМОЩЬ" },
};
}
else
{
config.MainSettings.ButtonSettings.Buttons = config.MainSettings.ButtonSettings.Buttons.GroupBy(b => new { b.Command, b.Text }).Select(g => g.First()).ToList();
}
SaveConfig();
}
catch (Exception ex)
{
PrintError($"Error in LoadConfig: {ex.Message}\n{ex.StackTrace}");
}
}
private void UpdateConfigValues()
{
if (config.ConfigVersion < PluginConfig.DefaultConfig().ConfigVersion)
{
PrintWarning("Updating old config version...");
PluginConfig baseConfig = PluginConfig.DefaultConfig();
config.ConfigVersion = baseConfig.ConfigVersion;
SaveConfig();
}
}
protected override void SaveConfig()
{
Config.WriteObject(config, true);
}
private void LoadData()
{
try
{
PlayersList.Clear();
Dictionary<ulong, bool> data = Interface.Oxide.DataFileSystem.ReadObject<
Dictionary<ulong, bool>
>("tpmenu_Players");
if (data != null)
{
foreach (KeyValuePair<ulong, bool> kvp in data)
{
PlayersList[kvp.Key] = kvp.Value;
}
}
}
catch (Exception ex)
{
PrintError($"Error loading data: {ex.Message}");
PlayersList.Clear();
}
}
private void SaveData()
{
try
{
Interface.Oxide.DataFileSystem.WriteObject("tpmenu_Players", PlayersList);
}
catch (Exception ex)
{
PrintError($"Error saving data: {ex.Message}");
}
}
private void OnPlayerConnected(BasePlayer player)
{
if (!config.MainSettings.ShowOnConnect)
{
return;
}
if (config.MainSettings.ShowOnlyFirstConnect)
{
if (!PlayersList.ContainsKey(player.userID))
{
PlayersList[player.userID] = true;
SaveData();
_ = timer.Once(2f, () => CreateMenu(player));
}
}
else
{
_ = timer.Once(2f, () => CreateMenu(player));
}
}
private void OnPlayerDisconnected(BasePlayer player, string reason)
{
_ = homesCache.Remove(player.userID);
_ = friendsCache.Remove(player.userID);
_ = lastHomesUpdate.Remove(player.userID);
_ = lastFriendsUpdate.Remove(player.userID);
_ = actionsInProgress.Remove(player.userID);
}
private Dictionary<string, Vector3> GetHomes(ulong playerid)
{
try
{
if (
homesCache.TryGetValue(playerid, out Dictionary<string, Vector3>? cachedHomes)
&& lastHomesUpdate.TryGetValue(playerid, out DateTime lastUpdate)
&& (DateTime.Now - lastUpdate).TotalSeconds < CACHE_TIMEOUT
)
{
return cachedHomes;
}
BasePlayer player = BasePlayer.FindByID(playerid);
if (player == null)
{
return new Dictionary<string, Vector3>();
}
Dictionary<string, Vector3> homes = new();
Plugin IQTeleportation = plugins.Find("IQTeleportation");
if (IQTeleportation != null)
{
try
{
object homeResult = IQTeleportation.Call("GetHomes", playerid);
if (homeResult is Dictionary<string, Vector3> iqHomes)
{
foreach (KeyValuePair<string, Vector3> home in iqHomes)
{
homes[home.Key] = home.Value;
}
}
}
catch (Exception ex)
{
PrintError($"Ошибка при получении домов из IQTeleportation: {ex.Message}");
}
}
if (NTeleportation != null)
{
if (
NTeleportation?.Call("API_GetHomes", player)
is Dictionary<string, Vector3> ntHomes
)
{
foreach (KeyValuePair<string, Vector3> home in ntHomes)
{
homes[home.Key] = home.Value;
}
}
}
if (Teleport != null)
{
if (
Teleport?.Call("ApiGetHomes", playerid)
is Dictionary<string, Vector3> tpHomes
)
{
foreach (KeyValuePair<string, Vector3> home in tpHomes)
{
homes[home.Key] = home.Value;
}
}
}
if (Teleportation != null)
{
if (
Teleportation?.Call("GetHomes", playerid)
is Dictionary<string, Vector3> telHomes
)
{
foreach (KeyValuePair<string, Vector3> home in telHomes)
{
homes[home.Key] = home.Value;
}
}
}
if (HomesGUI != null)
{
if (
HomesGUI?.Call("GetPlayerHomes", playerid.ToString())
is Dictionary<string, Vector3> guiHomes
)
{
foreach (KeyValuePair<string, Vector3> home in guiHomes)
{
homes[home.Key] = home.Value;
}
}
}
homesCache[playerid] = homes;
lastHomesUpdate[playerid] = DateTime.Now;
return homes;
}
catch (Exception ex)
{
PrintError($"Error in GetHomes: {ex.Message}");
return new Dictionary<string, Vector3>();
}
}
private List<ulong> GetFriends(ulong playerid)
{
try
{
if (
friendsCache.TryGetValue(playerid, out List<ulong>? cachedFriends)
&& lastFriendsUpdate.TryGetValue(playerid, out DateTime lastUpdate)
&& (DateTime.Now - lastUpdate).TotalSeconds < CACHE_TIMEOUT
)
{
return cachedFriends;
}
List<ulong> result = new();
if (Friends != null && Friends?.Call("GetFriends", playerid) is ulong[] friends)
{
result.AddRange(friends);
}
friendsCache[playerid] = result;
lastFriendsUpdate[playerid] = DateTime.Now;
return result;
}
catch (Exception ex)
{
PrintError($"Error in GetFriends: {ex.Message}");
return new List<ulong>();
}
}
private bool IsPlayerOnline(ulong friendId)
{
BasePlayer player = BasePlayer.FindByID(friendId);
return player?.IsConnected == true;
}
private string GetFriendName(ulong friendId)
{
try
{
BasePlayer player = BasePlayer.FindByID(friendId);
if (player != null)
{
return player.displayName.Length > 10
? player.displayName.Substring(0, 7) + "*"
: player.displayName;
}
IPlayer offlinePlayer = covalence.Players.FindPlayerById(friendId.ToString());
if (offlinePlayer != null)
{
string name = offlinePlayer.Name;
return name.Length > 10 ? name.Substring(0, 7) + "*" : name;
}
if (Friends != null)
{
if (
Friends?.Call("GetFriendData", friendId)
is Dictionary<string, object> friendData
&& friendData.TryGetValue("name", out object? value)
)
{
string name = value.ToString();
return name.Length > 10 ? name.Substring(0, 7) + "*" : name;
}
}
return friendId.ToString();
}
catch (Exception ex)
{
PrintError($"Error in GetFriendName: {ex.Message}");
return friendId.ToString();
}
}
private bool IsTeamLeader(BasePlayer player)
{
RelationshipManager.PlayerTeam team =
RelationshipManager.ServerInstance.FindPlayersTeam(player.userID);
return team?.teamLeader == player.userID;
}
private string GetGridString(Vector3 position)
{
Vector2 adjPosition = new((World.Size / 2) + position.x, (World.Size / 2) - position.z);
return $"{NumberToString((int)(adjPosition.x / 150))}{(int)(adjPosition.y / 150)}";
}
private string NumberToString(int number)
{
bool a = number > 26;
char c = (char)(65 + (a ? number - 26 : number));
return a ? "A" + c : c.ToString();
}
private int GetFreeHomesCount(BasePlayer player)
{
try
{
const int defaultMaxHomes = 5;
if (config?.Home != null)
{
int count = config.Home.MaxHomes;
if (count <= 0)
{
count = defaultMaxHomes;
}
if (config.Home.PermissionsHomes?.Count > 0)
{
foreach (KeyValuePair<string, int> perm in config.Home.PermissionsHomes)
{
if (permission.UserHasPermission(player.UserIDString, perm.Key))
{
count = perm.Value;
break;
}
}
}
return count;
}
else if (config?.Homes != null)
{
int count = config.Homes.MaxHomes;
if (count <= 0)
{
count = defaultMaxHomes;
}
if (config.Homes.PermissionsHomes?.Count > 0)
{
foreach (KeyValuePair<string, int> perm in config.Homes.PermissionsHomes)
{
if (permission.UserHasPermission(player.UserIDString, perm.Key))
{
count = perm.Value;
break;
}
}
}
return count;
}
PrintWarning(
"Не удалось найти настройки для домов в конфигурации, используем значение по умолчанию"
);
return defaultMaxHomes;
}
catch (Exception ex)
{
PrintError($"Error in GetFreeHomesCount: {ex.Message}");
return 5;
}
}
private int GetCurrentPage(BasePlayer player)
{
List<KeyValuePair<string, Vector3>> homes = GetHomes(player.userID).ToList();
return (int)Math.Ceiling(homes.Count / 5.0f);
}
private string GetImage(string url)
{
if (ImageLibrary == null || string.IsNullOrEmpty(url))
{
return string.Empty;
}
return imageCache.TryGetValue(url, out string cachedImage)
? cachedImage
: (imageCache[url] = (string)ImageLibrary.Call("GetImage", url) ?? string.Empty);
}
private void cmdOpentpmenu(BasePlayer player, string command, string[] args)
{
try
{
if (!permission.UserHasPermission(player.UserIDString, "tpmenu.use"))
{
player.ChatMessage("У вас нет разрешения на использование этой команды");
return;
}
DestroyUI(player);
CreateMenu(player);
}
catch (Exception ex)
{
PrintError($"Error in cmdOpentpmenu: {ex.Message}\n{ex.StackTrace}");
}
}
[ConsoleCommand("tpmenu")]
private void cmdOpentpmenuConsole(ConsoleSystem.Arg arg)
{
try
{
BasePlayer player = arg.Player();
if (player == null)
{
return;
}
if (!permission.UserHasPermission(player.UserIDString, "tpmenu.use"))
{
player.ChatMessage("У вас нет разрешения на использование этой команды");
return;
}
DestroyUI(player);
CreateMenu(player);
}
catch (Exception ex)
{
PrintError($"Error in cmdOpentpmenuConsole: {ex.Message}\n{ex.StackTrace}");
}
}
private void DestroyAllButtons(BasePlayer player)
{
for (int i = 0; i < 600; i++)
{
_ = CuiHelper.DestroyUi(player, $"{MainLayer}.ButtonOutline.{i}");
}
for (int i = 0; i < 10; i++)
{
_ = CuiHelper.DestroyUi(player, $"HomeRemove_{i}");
}
_ = CuiHelper.DestroyUi(player, "PrevPageButton");
_ = CuiHelper.DestroyUi(player, "PageIndicator");
_ = CuiHelper.DestroyUi(player, "NextPageButton");
}
[ConsoleCommand("tpmenu_UI")]
private void cmdtpmenuCommands(ConsoleSystem.Arg args)
{
BasePlayer player = args.Player();
if (player == null)
{
return;
}
switch (args.GetString(0).ToLower(System.Globalization.CultureInfo.CurrentCulture))
{
case "close":
DestroyUI(player);
break;
case "home":
if (args.Args.Length > 1)
{
string homeName = args.GetString(1);
player.Command("chat.say", $"/home {homeName}");
_ = timer.Once(0.1f, () => CreateMenu(player));
}
break;
case "removehome":
if (args.Args.Length > 1)
{
if (IsActionInProgress(player.userID))
{
player.ChatMessage("Дождитесь завершения предыдущего действия");
return;
}
SetActionInProgress(player.userID, true);
string homeName = args.GetString(1);
player.Command("chat.say", $"/removehome {homeName}");
_ = homesCache.Remove(player.userID);
_ = lastHomesUpdate.Remove(player.userID);
_ = timer.Once(
0.1f,
() =>
{
CreateMenu(player);
SetActionInProgress(player.userID, false);
}
);
}
break;
case "switchpage":
if (args.Args.Length > 1 && int.TryParse(args.GetString(1), out int page))
{
if (IsActionInProgress(player.userID))
{
return;
}
SetActionInProgress(player.userID, true);
try
{
Dictionary<string, Vector3> homes = GetHomes(player.userID);
int totalPages = (int)Math.Ceiling(homes.Count / 5.0f);
if (totalPages == 0)
{
totalPages = 1;
}
if (page < 1)
{
page = 1;
}
if (page > totalPages)
{
page = totalPages;
}
DestroyUI(player);
CreateMenu(player, page);
}
finally
{
SetActionInProgress(player.userID, false);
}
}
break;
case "scroll":
if (args.Args.Length > 1)
{
DestroyUI(player);
CreateMenu(player);
}
break;
case "sethome":
if (args.Args.Length > 1)
{
if (IsActionInProgress(player.userID))
{
player.ChatMessage("Дождитесь завершения предыдущего действия");
return;
}
SetActionInProgress(player.userID, true);
string gridPos = args.GetString(1);
string homeName = gridPos.Length > 7 ? gridPos.Substring(0, 7) : gridPos;
player.Command("chat.say", $"/sethome {homeName}");
_ = homesCache.Remove(player.userID);
_ = lastHomesUpdate.Remove(player.userID);
_ = timer.Once(
0.1f,
() =>
{
CreateMenu(player);
SetActionInProgress(player.userID, false);
}
);
}
break;
case "friend":
if (
args.Args.Length > 1
&& ulong.TryParse(args.GetString(1), out ulong friendId)
)
{
DestroyUI(player);
player.Command("chat.say", $"/tpr {friendId}");
}
break;
case "addfriend":
DestroyUI(player);
if (Friends != null)
{
_ = Friends.Call("ShowFriends", player);
ResetCache(player.userID);
}
else
{
player.ChatMessage("Плагин Friends не найден!");
}
break;
case "removefriend":
if (
args.Player() != null
&& ulong.TryParse(args.GetString(1), out ulong friendToRemove)
)
{
_ = Friends.Call("fremove", friendToRemove);
ResetCache(args.Player().userID);
ResetCache(friendToRemove);
UpdateFriendsPanel(args.Player());
}
break;
}
}
private void UpdateHomePanel(BasePlayer player, int page)
{
try
{
if (config.Home == null)
{
PrintError("Home settings are null! Initializing default settings.");
config.Home = new HomeSettings();
SaveConfig();
}
CuiElementContainer container = new();
const string homePanel = MainLayer + ".Home";
_ = container.Add(
new CuiLabel
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text =
{
Text = $"<color=#ff8c08>{config.Home.Title}</color>",
FontSize = 20,
Align = TextAnchor.MiddleCenter,
Font = BOLD_FONT_NAME,
},
},
homePanel + ".Title"
);
List<KeyValuePair<string, Vector3>> homes = GetHomes(player.userID).ToList();
int maxHomes = GetFreeHomesCount(player);
int totalPages = (int)Math.Ceiling(homes.Count / 5.0f);
if (totalPages == 0)
{
totalPages = 1;
}
if (page > totalPages)
{
page = totalPages;
}
List<KeyValuePair<string, Vector3>> pageHomes = homes.Skip((page - 1) * 5).Take(5).ToList();
const float startButtonY = 0.635f;
const float homeButtonX = 0.377f;
for (int i = 0; i < Math.Min(pageHomes.Count, 5); i++)
{
KeyValuePair<string, Vector3> home = pageHomes[i];
float buttonY = startButtonY - (i * (BUTTON_HEIGHT + BUTTON_SPACING));
ButtonConfig buttonConfig = new()
{
Command = $"tpmenu_UI home {home.Key}",
ButtonColor = "0.19 0.19 0.21 0.75",
TextColor = "1 1 1 1",
ButtonText = new Dictionary<string, string>
{
{ "ru", $"<color=#ff8c08>{home.Key}</color>" },
},
};
CreateButton(container, buttonConfig, homeButtonX, buttonY, 100 + i, player);
string removeButtonId = $"HomeRemove_{i}";
_ = container.Add(
new CuiButton
{
RectTransform =
{
AnchorMin =
$"{homeButtonX + BUTTON_WIDTH - 0.015f} {buttonY + 0.005f}",
AnchorMax =
$"{homeButtonX + BUTTON_WIDTH - 0.002f} {buttonY + BUTTON_HEIGHT - 0.005f}",
},
Button =
{
Color = "0.8 0 0 0.7",
Command = $"tpmenu_UI removehome {home.Key}",
},
Text =
{
Text = "×",
FontSize = FONT_SIZE + 4,
Align = TextAnchor.MiddleCenter,
Color = "1 1 1 1",
Font = BOLD_FONT_NAME,
},
},
MainLayer,
removeButtonId
);
}
int buttonCount = Math.Min(pageHomes.Count, 5);
bool showNavigationButtons = homes.Count > 5;
if (page == 1 && homes.Count > 5)
{
const float buttonY = startButtonY - (5 * (BUTTON_HEIGHT + BUTTON_SPACING));
const float smallButtonWidth = BUTTON_WIDTH / 4;
const float textWidth = BUTTON_WIDTH - (2 * smallButtonWidth);
const float textX = homeButtonX + smallButtonWidth;
_ = container.Add(
new CuiLabel
{
RectTransform =
{
AnchorMin = $"{textX} {buttonY}",
AnchorMax = $"{textX + textWidth} {buttonY + BUTTON_HEIGHT}",
},
Text =
{
Text = "<color=#FFFFFF>1/2</color>",
FontSize = FONT_SIZE,
Align = TextAnchor.MiddleCenter,
Font = BOLD_FONT_NAME,
},
},
MainLayer,
"PageIndicator"
);
const float rightButtonX = textX + textWidth;
_ = container.Add(
new CuiButton
{
RectTransform =
{
AnchorMin = $"{rightButtonX} {buttonY}",
AnchorMax =
$"{rightButtonX + smallButtonWidth} {buttonY + BUTTON_HEIGHT}",
},
Button =
{
Color = "0.19 0.19 0.21 0.75",
Command = "tpmenu_UI switchpage 2",
},
Text =
{
Text = "►",
FontSize = FONT_SIZE,
Align = TextAnchor.MiddleCenter,
Color = "1 1 1 0.8",
Font = BOLD_FONT_NAME,
},
},
MainLayer,
"NextPageButton"
);
}
else if (page > 1)
{
const float buttonY = startButtonY - (5 * (BUTTON_HEIGHT + BUTTON_SPACING));
const float smallButtonWidth = BUTTON_WIDTH / 4;
_ = container.Add(
new CuiButton
{
RectTransform =
{
AnchorMin = $"{homeButtonX} {buttonY}",
AnchorMax =
$"{homeButtonX + smallButtonWidth} {buttonY + BUTTON_HEIGHT}",
},
Button =
{
Color = "0.19 0.19 0.21 0.75",
Command = "tpmenu_UI switchpage 1",
},
Text =
{
Text = "◄",
FontSize = FONT_SIZE,
Align = TextAnchor.MiddleCenter,
Color = "1 1 1 0.8",
Font = BOLD_FONT_NAME,
},
},
MainLayer,
"PrevPageButton"
);
const float textWidth = BUTTON_WIDTH - (2 * smallButtonWidth);
const float textX = homeButtonX + smallButtonWidth;
_ = container.Add(
new CuiLabel
{
RectTransform =
{
AnchorMin = $"{textX} {buttonY}",
AnchorMax = $"{textX + textWidth} {buttonY + BUTTON_HEIGHT}",
},
Text =
{
Text = "<color=#FFFFFF>2/2</color>",
FontSize = FONT_SIZE,
Align = TextAnchor.MiddleCenter,
Font = BOLD_FONT_NAME,
},
},
MainLayer,
"PageIndicator"
);
}
if (homes.Count < maxHomes)
{
if (page == 1 && homes.Count <= 5)
{
float buttonY =
startButtonY - (buttonCount * (BUTTON_HEIGHT + BUTTON_SPACING));
ButtonConfig addButtonConfig = new()
{
Command =
$"tpmenu_UI sethome {GetGridString(player.transform.position)}",
ButtonColor = "0.19 0.19 0.21 0.75",
TextColor = "1 1 1 1",
ButtonText = new Dictionary<string, string>
{
{ "ru", "<color=#00FF00>ДОБАВИТЬ</color>" },
},
};
CreateButton(container, addButtonConfig, homeButtonX, buttonY, 200, player);
}
else if (page > 1 && pageHomes.Count < 5)
{
float buttonY =
startButtonY - (pageHomes.Count * (BUTTON_HEIGHT + BUTTON_SPACING));
ButtonConfig addButtonConfig = new()
{
Command =
$"tpmenu_UI sethome {GetGridString(player.transform.position)}",
ButtonColor = "0.19 0.19 0.21 1",
TextColor = "1 1 1 1",
ButtonText = new Dictionary<string, string>
{
{ "ru", "<color=#00FF00>ДОБАВИТЬ</color>" },
},
};
CreateButton(container, addButtonConfig, homeButtonX, buttonY, 200, player);
}
}
_ = container.Add(
new CuiLabel
{
RectTransform = { AnchorMin = "0 0.3", AnchorMax = "1 0.35" },
Text =
{
Text = "<color=#FFFFFF>АВТОЗАКРЫТИЕ</color>",
FontSize = 16,
Align = TextAnchor.MiddleCenter,
Font = BOLD_FONT_NAME,
},
},
homePanel
);
const float autodoorButtonY =
startButtonY - (6 * (BUTTON_HEIGHT + BUTTON_SPACING)) - 0.02f;
ButtonConfig autodoorButtonConfig = new()
{
Command = "chat.say /ad",
ButtonColor = "0.19 0.19 0.21 0.75",
TextColor = "1 1 1 1",
ButtonText = new Dictionary<string, string>
{
{ "ru", "<color=#FFFFFF>АВТОЗАКРЫТИЕ</color>" },
},
};
CreateButton(
container,
autodoorButtonConfig,
homeButtonX,
autodoorButtonY,
250,
player
);
_ = CuiHelper.AddUi(player, container);
}
catch (Exception ex)
{
PrintError($"Error in UpdateHomePanel: {ex.Message}\n{ex.StackTrace}");
}
}
private void UpdateFriendsPanel(BasePlayer player)
{
try
{
_ = CuiHelper.DestroyUi(player, "FriendsPanel");
DestroyAllButtons(player);
CuiElementContainer container = new();
CreateFriendsPanel(container, player);
_ = CuiHelper.AddUi(player, container);
}
catch (Exception ex)
{
PrintError($"Error in UpdateFriendsPanel: {ex.Message}\n{ex.StackTrace}");
}
}
private void DestroyUI(BasePlayer player)
{
try
{
_ = CuiHelper.DestroyUi(player, MainLayer);
}
catch (Exception ex)
{
PrintError($"Error in DestroyUI: {ex.Message}\n{ex.StackTrace}");
}
}
private void Unload()
{
foreach (BasePlayer? player in BasePlayer.activePlayerList)
{
DestroyUI(player);
}
}
private void CreateMenu(BasePlayer player, int page = 1)
{
try
{
DestroyUI(player);
CuiElementContainer container = new();
_ = container.Add(
new CuiPanel
{
CursorEnabled = true,
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Image = { Color = "0 0 0 0" },
},
"Overlay",
MainLayer
);
_ = container.Add(
new CuiButton
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Button = { Color = "0 0 0 0", Command = "tpmenu_UI close" },
Text = { Text = "" },
},
MainLayer,
MainLayer + ".Background"
);
CreatePanels(container, player, TITLE_FONT_SIZE);
CreateButtons(container, player, page);
_ = CuiHelper.AddUi(player, container);
}
catch (Exception ex)
{
PrintError($"Error in CreateMenu: {ex.Message}");
}
}
private void CreatePanels(
CuiElementContainer container,
BasePlayer player,
int titleFontSize
)
{
CreatePanel(
container,
"TeleportPanel",
$"{LEFT_PANEL_X} {TOP_PANEL_Y - PANEL_HEIGHT}",
$"{LEFT_PANEL_X + PANEL_WIDTH} {TOP_PANEL_Y}",
"<color=#ff8c08>ТЕЛЕПОРТ</color>",
titleFontSize
);
CreatePanel(
container,
"MenuPanel",
$"{CENTER_PANEL_X} {TOP_PANEL_Y - PANEL_HEIGHT}",
$"{CENTER_PANEL_X + PANEL_WIDTH} {TOP_PANEL_Y}",
"",
titleFontSize
);
_ = container.Add(
new CuiPanel
{
RectTransform =
{
AnchorMin = $"{CENTER_PANEL_X} {TOP_PANEL_Y - PANEL_HEIGHT}",
AnchorMax = $"{RIGHT_PANEL_X} {TOP_PANEL_Y}",
},
Image = { Color = "0 0 0 0" },
},
MainLayer,
"MenuTitlePanel"
);
_ = container.Add(
new CuiLabel
{
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text =
{
Text = "<color=#ff8c08>МЕНЮ</color>",
FontSize = titleFontSize,
Align = TextAnchor.MiddleCenter,
Font = BOLD_FONT_NAME,
},
},
"MenuTitlePanel"
);
CreatePanel(
container,
"TradePanel",
$"{RIGHT_PANEL_X} {TOP_PANEL_Y - PANEL_HEIGHT}",
$"{RIGHT_PANEL_X + PANEL_WIDTH} {TOP_PANEL_Y}",
"<color=#ff8c08>ОБМЕН</color>",
titleFontSize
);
CreateTeleportButtons(container, player);
CreateTradeButtons(container, player);
_ = container.Add(
new CuiLabel
{
RectTransform =
{
AnchorMin = $"{CENTER_PANEL_X - 0.02} 0.37",
AnchorMax = $"{RIGHT_PANEL_X + 0.02} 0.41",
},
Text =
{
Text = "<color=#ff8c08>Frenetic</color> Rust",
FontSize = titleFontSize,
Align = TextAnchor.MiddleCenter,
Font = BOLD_FONT_NAME,
},
},
MainLayer
);
_ = container.Add(
new CuiLabel
// ... [Code preview truncated due to length. Full source in attached file] ...