Иконка ресурса

FREE Friends : 1.22.2 1.22.2

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// #define TESTING

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using ProtoBuf;
using UnityEngine;
using Random = UnityEngine.Random;

namespace Oxide.Plugins
{
	[Info("Friends", "Mevent", "1.22.2")]
	[Description("Adds a friends system with a visual interface for quick interaction")]
	public class Friends : RustPlugin
	{
		#region Fields

		[PluginReference] private Plugin
			ImageLibrary = null,
			Notifications = null,
			PlayerList = null,
			PlayerDatabase = null,
			Bank = null;

		private const string Layer = "Com.Mevent.Main";

		private static Friends _instance;

		private readonly Dictionary<ulong, EntityEntry> _playerEntities = new Dictionary<ulong, EntityEntry>();

		private class EntityEntry
		{
			public HashSet<AutoTurret> AutoTurrets = new HashSet<AutoTurret>();
			public HashSet<BuildingPrivlidge> BuildingPrivileges = new HashSet<BuildingPrivlidge>();
			public HashSet<CodeLock> CodeLocks = new HashSet<CodeLock>();
		}

		private enum AutoAuthType
		{
			All,
			Turret,
			Cupboard,
			Doors,
			Containers
		}

		private enum BtnType
		{
			None,
			Doors,
			Cupboard,
			Turrets,
			Containers,
			FriendlyFire,
			Sams
		}

		#endregion

		#region Config

		private static Configuration _config;

		private class Configuration
		{
			[JsonProperty(PropertyName = "Добавлять в команду | Add to team")]
			public bool AddTeam = true;

			[JsonProperty(PropertyName = "Максимальное кол-во друзей | Max Friends")]
			public int MaxFriendsAmount = 3;

			[JsonProperty(PropertyName = "Задержка между сообщениями FF | Delay between FF messages")]
			public float FFDelay = 1;

			[JsonProperty(PropertyName =
				"Закрывать интерфейс после нажатия на кнопку | Close the interface after clicking on the button")]
			public bool AutoClose = true;

			[JsonProperty(PropertyName = "Огонь по друзьям | Friendly Fire")]
			public bool FriendlyFire = false;

			[JsonProperty(PropertyName = "Двери | Doors")]
			public bool Doors = true;

			[JsonProperty(PropertyName = "Турели | Turrets")]
			public bool Turrets = true;

			[JsonProperty(PropertyName = "Шкаф | Cupboard")]
			public bool Cupboard = true;

			[JsonProperty(PropertyName = "Ящики | Containers")]
			public bool Containers = true;

			[JsonProperty(PropertyName = "ПВО | SAMs")]
			public bool SAMs = true;

			[JsonProperty(PropertyName = "Добавлять друга к остальным друзьям? | Add a friend to other friends?")]
			public bool UseTeams = true;

			[JsonProperty(PropertyName = "Включить логирование в консоль? | Enable logging to the console?")]
			public bool LogToConsole = true;

			[JsonProperty(PropertyName = "Включить логирование в файл? | Enable logging to the file?")]
			public bool LogToFile = true;

			[JsonProperty(PropertyName = "Фон | Background")]
			public IPanel Background = new IPanel
			{
				AnchorMin = "0 0", AnchorMax = "1 1",
				OffsetMin = "0 0", OffsetMax = "0 0",
				Image = string.Empty,
				Color = new IColor("#0D1F4E", 95),
				isRaw = false,
				Sprite = "Assets/Content/UI/UI.Background.Tile.psd",
				Material = "Assets/Icons/IconMaterial.mat"
			};

			[JsonProperty(PropertyName = "Заглавие | Title")]
			public IText Title = new IText
			{
				AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5",
				OffsetMin = "-150 300", OffsetMax = "150 360",
				Font = "robotocondensed-bold.ttf",
				Align = TextAnchor.MiddleCenter,
				FontSize = 38,
				Color = new IColor("#FFFFFF", 100)
			};

			[JsonProperty(PropertyName = "Закрыть | Close")]
			public IText Close = new IText
			{
				AnchorMin = "1 1", AnchorMax = "1 1",
				OffsetMin = "-35 -35", OffsetMax = "-5 -5",
				Font = "robotocondensed-bold.ttf",
				Align = TextAnchor.MiddleCenter,
				FontSize = 24,
				Color = new IColor("#FFFFFF", 100)
			};

			[JsonProperty(PropertyName = "Ошибка | Error")]
			public IText Error = new IText
			{
				AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5",
				OffsetMin = "-300 -150", OffsetMax = "300 150",
				Font = "robotocondensed-bold.ttf",
				Align = TextAnchor.MiddleCenter,
				FontSize = 38,
				Color = new IColor("#FFFFFF", 100)
			};

			[JsonProperty(PropertyName = "Назад | Back")]
			public IText Back = new IText
			{
				AnchorMin = "0 0.5", AnchorMax = "0 0.5",
				OffsetMin = "0 -40", OffsetMax = "65 40",
				Font = "robotocondensed-bold.ttf",
				Align = TextAnchor.MiddleCenter,
				FontSize = 60,
				Color = new IColor("#FFFFFF", 100)
			};

			[JsonProperty(PropertyName = "Вперёд | Next")]
			public IText Next = new IText
			{
				AnchorMin = "1 0.5", AnchorMax = "1 0.5",
				OffsetMin = "-65 -40", OffsetMax = "0 40",
				Font = "robotocondensed-bold.ttf",
				Align = TextAnchor.MiddleCenter,
				FontSize = 60,
				Color = new IColor("#FFFFFF", 100)
			};

