C#:
using System;
using System.Collections.Generic;
using System.Globalization;
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;
using Oxide.Game.Rust.Cui;
using ProtoBuf;
using UnityEngine;
using WebSocketSharp;
namespace Oxide.Plugins {
[Info("Friends", "Mevent", "1.11.0")]
[Description("Adds a friends system with a visual interface for quick interaction")]
public class Friends : RustPlugin {
#region Fields
[PluginReference]
private Plugin ImageLibrary, Notifications, PlayerList;
private const string Layer = "Com.Mevent.Main";
private static Friends _instance;
private readonly Dictionary<ulong, EntityEntry> playerEntites = new Dictionary<ulong, EntityEntry>();
private class EntityEntry {
public HashSet<AutoTurret> autoTurrets = new HashSet<AutoTurret>();
public HashSet<BuildingPrivlidge> buildingPrivlidges = new HashSet<BuildingPrivlidge>();
}
private enum AutoAuthType {
All, Turret, Cupboard
}
private enum BtnType {
None, Doors, Cupboard, Turrets, Containers, FriendlyFire
}
#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 = "Огонь по друзьям | Friendly Fire")]
public bool FriendlyFire;
[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 = "Фон | 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 IText {
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
}
, 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.None, Text = "Trade", Command = "chat.say /trade {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.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.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.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.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)
}
}
}
;
[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"
}
;
}
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 IText 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) {
if (name.IsNullOrEmpty()) name = CuiHelper.GetGuid();
container.Add(new CuiPanel {
RectTransform = {
AnchorMin = AnchorMin, AnchorMax = AnchorMax, OffsetMin = oMin, OffsetMax = oMax
}
, Image = {
Color = Color.Get()
}
}
, parent, name);
if (_instance != null && _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 = target?.Name;
if (string.IsNullOrEmpty(displayName)) displayName = targetId;
var status = target != null && target.IsConnected;
Nickname?.Get(ref container, name, name + ".Nickname", displayName);
if (_instance != null) 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];
if (button == null) continue;
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}");
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 = !Sprite.IsNullOrEmpty() ? 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 = !Sprite.IsNullOrEmpty() ? Sprite : "Assets/Content/UI/UI.Background.Tile.psd", Material = !Material.IsNullOrEmpty() ? 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) {
if (name.IsNullOrEmpty()) 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;
}
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 = command.Contains("chat.say") ? $"friendssendcmd {command}" : $"{command}", Color = color.Get(), Close = Layer
}
}
, 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();
SaveConfig();
}
catch {
PrintError("Your configuration file contains an error.Using default configuration values.");
LoadDefaultConfig();
}
}
protected override void SaveConfig() {
Config.WriteObject(_config);
}
protected override void LoadDefaultConfig() {
_config = new Configuration();
}
#endregion
#region Data
private static PluginData _data;
private void SaveData() {
Interface.Oxide.DataFileSystem.WriteObject(Name, _data);
}
private void LoadData() {
try {
_data = Interface.Oxide.DataFileSystem.ReadObject<PluginData>(Name);
}
catch (Exception e) {
PrintError(e.ToString());
}
if (_data == null) _data = new PluginData();
}
private class PluginData {
[JsonProperty(PropertyName = "Players", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, PlayerData> Players = new Dictionary<ulong, PlayerData>();
}
private class PlayerData {
[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 List<ulong> RemovedFriends = new List<ulong>();
public bool IsFriend(ulong friend) {
for (var i = 0; i < Friends.Count; i++) {
var check = Friends[i];
if (check.UserId == friend) return true;
}
return false;
}
public FriendData GetFriend(ulong friend) {
for (var i = 0; i < Friends.Count; i++) {
var check = Friends[i];
if (check.UserId == friend) return check;
}
return null;
}
public void AddFriend(ulong friend) {
if (Friends.Exists(x => x.UserId == friend) || !friend.IsSteamId()) return;
Friends.Add(new FriendData {
UserId = friend, FriendlyFire = _config.FriendlyFire, Doors = _config.Doors, Turrets = _config.Turrets, Cupboard = _config.Cupboard, Containers = _config.Containers
}
);
RemovedFriends.Remove(friend);
}
public void RemoveFriend(ulong friend) {
Friends?.ToList().ForEach(f => {
if (f.UserId == friend) {
if (!RemovedFriends.Contains(friend)) RemovedFriends.Add(friend);
Friends.Remove(f);
}
}
);
}
}
private class FriendData {
public ulong UserId;
public bool FriendlyFire;
public bool Doors;
public bool Turrets;
public bool Cupboard;
public bool Containers;
}
private static PlayerData GetPlayerData(BasePlayer player) {
if (player == null || !player.userID.IsSteamId()) return null;
if (!_data.Players.ContainsKey(player.userID)) _data.Players.Add(player.userID, new PlayerData {
DisplayName = player.displayName, UserId = player.userID
}
);
return _data.Players[player.userID];
}
private static PlayerData GetPlayerData(ulong userId) {
if (!userId.IsSteamId()) return null;
if (!_data.Players.ContainsKey(userId)) _data.Players.Add(userId, new PlayerData {
UserId = userId
}
);
return _data.Players[userId];
}
#endregion
#region Hooks
private void Init() {
_instance = this;
LoadData();
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() {
SaveData();
}
private void Unload() {
foreach (var player in BasePlayer.activePlayerList) CuiHelper.DestroyUi(player, Layer);
SaveData();
_instance = null;
_config = null;
_data = null;
}
private void OnPlayerConnected(BasePlayer player) {
if (player == null || !player.userID.IsSteamId()) return;
var data = GetPlayerData(player);
if (data == null) return;
data.DisplayName = player.displayName;
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.Instance.playerToTeam.ContainsKey(x.UserId));
if (friend == null) {
team = RelationshipManager.Instance.CreateTeam();
team.AddPlayer(player);
team.SetTeamLeader(player.userID);
var friendPlayer = FindPlayer(data.Friends);
if (friendPlayer != null) team.AddPlayer(friendPlayer);
}
else {
team = RelationshipManager.Instance.playerToTeam[friend.UserId];
if (team == null) return;
team.AddPlayer(player);
}
}
}
#region Cooldown
private readonly Dictionary<ulong, float> _cooldown = new Dictionary<ulong, float>();
private bool IsCd(ulong user) {
return GetCd(user) >= 0;
}
private int GetCd(ulong user) {
return _cooldown.ContainsKey(user) ? (int) (_cooldown[user] - Time.time) : -1;
}
private void SetCd(ulong user) {
var time = Time.time + _config.FFDelay;
if (_cooldown.ContainsKey(user)) _cooldown[user] = time;
else _cooldown.Add(user, time);
}
#endregion
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 = GetPlayerData(initiator);
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 => {
GetPlayerData(player)?.RemoveFriend(user);
GetPlayerData(user)?.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)) {
playerTeam.members.ToList().ForEach(user => {
if (user == player.userID) return;
GetPlayerData(player)?.AddFriend(user);
GetPlayerData(user)?.AddFriend(player.userID);
}
);
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 => {
GetPlayerData(player)?.RemoveFriend(user);
GetPlayerData(user)?.RemoveFriend(player.userID);
}
);
UpdateAuthList(target, AutoAuthType.All);
UpdateTeamAuthList(playerTeam.members);
}
}
);
}
private void OnTeamInvite(BasePlayer inviter, BasePlayer target) {
SendInvite(inviter, target);
}
/*
private object OnTeamInvite(BasePlayer inviter, BasePlayer target)
{
if (inviter.Team == null || inviter.Team.members.Count >= _config.MaxFriends + 1)
{
Notify(inviter, _config.Notify.Delay, Msg(NotifyTitle, inviter.UserIDString),
Msg(TargetMaxFriends, inviter.UserIDString), _config.Notify.Image);
return false;
}
SendInvite(inviter, target);
return null;
}*/
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
#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 = GetPlayerData(player);
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);
retu
// ... [Code preview truncated due to length. Full source in attached file] ...