			[JsonProperty(PropertyName = "Настройка интерфейса | Interface Settings")]
			public IFriendPanel Panel = new IFriendPanel
			{
				AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5",
				Height = 100,
				Width = 820,
				Margin = 20,
				Count = 4,
				Color = new IColor("#1D3676", 100),
				Avatar = new InterfacePosition
				{
					AnchorMin = "0 0", AnchorMax = "0 0",
					OffsetMin = "5 5", OffsetMax = "95 95"
				},
				Nickname = new NickName
				{
					AnchorMin = "0 0", AnchorMax = "0 0",
					OffsetMin = "100 40", OffsetMax = "500 100",
					Align = TextAnchor.LowerLeft,
					Font = "robotocondensed-regular.ttf",
					Color = new IColor("#FFFFFF", 100),
					FontSize = 38,
					MaxLength = 30
				},
				Status = new SText
				{
					AnchorMin = "0 0", AnchorMax = "0 0",
					OffsetMin = "100 0", OffsetMax = "200 50",
					Align = TextAnchor.UpperLeft,
					Font = "robotocondensed-regular.ttf",
					Color = new IColor("#B5FFC9", 100),
					OfflineColor = new IColor("#B46292", 100),
					FontSize = 16
				},
				Button = new IButton
				{
					AnchorMin = "1 0.5", AnchorMax = "1 0.5",
					Height = 35,
					Width = 120,
					Margin = 5
				},
				Buttons = new List<FButton>
				{
					new FButton
					{
						Type = BtnType.Doors,
						Text = "Двери",
						Command = "friend doors {user}",
						ActiveColor = new IColor("#5D8FDF", 95),
						DisactiveColor = new IColor("#5D8FDF", 35),
						FontSize = 20,
						Font = "robotocondensed-regular.ttf",
						Align = TextAnchor.MiddleCenter,
						TColor = new IColor("#1D3676", 100)
					},
					new FButton
					{
						Type = BtnType.Cupboard,
						Text = "Шкаф",
						Command = "friend cupboard {user}",
						ActiveColor = new IColor("#5D8FDF", 95),
						DisactiveColor = new IColor("#5D8FDF", 35),
						FontSize = 20,
						Font = "robotocondensed-regular.ttf",
						Align = TextAnchor.MiddleCenter,
						TColor = new IColor("#1D3676", 100)
					},
					new FButton
					{
						Type = BtnType.Containers,
						Text = "Ящики",
						Command = "friend containers {user}",
						ActiveColor = new IColor("#5D8FDF", 95),
						DisactiveColor = new IColor("#5D8FDF", 35),
						FontSize = 20,
						Font = "robotocondensed-regular.ttf",
						Align = TextAnchor.MiddleCenter,
						TColor = new IColor("#1D3676", 100)
					},
					new FButton
					{
						Type = BtnType.Turrets,
						Text = "Турели",
						Command = "friend turrets {user}",
						ActiveColor = new IColor("#5D8FDF", 95),
						DisactiveColor = new IColor("#5D8FDF", 35),
						FontSize = 20,
						Font = "robotocondensed-regular.ttf",
						Align = TextAnchor.MiddleCenter,
						TColor = new IColor("#1D3676", 100)
					},
					new FButton
					{
						Type = BtnType.None,
						Text = "Исключить",
						Command = "friend remove {user}",
						ActiveColor = new IColor("#5D8FDF", 95),
						DisactiveColor = new IColor("#5D8FDF", 35),
						FontSize = 20,
						Font = "robotocondensed-regular.ttf",
						Align = TextAnchor.MiddleCenter,
						TColor = new IColor("#1D3676", 100)
					},
					new FButton
					{
						Type = BtnType.FriendlyFire,
						Text = "Урон",
						Command = "ff {user}",
						ActiveColor = new IColor("#5D8FDF", 95),
						DisactiveColor = new IColor("#5D8FDF", 35),
						FontSize = 20,
						Font = "robotocondensed-regular.ttf",
						Align = TextAnchor.MiddleCenter,
						TColor = new IColor("#1D3676", 100)
					},
					new FButton
					{
						Type = BtnType.None,
						Text = "ТП",
						Command = "tpr {user}",
						ActiveColor = new IColor("#5D8FDF", 95),
						DisactiveColor = new IColor("#5D8FDF", 35),
						FontSize = 20,
						Font = "robotocondensed-regular.ttf",
						Align = TextAnchor.MiddleCenter,
						TColor = new IColor("#1D3676", 100)
					},
					new FButton
					{
						Type = BtnType.Sams,
						Text = "ПВО",
						Command = "friend sams {user}",
						ActiveColor = new IColor("#5D8FDF", 95),
						DisactiveColor = new IColor("#5D8FDF", 35),
						FontSize = 20,
						Font = "robotocondensed-regular.ttf",
						Align = TextAnchor.MiddleCenter,
						TColor = new IColor("#1D3676", 100)
					}
				}
			};

			[JsonProperty(PropertyName = "Приглашение в друзья | Friend Invite")]
			public INotify FriendInvite = new INotify
			{
				Image = "friend",
				Url = "https://i.imgur.com/qAxHQIn.png",
				Delay = 30,
				Buttons = new List<INotifyButton>
				{
					new INotifyButton
					{
						AnchorMin = "0.5 0", AnchorMax = "0.5 0",
						OffsetMin = "-137.5 5", OffsetMax = "-2.5 25",
						Align = TextAnchor.MiddleCenter,
						Font = "robotocondensed-regular.ttf",
						FontSize = 14,
						Color = new IColor("#FFFFFF", 95),
						BColor = new IColor("#528A4E", 95),
						Command = "friend accept",
						Msg = "Accept"
					},
					new INotifyButton
					{
						AnchorMin = "0.5 0", AnchorMax = "0.5 0",
						OffsetMin = "2.5 5", OffsetMax = "137.5 25",
						Align = TextAnchor.MiddleCenter,
						Font = "robotocondensed-regular.ttf",
						FontSize = 14,
						Color = new IColor("#FFFFFF", 95),
						BColor = new IColor("#50488A", 95),
						Command = "friend cancel",
						Msg = "Cancel"
					}
				}
			};

			[JsonProperty(PropertyName = "Оповещение | Notification")]
			public INotify Notify = new INotify
			{
				Image = "friend",
				Url = "https://i.imgur.com/qAxHQIn.png",
				Delay = 5,
				Buttons = new List<INotifyButton>()
			};

			[JsonProperty(PropertyName = "Найти друга | Find a Friend")]
			public INotifyButton FindFriendBtn = new INotifyButton
			{
				AnchorMin = "0.5 0", AnchorMax = "0.5 0",
				OffsetMin = "-150 25", OffsetMax = "150 60",
				Align = TextAnchor.MiddleCenter,
				FontSize = 14,
				Font = "robotocondensed-regular.ttf",
				Color = new IColor("#5D8FDF", 100),
				BColor = new IColor("#1D3676", 100),
				Command = "playerslist",
				Msg = "FindFriend"
			};

			[JsonProperty(PropertyName = "PlayerDatabase")]
			public PlayerDatabaseConf PlayerDatabase =
				new PlayerDatabaseConf(false, "Friends");

			public VersionNumber Version;
		}

		private class PlayerDatabaseConf
		{
			[JsonProperty(PropertyName = "Включено")]
			public bool Enabled;

			[JsonProperty(PropertyName = "Поле")] public string Field;

			public PlayerDatabaseConf(bool enabled, string field)
			{
				Enabled = enabled;
				Field = field;
			}
		}

		private class NickName : IText
		{
			[JsonProperty(PropertyName = "Максимальная длина | Max Lenght")]
			public int MaxLength;

			public new void Get(ref CuiElementContainer container, string parent = "Hud", string name = null,
				string text = "")
			{
				if (string.IsNullOrEmpty(name))
					name = CuiHelper.GetGuid();

				text = text.Substring(0, Mathf.Min(text.Length, MaxLength));

				container.Add(new CuiLabel
				{
					RectTransform =
						{AnchorMin = AnchorMin, AnchorMax = AnchorMax, OffsetMin = OffsetMin, OffsetMax = OffsetMax},
					Text =
					{
						Text = $"{text}", Align = Align, FontSize = FontSize, Color = Color.Get(),
						Font = Font
					}
				}, parent, name);
			}
		}

		private class INotify
		{
			[JsonProperty(PropertyName = "Ключ изображения | Image Key")]
			public string Image;

			[JsonProperty(PropertyName = "Ссылка на изображение | Image Url")]
			public string Url;

			[JsonProperty(PropertyName = "Время показа | Show Time")]
			public float Delay;

			[JsonProperty(PropertyName = "Кнопки | Buttons", ObjectCreationHandling = ObjectCreationHandling.Replace)]
			public List<INotifyButton> Buttons;
		}

		private class INotifyButton : IText
		{
			[JsonProperty(PropertyName = "Цвет | Color")]
			public IColor BColor;

			[JsonProperty(PropertyName = "Ключ языкового файла | Lang Key")]
			public string Msg;

			[JsonProperty(PropertyName = "Команда | Command")]
			public string Command;

			public void Get(ref CuiElementContainer container, BasePlayer player, string name = null,
				string parent = "")
			{
				if (string.IsNullOrEmpty(name))
					name = CuiHelper.GetGuid();

				container.Add(new CuiButton
				{
					RectTransform =
						{AnchorMin = AnchorMin, AnchorMax = AnchorMax, OffsetMin = OffsetMin, OffsetMax = OffsetMax},
					Text =
					{
						Text = _instance.Msg(Msg, player.UserIDString), Align = Align, FontSize = FontSize,
						Color = Color.Get(),
						Font = Font
					},
					Button =
					{
						Color = BColor.Get(),
						Command = Command
					}
				}, parent, name);
			}
		}

		private abstract class IAnchors
		{
			public string AnchorMin;

			public string AnchorMax;
		}

		private class InterfacePosition : IAnchors
		{
			public string OffsetMin;

			public string OffsetMax;
		}

		private class IFriendPanel : IAnchors
		{
			[JsonProperty(PropertyName = "Высота | Height")]
			public float Height;

			[JsonProperty(PropertyName = "Ширина | Width")]
			public float Width;

			[JsonProperty(PropertyName = "Отступ | Margin")]
			public float Margin;

			[JsonProperty(PropertyName = "Количество на странице | Count On Page")]
			public int Count;

			[JsonProperty(PropertyName = "Цвет | Color")]
			public IColor Color;

			[JsonProperty(PropertyName = "Аватарка | Avatar")]
			public InterfacePosition Avatar;

			[JsonProperty(PropertyName = "Никнейм | Nickname")]
			public NickName Nickname;

			[JsonProperty(PropertyName = "Статус | Status")]
			public SText Status;

			[JsonProperty(PropertyName = "Кнопка | Button")]
			public IButton Button;

			[JsonProperty(PropertyName = "Кнопки | Buttons", ObjectCreationHandling = ObjectCreationHandling.Replace)]
			public List<FButton> Buttons;

			public void Get(ref CuiElementContainer container, BasePlayer player, FriendData data, string parent,
				string name,
				string oMin, string oMax,
				int page,
				string mainParent)
			{
				if (string.IsNullOrEmpty(name))
					name = CuiHelper.GetGuid();

				container.Add(new CuiPanel
				{
					RectTransform =
					{
						AnchorMin = AnchorMin, AnchorMax = AnchorMax,
						OffsetMin = oMin, OffsetMax = oMax
					},
					Image = {Color = Color.Get()}
				}, parent, name);

				if (_instance.ImageLibrary && Avatar != null)
					container.Add(new CuiElement
					{
						Parent = name,
						Components =
						{
							new CuiRawImageComponent
								{Png = _instance.ImageLibrary.Call<string>("GetImage", $"avatar_{data.UserId}")},
							new CuiRectTransformComponent
							{
								AnchorMin = Avatar.AnchorMin, AnchorMax = Avatar.AnchorMax,
								OffsetMin = Avatar.OffsetMin, OffsetMax = Avatar.OffsetMax
							}
						}
					});

				#region Name

				var targetId = $"{data.UserId}";

				var target = _instance.covalence.Players.FindPlayer(targetId);
				var displayName = _instance.GetPlayerName(data.UserId);

				var status = target != null && target.IsConnected;

				Nickname?.Get(ref container, name, name + ".Nickname", displayName);

				Status?.Get(ref container, status, name, name + ".Status",
					_instance.Msg(status ? Online : Offline, player.UserIDString));

				#endregion

				#region Buttons

				if (Buttons != null)
				{
					var xSwitch = -Button.Margin;

					for (var i = 0; i < Buttons.Count; i++)
					{
						var button = Buttons[i];

						var up = i < Buttons.Count / 2;

						var ySwitch = up ? Button.Height + Button.Margin / 2f : -(Button.Margin / 2f);

						button.Get(ref container, data, name, name + $".Btn.{i}", Button.AnchorMin, Button.AnchorMax,
							$"{xSwitch - Button.Width} {ySwitch - Button.Height}",
							$"{xSwitch} {ySwitch}",
							page,
							mainParent);

						if (i + 1 == Buttons.Count / 2)
							xSwitch = -Button.Margin;
						else
							xSwitch = xSwitch - Button.Margin - Button.Width;
					}
				}

				#endregion
			}
		}

		private class IPanel : InterfacePosition
		{
			[JsonProperty(PropertyName = "Изображение | Image")]
			public string Image;

			[JsonProperty(PropertyName = "Цвет | Color")]
			public IColor Color;

			[JsonProperty(PropertyName = "Сохранять цвет изображения? | Save Image Color")]
			public bool isRaw;

			[JsonProperty(PropertyName = "Sprite")]
			public string Sprite;

			[JsonProperty(PropertyName = "Material")]
			public string Material;

			public void Get(ref CuiElementContainer container, string parent = "Hud", string name = null,
				bool cursor = false)
			{
				if (string.IsNullOrEmpty(name))
					name = CuiHelper.GetGuid();

				if (isRaw)
				{
					var element = new CuiElement
					{
						Name = name,
						Parent = parent,
						Components =
						{
							new CuiRawImageComponent
							{
								Png = !string.IsNullOrEmpty(Image)
									? _instance.ImageLibrary.Call<string>("GetImage", Image)
									: null,
								Color = Color.Get(),
								Material = Material,
								Sprite = !string.IsNullOrEmpty(Sprite) ? Sprite : "Assets/Icons/rust.png"
							},
							new CuiRectTransformComponent
							{
								AnchorMin = AnchorMin, AnchorMax = AnchorMax, OffsetMin = OffsetMin,
								OffsetMax = OffsetMax
							}
						}
					};

					if (cursor) element.Components.Add(new CuiNeedsCursorComponent());

					container.Add(element);
				}
				else
				{
					container.Add(new CuiPanel
					{
						RectTransform =
						{
							AnchorMin = AnchorMin, AnchorMax = AnchorMax, OffsetMin = OffsetMin, OffsetMax = OffsetMax
						},
						Image =
						{
							Png = !string.IsNullOrEmpty(Image)
								? _instance.ImageLibrary.Call<string>("GetImage", Image)
								: null,
							Color = Color.Get(),
							Sprite =
								!string.IsNullOrEmpty(Sprite) ? Sprite : "Assets/Content/UI/UI.Background.Tile.psd",
							Material = !string.IsNullOrEmpty(Material) ? Material : "Assets/Icons/IconMaterial.mat"
						},
						CursorEnabled = cursor
					}, parent, name);
				}
			}
		}

		private class FButton
		{
			[JsonProperty(PropertyName = "Тип | Type")] [JsonConverter(typeof(StringEnumConverter))]
			public BtnType Type;

			[JsonProperty(PropertyName = "Текст | Text")]
			public string Text;

			[JsonProperty(PropertyName = "Команда | Command")]
			public string Command;

			[JsonProperty(PropertyName = "Активный Цвет | Active Color")]
			public IColor ActiveColor;

			[JsonProperty(PropertyName = "Неактивный Цвет | Disactive Color")]
			public IColor DisactiveColor;

			[JsonProperty(PropertyName = "Font Size")]
			public int FontSize;

			[JsonProperty(PropertyName = "Font")] public string Font;

			[JsonProperty(PropertyName = "Align")] [JsonConverter(typeof(StringEnumConverter))]
			public TextAnchor Align;

			[JsonProperty(PropertyName = "Text Color")]
			public IColor TColor;

			public void Get(ref CuiElementContainer container, FriendData data, string parent, string name, string aMin,
				string aMax, string oMin, string oMax, int page,
				string mainParent)
			{
				if (string.IsNullOrEmpty(name))
					name = CuiHelper.GetGuid();

				var color = ActiveColor;

				switch (Type)
				{
					case BtnType.Doors:
						color = data.Doors ? ActiveColor : DisactiveColor;
						break;
					case BtnType.Cupboard:
						color = data.Cupboard ? ActiveColor : DisactiveColor;
						break;
					case BtnType.Turrets:
						color = data.Turrets ? ActiveColor : DisactiveColor;
						break;
					case BtnType.FriendlyFire:
						color = data.FriendlyFire ? ActiveColor : DisactiveColor;
						break;
					case BtnType.Containers:
						color = data.Containers ? ActiveColor : DisactiveColor;
						break;
					case BtnType.Sams:
						color = data.SAMs ? ActiveColor : DisactiveColor;
						break;
				}

				var text = Text.Replace("{user}", data.UserId.ToString());
				var command = Command.Replace("{user}", data.UserId.ToString());

				container.Add(new CuiButton
				{
					RectTransform =
					{
						AnchorMin = aMin, AnchorMax = aMax,
						OffsetMin = oMin, OffsetMax = oMax
					},
					Text =
					{
						Text = $"{text}",
						Align = Align,
						Font = Font,
						FontSize = FontSize,
						Color = TColor.Get()
					},
					Button =
					{
						Command =
							$"UI_Friends sendcmd {page} {mainParent} {command}", // command.Contains("chat.say") ? $"friendssendcmd {command}" : $"{command}",
						Color = color.Get(),
						Close = _config.AutoClose ? Layer : string.Empty
					}
				}, parent, name);
			}
		}

		private class IButton : IAnchors
		{
			[JsonProperty(PropertyName = "Высота | Height")]
			public float Height;

			[JsonProperty(PropertyName = "Ширина | Width")]
			public float Width;

			[JsonProperty(PropertyName = "Отступ | Margin")]
			public float Margin;
		}

		private class SText : InterfacePosition
		{
			[JsonProperty(PropertyName = "Font Size")]
			public int FontSize;

			[JsonProperty(PropertyName = "Font")] public string Font;

			[JsonProperty(PropertyName = "Align")] [JsonConverter(typeof(StringEnumConverter))]
			public TextAnchor Align;

			[JsonProperty(PropertyName = "Цвет игрока онлайн | Online Color")]
			public IColor Color;

			[JsonProperty(PropertyName = "Цвет игрока оффлайн | Offline Color")]
			public IColor OfflineColor;

			public void Get(ref CuiElementContainer container, bool online, string parent = "Hud", string name = null,
				string text = "")
			{
				if (string.IsNullOrEmpty(name))
					name = CuiHelper.GetGuid();

				container.Add(new CuiLabel
				{
					RectTransform =
						{AnchorMin = AnchorMin, AnchorMax = AnchorMax, OffsetMin = OffsetMin, OffsetMax = OffsetMax},
					Text =
					{
						Text = $"{text}", Align = Align, FontSize = FontSize,
						Color = online ? Color.Get() : OfflineColor.Get(),
						Font = Font
					}
				}, parent, name);
			}
		}

		private class IText : InterfacePosition
		{
			[JsonProperty(PropertyName = "Font Size")]
			public int FontSize;

			[JsonProperty(PropertyName = "Font")] public string Font;

			[JsonProperty(PropertyName = "Align")] [JsonConverter(typeof(StringEnumConverter))]
			public TextAnchor Align;

			[JsonProperty(PropertyName = "Text Color")]
			public IColor Color;

			public void Get(ref CuiElementContainer container, string parent = "Hud", string name = null,
				string text = "")
			{
				if (string.IsNullOrEmpty(name))
					name = CuiHelper.GetGuid();

				container.Add(new CuiLabel
				{
					RectTransform =
						{AnchorMin = AnchorMin, AnchorMax = AnchorMax, OffsetMin = OffsetMin, OffsetMax = OffsetMax},
					Text =
					{
						Text = $"{text}", Align = Align, FontSize = FontSize, Color = Color.Get(),
						Font = Font
					}
				}, parent, name);
			}
		}

		private class IColor
		{
			[JsonProperty(PropertyName = "HEX")] public string HEX;

			[JsonProperty(PropertyName = "Непрозрачность | Opacity (0 - 100)")]
			public float Alpha;

			public string Get()
			{
				if (string.IsNullOrEmpty(HEX)) HEX = "#FFFFFF";

				var str = HEX.Trim('#');
				if (str.Length != 6) throw new Exception(HEX);
				var r = byte.Parse(str.Substring(0, 2), NumberStyles.HexNumber);
				var g = byte.Parse(str.Substring(2, 2), NumberStyles.HexNumber);
				var b = byte.Parse(str.Substring(4, 2), NumberStyles.HexNumber);

				return $"{(double) r / 255} {(double) g / 255} {(double) b / 255} {Alpha / 100}";
			}

			public IColor(string hex, float alpha)
			{
				HEX = hex;
				Alpha = alpha;
			}
		}

		protected override void LoadConfig()
		{
			base.LoadConfig();
			try
			{
				_config = Config.ReadObject<Configuration>();
				if (_config == null) throw new Exception();

				if (_config.Version < Version)
					UpdateConfigValues();

				SaveConfig();
			}
			catch
			{
				PrintError("Your configuration file contains an error.Using default configuration values.");
				LoadDefaultConfig();
			}
		}

		private void UpdateConfigValues()
		{
			PrintWarning("Config update detected! Updating config values...");

			var baseConfig = new Configuration();

			if (_config.Version < new VersionNumber(1, 14, 0))
				_config.AutoClose = baseConfig.AutoClose;

			if (_config.Version < new VersionNumber(1, 15, 0))
			{
				_config.UseTeams = baseConfig.UseTeams;
				_config.SAMs = baseConfig.SAMs;
			}

			_config.Version = Version;
			PrintWarning("Config update completed!");
		}

		protected override void SaveConfig()
		{
			Config.WriteObject(_config);
		}

		protected override void LoadDefaultConfig()
		{
			_config = new Configuration();
		}

		#endregion

		#region Data

		private Dictionary<string, PlayerData> _usersData = new Dictionary<string, PlayerData>();

		private ListHashSet<ulong> _changedPlayers = new ListHashSet<ulong>();
		
		#region PlayerDatabase

		private PlayerData LoadPlayerDatabaseData(string userId)
		{
			PlayerData data;
			if (_usersData.TryGetValue(userId, out data))
				return data;

			var success =
				PlayerDatabase?.Call<string>("GetPlayerDataRaw", userId, _config.PlayerDatabase.Field);
			if (string.IsNullOrEmpty(success))
			{
				data = new PlayerData
				{
					UserID = Convert.ToUInt64(userId)
				};
				SavePlayerDatabaseData(userId, data);
				return _usersData[userId] = data;
			}

			if ((data = JsonConvert.DeserializeObject<PlayerData>(success)) == null)
			{
				data = new PlayerData
				{
					UserID = Convert.ToUInt64(userId)
				};
				SavePlayerDatabaseData(userId, data);
				return _usersData[userId] = data;
			}

			return _usersData[userId] = data;
		}

		private void SavePlayerDatabaseData(string userId, PlayerData data)
		{
			if (data == null) return;

			var serializeObject = JsonConvert.SerializeObject(data);
			if (serializeObject == null) return;

			PlayerDatabase?.Call("SetPlayerData", userId, _config.PlayerDatabase.Field, serializeObject);
		}

		#endregion

		#region Classes

		private class PlayerData
		{
			#region Fields

			[JsonProperty(PropertyName = "Display Name")]
			public string DisplayName;

			[JsonProperty(PropertyName = "UserId")]
			public ulong UserID;

			[JsonProperty(PropertyName = "Friends", ObjectCreationHandling = ObjectCreationHandling.Replace)]
			public List<FriendData> Friends = new List<FriendData>();

			[JsonProperty(PropertyName = "Removed Friends", ObjectCreationHandling = ObjectCreationHandling.Replace)]
			public HashSet<ulong> RemovedFriends = new HashSet<ulong>();

			#endregion

			#region Utils

			public bool IsFriend(ulong friend)
			{
				return Friends.Exists(check => check.UserId == friend);
			}

			public FriendData GetFriend(ulong friend)
			{
				return Friends.Find(check => check.UserId == friend);
			}

			public void AddFriend(ulong friend)
			{
				if (!friend.IsSteamId() || IsFriend(friend)) return;

				Friends.Add(new FriendData
				{
					UserId = friend,
					FriendlyFire = _config.FriendlyFire,
					Doors = _config.Doors,
					Turrets = _config.Turrets,
					Cupboard = _config.Cupboard,
					Containers = _config.Containers,
					SAMs = _config.SAMs
				});

				RemovedFriends.Remove(friend);
				
				OnChanged();
			}

			public void RemoveFriend(ulong friend)
			{
				if (Friends.RemoveAll(x => x.UserId == friend) > 0) 
					RemovedFriends.Add(friend);
				
				OnChanged();
			}

			#endregion

			#region Helpers

			private static string BaseFolder()
			{
				return "Friends" + Path.DirectorySeparatorChar + "Players" + Path.DirectorySeparatorChar;
			}

			public static string[] GetFiles()
			{
				return GetFiles(BaseFolder());
			}

			public static string[] GetFiles(string baseFolder)
			{
				try
				{
					var json = ".json".Length;
					var paths = Interface.Oxide.DataFileSystem.GetFiles(baseFolder);
					for (var i = 0; i < paths.Length; i++)
					{
						var path = paths[i];
						var separatorIndex = path.LastIndexOf(Path.DirectorySeparatorChar);

						// We have to do this since GetFiles returns paths instead of filenames
						// And other methods require filenames
						paths[i] = path.Substring(separatorIndex + 1, path.Length - separatorIndex - 1 - json);
					}

					return paths;
				}
				catch
				{
					return Array.Empty<string>();
				}
			}

			public static PlayerData GetOrLoad(string userId)
			{
				if (!userId.IsSteamId()) return null;

				if (_config.PlayerDatabase.Enabled) return _instance.LoadPlayerDatabaseData(userId);

				return GetOrLoad(BaseFolder(), userId);
			}

			public static PlayerData GetNotLoad(string userId)
			{
				if (!userId.IsSteamId()) return null;

				if (_config.PlayerDatabase.Enabled) return _instance.LoadPlayerDatabaseData(userId);

				var data = GetOrLoad(BaseFolder(), userId, false);

				return data;
			}

			public static PlayerData GetOrLoad(string baseFolder, string userId, bool load = true)
			{
				PlayerData data;
				if (_instance._usersData.TryGetValue(userId, out data)) return data;

				try
				{
					data = ReadOnlyObject(baseFolder + userId);
				}
				catch (Exception e)
				{
					Interface.Oxide.LogError(e.ToString());
				}

				return load
					? _instance._usersData[userId] = data
					: data;
			}

			public static PlayerData GetOrCreate(string userId)
			{
				if (!userId.IsSteamId()) return null;

				if (_config.PlayerDatabase.Enabled) return _instance.LoadPlayerDatabaseData(userId);

				return GetOrLoad(userId) ?? (_instance._usersData[userId] = new PlayerData
				{
					UserID = Convert.ToUInt64(userId)
				});
			}
			
			public static bool IsLoaded(string userId)
			{
				return _instance._usersData.ContainsKey(userId);
			}

			public static void Save()
			{
#if TESTING
				Debug.Log($"[Save] count={_instance?._usersData.Count}");
#endif

				_instance?._usersData?.Keys.ToList().ForEach(Save);
			}

			public static void Save(string userId)
			{
				if (_config.PlayerDatabase.Enabled)
				{
					_instance.SavePlayerDatabaseData(userId, _instance.LoadPlayerDatabaseData(userId));
					return;
				}

				PlayerData data;
				if (!_instance._usersData.TryGetValue(userId, out data))
					return;

				Interface.Oxide.DataFileSystem.WriteObject(BaseFolder() + userId, data);
				
				_instance?._changedPlayers?.Remove(data.UserID);
			}

			public static void SaveAndUnload(string userId)
			{
				Save(userId);

				Unload(userId);
			}

			public static void Unload(string userId)
			{
				_instance?._usersData?.Remove(userId);
				
				_instance?._changedPlayers?.Remove(Convert.ToUInt64(userId));
			}

			private static PlayerData ReadOnlyObject(string name)
			{
				return Interface.Oxide.DataFileSystem.ExistsDatafile(name)
					? Interface.Oxide.DataFileSystem.GetFile(name).ReadObject<PlayerData>()
					: null;
			}

			public void OnChanged()
			{
				_instance?._changedPlayers?.TryAdd(UserID);
			}
			
			#endregion
		}
		
		private class FriendData
		{
			[JsonProperty(PropertyName = "UserId")]
			public ulong UserId;

			[JsonProperty(PropertyName = "FriendlyFire")]
			public bool FriendlyFire;

			[JsonProperty(PropertyName = "Doors")] public bool Doors;

			[JsonProperty(PropertyName = "Turrets")]
			public bool Turrets;

			[JsonProperty(PropertyName = "Cupboard")]
			public bool Cupboard;

			[JsonProperty(PropertyName = "Containers")]
			public bool Containers;

			[JsonProperty(PropertyName = "SAMs")] public bool SAMs;
		}

		#region Save Coroutine

		private Coroutine _saveCoroutine;

		private void StartSaveCoroutine()
		{
			_saveCoroutine = Rust.Global.Runner.StartCoroutine(HandleChangedPlayers());
		}

		private void StopSaveCoroutine()
		{
			if (_saveCoroutine != null) 
				Rust.Global.Runner.StopCoroutine(_saveCoroutine);
		}

		private IEnumerator HandleChangedPlayers()
		{
			var count = 0;
			
			while (_changedPlayers.Count > 0)
			{
				var player = _changedPlayers[0];
				_changedPlayers.RemoveAt(0);
				
				PlayerData.Save(player.ToString());
				
				if (++count % 5 == 0)
					yield return CoroutineEx.waitForFixedUpdate;
			}
		}
		
		#endregion
		
		#endregion

		#region Utils

		private static PlayerData FindPlayerData(string user)
		{
			foreach (var player in BasePlayer.allPlayerList)
				if (player.displayName.StartsWith(user, StringComparison.CurrentCultureIgnoreCase))
					return PlayerData.GetOrLoad(player.UserIDString);

			return null;
		}

		#region Migration

		private Coroutine _playersMigration;

		private IEnumerator StartOnAllPlayers(string[] players,
			Action<string> callback = null,
			Action onFinish = null)
		{
			for (var i = 0; i < players.Length; i++)
			{
				callback?.Invoke(players[i]);

				if (i % 10 == 0)
					yield return CoroutineEx.waitForFixedUpdate;
			}

			onFinish?.Invoke();

			_playersMigration = null;
		}

		#endregion

		#endregion

		#endregion

		#region Hooks

		private void Init()
		{
			_instance = this;

			AddCovalenceCommand(new[] {"ff", "friendlyfire", "friend", "friends", "f", "fmenu", "f.menu"},
				nameof(CmdFriends));

			if (!_config.AddTeam)
			{
				Unsubscribe(nameof(OnTeamLeave));
				Unsubscribe(nameof(OnTeamAcceptInvite));
				Unsubscribe(nameof(OnTeamKick));
				Unsubscribe(nameof(OnTeamInvite));
				Unsubscribe(nameof(OnTeamRejectInvite));
			}
		}

		private void OnServerInitialized()
		{
			if (!ImageLibrary) PrintWarning("IMAGE LIBRARY IS NOT INSTALLED.");

			#region Init

			foreach (var player in BasePlayer.activePlayerList) 
				OnPlayerConnected(player);

			#endregion

			#region Notifications

			Notifications?.Call("AddImage", _config.FriendInvite.Image, _config.FriendInvite.Url);

			Notifications?.Call("AddImage", _config.Notify.Image, _config.Notify.Url);

			#endregion

			#region Team

			RelationshipManager.maxTeamSize = _config.MaxFriendsAmount + 1;

			#endregion

			#region Auth

			foreach (var entity in BaseNetworkable.serverEntities.OfType<BaseEntity>()) CheckEntity(entity);

			#endregion

			timer.Every(1, TimeHandle);
		}

		private void OnServerSave()
		{
			timer.In(Random.Range(2f, 7f), StartSaveCoroutine);
		}

		private void Unload()
		{
			if (_playersMigration != null)
				Rust.Global.Runner.StopCoroutine(_playersMigration);

			if (_friendsConvertation != null)
				Rust.Global.Runner.StopCoroutine(_friendsConvertation);

			StopSaveCoroutine();
			
			foreach (var player in BasePlayer.activePlayerList)
			{
				CuiHelper.DestroyUi(player, Layer);

				PlayerData.SaveAndUnload(player.UserIDString);
			}

			_instance = null;
			_config = null;
		}

		private void OnPlayerConnected(BasePlayer player)
		{
			if (player == null || !player.userID.IsSteamId()) return;

			var data = PlayerData.GetOrCreate(player.UserIDString);
			if (data == null) return;

			var oldName = data.DisplayName;
			var newName = player.displayName;
			if (string.IsNullOrEmpty(oldName) || !oldName.Equals(newName))
			{
				data.DisplayName = newName;
				data.OnChanged();
			}
			
			GetAvatar(player.userID,
				avatar => ImageLibrary?.Call("AddImage", avatar, $"avatar_{player.UserIDString}"));

			if (_config.AddTeam && data.Friends.Count > 0)
			{
				RelationshipManager.PlayerTeam team;
				var friend =
					data.Friends.Find(x => RelationshipManager.ServerInstance.playerToTeam.ContainsKey(x.UserId));
				if (friend == null)
				{
					team = RelationshipManager.ServerInstance.CreateTeam();
					team.AddPlayer(player);
					team.SetTeamLeader(player.userID);

					var friendPlayer = FindPlayer(data.Friends);
					if (friendPlayer != null) team.AddPlayer(friendPlayer);
				}
				else
				{
					team = RelationshipManager.ServerInstance.playerToTeam[friend.UserId];
					if (team == null) return;
					team.AddPlayer(player);
				}
			}
		}

		private void OnPlayerDisconnected(BasePlayer player)
		{
			if (player == null) return;

			PlayerData.SaveAndUnload(player.UserIDString);
		}

		private void OnEntityTakeDamage(BasePlayer player, HitInfo info)
		{
			if (player == null || info == null) return;

			var initiator = info.InitiatorPlayer;
			if (initiator == null) return;

			if (!IsFriend(player.userID, initiator.userID) || player.userID == initiator.userID)
				return;

			var data = PlayerData.GetOrLoad(initiator.UserIDString);

			var friend = data?.Friends.Find(x => x.UserId == player.userID);
			if (friend == null || friend.FriendlyFire) return;

			info.damageTypes.ScaleAll(0);

			if (IsCd(initiator.userID)) return;

			Notify(initiator, _config.Notify.Delay, Msg(NotifyTitle, initiator.UserIDString),
				Msg(FF, initiator.UserIDString, player.displayName), _config.Notify.Image);

			SetCd(initiator.userID);
		}

		#region Team

		private void OnTeamLeave(RelationshipManager.PlayerTeam playerTeam, BasePlayer player)
		{
			if (playerTeam == null || player == null) return;

			playerTeam.members.ToList().ForEach(user =>
			{
				PlayerData.GetOrLoad(player.UserIDString)?.RemoveFriend(user);
				PlayerData.GetOrLoad(user.ToString())?.RemoveFriend(player.userID);
			});

			UpdateAuthList(player.userID, AutoAuthType.All);
			UpdateTeamAuthList(playerTeam.members);
		}

		private void OnTeamAcceptInvite(RelationshipManager.PlayerTeam playerTeam, BasePlayer player)
		{
			NextTick(() =>
			{
				if (playerTeam == null || player == null || !player.userID.IsSteamId()) return;

				var invite = _invites.Find(x => x.Target == player);
				if (invite != null && playerTeam.members.Exists(x => invite.Inviter.userID == x))
				{
					AcceptInvite(player);
					return;
				}

				if (playerTeam.members.Contains(player.userID))
					if (_config.UseTeams)
					{
						playerTeam.members.ToList().ForEach(user =>
						{
							if (user == player.userID) return;

							PlayerData.GetOrLoad(player.UserIDString)?.AddFriend(user);
							PlayerData.GetOrLoad(user.ToString())?.AddFriend(player.userID);

							Log("friends", $"Player '{player}' added '{user}' as a friend");
						});

						UpdateTeamAuthList(playerTeam.members);
					}
			});
		}

		private void OnTeamKick(RelationshipManager.PlayerTeam playerTeam, BasePlayer player, ulong target)
		{
			NextTick(() =>
			{
				if (playerTeam == null) return;

				if (!playerTeam.members.Contains(target))
				{
					playerTeam.members.ToList().ForEach(user =>
					{
						PlayerData.GetOrLoad(user.ToString())?.RemoveFriend(target);
						PlayerData.GetOrLoad(target.ToString())?.RemoveFriend(user);
					});

					UpdateAuthList(target, AutoAuthType.All);
					UpdateTeamAuthList(playerTeam.members);
				}
			});
		}

		private void OnTeamInvite(BasePlayer inviter, BasePlayer target)
		{
			SendInvite(inviter, target);
		}

		private void OnTeamRejectInvite(BasePlayer rejector, RelationshipManager.PlayerTeam team)
		{
			CancelInvite(rejector);
		}

		private void UpdateTeamAuthList(List<ulong> teamMembers)
		{
			if (teamMembers.Count <= 0) return;
			teamMembers.ForEach(member => UpdateAuthList(member, AutoAuthType.All));
		}

		#endregion

		#region SAMs

		private object OnSamSiteTarget(SamSite samSite, BaseVehicle vehicle)
		{
			if (samSite == null || !samSite.OwnerID.IsSteamId() || vehicle == null)
				return null;

			var data = PlayerData.GetOrLoad(samSite.OwnerID.ToString());
			if (data == null)
				return null;

			if (vehicle.OwnerID == samSite.OwnerID ||
			    data.Friends.Exists(friend => friend.UserId == vehicle.OwnerID && friend.SAMs))
				return true;

			foreach (var mounted in vehicle.allMountPoints.Where(allMountPoint => allMountPoint != null && allMountPoint.mountable != null).Select(allMountPoint => allMountPoint.mountable.GetMounted()).Where(mounted => mounted != null))
			{
				if (mounted.userID == samSite.OwnerID)
					return true;

				var friendData = data.GetFriend(mounted.userID);
				if (friendData != null && friendData.SAMs)
					return true;
			}

			return null;
		}

		#endregion

		#endregion

		#region Commands

		private void CmdFriends(IPlayer cov, string command, string[] args)
		{
			var player = cov?.Object as BasePlayer;
			if (player == null) return;

			switch (command)
			{
				case "friendlyfire":
				case "ff":
				{
					var data = PlayerData.GetOrLoad(player.ToString());
					if (data == null) return;

					if (args.Length < 1)
					{
						Reply(player, FFErrorSyntax, command);
						return;
					}

					var friends = FindRemoveFriend(player, args[0]);
					if (friends == null || friends.Count == 0)
					{
						Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
							Msg(NotFound, player.UserIDString, args[0]), _config.Notify.Image);
						return;
					}

					PlayerData friend;

					if (friends.Count > 1)
					{
						int index;
						if (args.Length >= 3 && int.TryParse(args[2], out index))
						{
							if (index - 1 < 0 || friends.Count <= index - 1)
								return;

							friend = friends[index - 1];
						}
						else
						{
							var f = 0;
							var str = string.Join(", ", friends.Select(x =>
							{
								f++;
								return $"{x.DisplayName} ({x.UserID}) [{f}]";
							}));

							Reply(player, MultipleFound, str, f, args[0], args[1]);
							return;
						}
					}
					else
					{
						friend = friends.FirstOrDefault();
					}

					if (friend == null)
					{
						Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
							Msg(NotFound, player.UserIDString, args[0]), _config.Notify.Image);
						return;
					}

					var fData = data.GetFriend(friend.UserID);
					if (fData == null)
					{
						Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
							Msg(IsNotFriend, player.UserIDString, friend.DisplayName), _config.Notify.Image);
						return;
					}


					if (args.Length > 2)
						switch (args[1].ToLower())
						{
							case "on":
							{
								fData.FriendlyFire = true;
								break;
							}
							case "off":
							{
								fData.FriendlyFire = false;
								break;
							}
						}
					else
						fData.FriendlyFire = !fData.FriendlyFire;

					Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
						Msg(fData.FriendlyFire ? FFOn : FFOff, player.UserIDString), _config.Notify.Image);
					break;
				}
				default:
				{
					if (args.Length == 0)
					{
						MainUi(player);
						return;
					}

					switch (args[0].ToLower())
					{
						case "+":
						case "i":
						case "inv":
						case "invite":
						case "add":
						{
							if (args.Length < 2)
							{
								Reply(player, ErrorSyntax, command, args[0]);
								return;
							}

							var friends = FindPlayer(args[1]);
							if (friends.Count == 0)
							{
								Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
									Msg(NotFound, player.UserIDString, args[1]), _config.Notify.Image);
								return;
							}

							BasePlayer friend;

							if (friends.Count > 1)
							{
								int index;
								if (args.Length >= 3 && int.TryParse(args[2], out index))
								{
									if (index - 1 < 0 || friends.Count <= index - 1)
										return;

									friend = friends[index - 1];
								}
								else
								{
									var f = 0;
									var str = string.Join(", ", friends.Select(x =>
									{
										f++;
										return $"{x.displayName} ({x.UserIDString}) [{f}]";
									}));

									Reply(player, MultipleFound, str, f, args[0], args[1]);
									return;
								}
							}
							else
							{
								friend = friends.FirstOrDefault();
							}

							if (friend == null)
							{
								Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
									Msg(NotFound, player.UserIDString, args[1]), _config.Notify.Image);
								return;
							}

							if (player.userID == friend.userID)
							{
								Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
									Msg(CantAddSelf, player.UserIDString), _config.Notify.Image);
								return;
							}

							var pData = PlayerData.GetOrLoad(player.UserIDString);
							if (pData == null) return;

							if (pData.IsFriend(friend.userID))
							{
								Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
									Msg(AlreadyFriends, player.UserIDString), _config.Notify.Image);
								return;
							}

							if (pData.Friends.Count >= _config.MaxFriendsAmount)
							{
								Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
									Msg(MaxFriends, player.UserIDString), _config.Notify.Image);
								return;
							}

							var fData = PlayerData.GetOrLoad(friend.UserIDString);
							if (fData == null) return;

							if (fData.Friends.Count >= _config.MaxFriendsAmount)
							{
								Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
									Msg(TargetMaxFriends, player.UserIDString), _config.Notify.Image);
								return;
							}

							SendInvite(player, friend);
							break;
						}

						case "-":
						case "del":
						case "delete":
						case "rem":
						case "remove":
						{
							if (args.Length < 2)
							{
								Reply(player, ErrorSyntax, command, args[0]);
								return;
							}

							var friends = FindRemoveFriend(player, args[1]);
							if (friends == null || friends.Count == 0)
							{
								Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
									Msg(NotFound, player.UserIDString, args[1]), _config.Notify.Image);
								return;
							}

							PlayerData friend;

							if (friends.Count > 1)
							{
								int index;
								if (args.Length >= 3 && int.TryParse(args[2], out index))
								{
									if (index - 1 < 0 || friends.Count <= index - 1)
										return;

									friend = friends[index - 1];
								}
								else
								{
									var f = 0;
									var str = string.Join(", ", friends.Select(x =>
									{
										f++;
										return $"{x.DisplayName} ({x.UserID}) [{f}]";
									}));

									Reply(player, MultipleFound, str, f, args[0], args[1]);
									return;
								}
							}
							else
							{
								friend = friends.FirstOrDefault();
							}

							if (friend == null)
							{
								Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
									Msg(NotFound, player.UserIDString, args[1]), _config.Notify.Image);
								return;
							}

							CuiHelper.DestroyUi(player, Layer);

							RemoveFriend(player, friend.UserID);
							break;
						}

						case "a":
						case "accept":
						{
							AcceptInvite(player);
							break;
						}

						case "c":
						case "cancel":
						{
							CancelInvite(player);
							break;
						}

						case "list":
						{
							var data = PlayerData.GetOrLoad(player.UserIDString);
							if (data == null) return;

							if (data.Friends.Count == 0)
							{
								Notify(player, _config.Notify.Delay, Msg(NotifyTitle, player.UserIDString),
									Msg(NoFriends, player.UserIDString), _config.Notify.Image);
							}
							else
							{
								var friends = string.Join(", ",
									data.Friends.Select(x =>
										$"{PlayerData.GetOrLoad(x.UserId.ToString())?.DisplayName ?? "NONE"} ({x.UserId})"));

								Reply(player, Msg(FriendList, player.UserIDString, friends));
							}

							break;
						}

						case "doors":
						{
							if (args.Length < 2)
							{
								Reply(player, ErrorSyntax, command, args[0]);
								return;
							}

							var friend = FindPlayerData(args[1]);
							if (friend == null)
							{
								Reply(player, NotFound, args[1]);
								return;
	
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху