Advertisement
Guest User

Untitled

a guest
Feb 19th, 2023
186
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 369.07 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Text.RegularExpressions;
  6. using Facepunch;
  7. using Newtonsoft.Json;
  8. using Oxide.Core;
  9. using Oxide.Core.Configuration;
  10. using Oxide.Core.Libraries.Covalence;
  11. using Oxide.Core.Plugins;
  12. using Oxide.Game.Rust.Cui;
  13. using Oxide.Game.Rust.Libraries;
  14. using Oxide.Plugins.DuelistExtensionMethods;
  15. using Rust;
  16. using Rust.Workshop;
  17. using UnityEngine;
  18. using Random = UnityEngine.Random;
  19.  
  20. //https://discord.com/channels/@me/809480368218046485/809491769959776336
  21. //https://umod.org/community/duelist/30748-two-users-pick-the-same-kit-but-it-still-runs-with-random-kits
  22. // add map markers
  23.  
  24. namespace Oxide.Plugins
  25. {
  26.     [Info("Duelist", "nivex", "1.2.815")]
  27.     [Description("1v1 and team deathmatch event.")]
  28.     public class Duelist : RustPlugin
  29.     {
  30.         [PluginReference] Plugin Kits, ZoneManager, Economics, ServerRewards, Clans, AimTrain, LustyMap;
  31.  
  32.         private static Duelist Instance;
  33.  
  34.         public enum Team { Good = 0, Evil = 1, None = 2 }
  35.         private const string hewwPrefab = "assets/prefabs/building/wall.external.high.wood/wall.external.high.wood.prefab";
  36.         private const string heswPrefab = "assets/prefabs/building/wall.external.high.stone/wall.external.high.stone.prefab";
  37.         private const bool debugMode = false;
  38.         private List<string> readyUiList = new List<string>();
  39.         private List<string> spectators = new List<string>();
  40.         private List<Rematch> rematches = new List<Rematch>();
  41.         private Dictionary<string, AttackerInfo> tdmAttackers = new Dictionary<string, AttackerInfo>();
  42.         private Dictionary<string, string> tdmKits = new Dictionary<string, string>();
  43.         private HashSet<GoodVersusEvilMatch> tdmMatches = new HashSet<GoodVersusEvilMatch>();
  44.         private List<DuelingZone> duelingZones = new List<DuelingZone>(); // where all the fun is at
  45.         private StoredData duelsData = new StoredData();
  46.         private Dictionary<string, string> dataDuelists = new Dictionary<string, string>(); // active duelers
  47.         private Dictionary<string, long> dataImmunity = new Dictionary<string, long>(); // players immune to damage
  48.         private Dictionary<string, Vector3> dataImmunitySpawns = new Dictionary<string, Vector3>(); // players spawn points
  49.         private int blockedMask = LayerMask.GetMask("Player (Server)", "Prevent Building", "Construction", "Deployed", "Trigger"); // layers we won't be setting a zone within 50 meters of
  50.         private int constructionMask = LayerMask.GetMask("Construction", "Deployed");
  51.         private bool matchUpdateRequired;
  52.         private int groundMask = LayerMask.GetMask("Terrain", "World", "Default"); // used to find dueling zone/set custom zone and create spawn points
  53.         private int wallMask = LayerMask.GetMask("Terrain", "World", "Default", "Construction", "Deployed");
  54.         private int waterMask = LayerMask.GetMask("Water"); // used to count water colliders when finding a random dueling zone on the map
  55.         private int worldMask = LayerMask.GetMask("World");
  56.         private Timer announceTimer;
  57.         private SortedDictionary<string, string> boneTags = new SortedDictionary<string, string> { ["r_"] = "Right ", ["l_"] = "Left ", [".prefab"] = string.Empty, ["1"] = string.Empty, ["2"] = string.Empty, ["3"] = string.Empty, ["4"] = string.Empty, ["END"] = string.Empty, ["_"] = " ", ["."] = " " };
  58.         private Dictionary<string, string> announcements = new Dictionary<string, string>(); // users id and announcement
  59.         private Dictionary<string, long> dataDeath = new Dictionary<string, long>(); // users id and timestamp of when they're to be executed
  60.         private Dictionary<string, string> dataRequests = new Dictionary<string, string>(); // users requesting a duel and to whom
  61.         private Dictionary<string, bool> deployables = new Dictionary<string, bool>();
  62.         private Dictionary<ulong, List<BaseEntity>> duelEntities = new Dictionary<ulong, List<BaseEntity>>();
  63.         private DynamicConfigFile duelsFile;
  64.         private Timer eventTimer; // timer to check for immunity and auto death time of duelers
  65.         private Timer matchTimer; // timer to check for updates to the match ui
  66.         private SpawnFilter filter = new SpawnFilter(); // RandomDropPosition()
  67.         private Dictionary<Vector3, float> managedZones = new Dictionary<Vector3, float>(); // blocked zones from zonemanager plugin
  68.         private List<Vector3> monuments = new List<Vector3>(); // positions of monuments on the server
  69.         private Dictionary<string, string> prefabs = new Dictionary<string, string>();
  70.         private bool resetDuelists; // if wipe is detected then assign awards and wipe VictoriesSeed / LossesSeed
  71.         private Dictionary<string, List<ulong>> skinsCache = new Dictionary<string, List<ulong>>(); // used to randomize custom kit skins which skin id values are 0
  72.         private Dictionary<string, string> tdmRequests = new Dictionary<string, string>(); // users requesting a deathmatch and to whom
  73.         private Dictionary<string, List<ulong>> workshopskinsCache = new Dictionary<string, List<ulong>>();
  74.         private List<string> dcsBlock = new List<string>(); // users blocked from 1v1 for 60 seconds after suiciding or disconnecting
  75.         private Dictionary<string, string> playerZones = new Dictionary<string, string>(); // id, set zone name
  76.  
  77.         public class StoredData
  78.         {
  79.             public List<string> Allowed = new List<string>(); // list of users that allow duel requests
  80.             public Dictionary<string, List<string>> AutoGeneratedSpawns = new Dictionary<string, List<string>>();
  81.             public Dictionary<string, string> Bans = new Dictionary<string, string>(); // users banned from dueling
  82.             public Dictionary<string, BetInfo> Bets = new Dictionary<string, BetInfo>(); // active bets users have placed
  83.             public Dictionary<string, List<string>> BlockedUsers = new Dictionary<string, List<string>>(); // users and the list of players they blocked from requesting duels with
  84.             public List<string> Chat = new List<string>(); // user ids of those who opted out of seeing duel death messages
  85.             public List<string> ChatEx = new List<string>(); // user ids of those who opted to see duel death messages when the config blocks them for all players
  86.             public Dictionary<string, List<BetInfo>> ClaimBets = new Dictionary<string, List<BetInfo>>(); // active bets users need to claim after winning a bet
  87.             public Dictionary<string, string> CustomKits = new Dictionary<string, string>(); // userid and custom kit
  88.             public bool DuelsEnabled; // enable/disable dueling for all players (not admins)
  89.             public Dictionary<string, string> Homes = new Dictionary<string, string>(); // user id and location of where they teleported from
  90.             public Dictionary<string, string> Kits = new Dictionary<string, string>(); // userid and kit. give kit when they wake up inside of the dueling zone
  91.             public Dictionary<string, int> Losses = new Dictionary<string, int>(); // user id / losses for lifetime
  92.             public Dictionary<string, int> LossesSeed = new Dictionary<string, int>(); // user id / losses for seed
  93.             public SortedDictionary<long, string> Queued = new SortedDictionary<long, string>(); // queued duelers sorted by timestamp and user id. first come first serve
  94.             public List<string> Restricted = new List<string>(); // list of users blocked from requesting a duel for 60 seconds
  95.             public List<string> Spawns = new List<string>(); // custom spawn points
  96.             public List<string> AutoReady = new List<string>();
  97.             public Dictionary<string, int> MatchVictories = new Dictionary<string, int>(); // player name & total wins
  98.             public Dictionary<string, int> MatchVictoriesSeed = new Dictionary<string, int>(); // player name & wins for current seed
  99.             public Dictionary<string, int> MatchLosses = new Dictionary<string, int>(); // player name & total losses
  100.             public Dictionary<string, int> MatchLossesSeed = new Dictionary<string, int>(); // player name & losses for current seed
  101.             public Dictionary<string, int> MatchDeaths = new Dictionary<string, int>(); // player name & total deaths
  102.             public Dictionary<string, int> MatchDeathsSeed = new Dictionary<string, int>(); // player name & deaths for the seed
  103.             public Dictionary<string, int> MatchKills = new Dictionary<string, int>(); // player name & total kills
  104.             public Dictionary<string, int> MatchKillsSeed = new Dictionary<string, int>(); // player name & kills for current seed
  105.             public Dictionary<string, Dictionary<string, int>> MatchSizesVictories = new Dictionary<string, Dictionary<string, int>>(); // size, id, wins
  106.             public Dictionary<string, Dictionary<string, int>> MatchSizesVictoriesSeed = new Dictionary<string, Dictionary<string, int>>(); // size, id, wins seed
  107.             public Dictionary<string, Dictionary<string, int>> MatchSizesLosses = new Dictionary<string, Dictionary<string, int>>(); // size, id, losses
  108.             public Dictionary<string, Dictionary<string, int>> MatchSizesLossesSeed = new Dictionary<string, Dictionary<string, int>>(); // size, id, losses seed
  109.             public int TotalDuels; // the total amount of duels ever played on the server
  110.             public Dictionary<string, int> Victories = new Dictionary<string, int>(); // user id / wins for lifetime
  111.             public Dictionary<string, int> VictoriesSeed = new Dictionary<string, int>(); // user id / wins for seed
  112.             public List<string> ZoneIds = new List<string>(); // the locations of each dueling zone
  113.             public Dictionary<string, string> DuelZones = new Dictionary<string, string>(); // location, name
  114.         }
  115.  
  116.         private class Tracker : FacepunchBehaviour
  117.         {
  118.             public BasePlayer player;
  119.             private Duelist _;
  120.  
  121.             private void Awake()
  122.             {
  123.                 player = GetComponent<BasePlayer>();                
  124.             }
  125.  
  126.             public void Init(Duelist _)
  127.             {
  128.                 this._ = _;
  129.                 InvokeRepeating(Track, 0f, 0.5f);
  130.             }
  131.  
  132.             private void Track()
  133.             {
  134.                 if (player == null || player.transform == null)
  135.                 {
  136.                     Destroy(this);
  137.                     return;
  138.                 }
  139.  
  140.                 if (_.dataDuelists.ContainsKey(player.UserIDString) || _.tdmMatches.Exists(team => team.GetTeam(player) != Team.None))
  141.                 {
  142.                     if (!_.DuelTerritory(player.transform.position))
  143.                     {
  144.                         player.inventory.Strip();
  145.                         Destroy(this);
  146.                     }
  147.                 }
  148.             }
  149.  
  150.             private void OnDestroy()
  151.             {
  152.                 try { CancelInvoke(); } catch { }
  153.                 Destroy(this);
  154.             }
  155.         }
  156.  
  157.         public class Rematch
  158.         {
  159.             public Rematch(Duelist _)
  160.             {
  161.                 this._ = _;
  162.             }
  163.             private Duelist _;
  164.             public List<BasePlayer> Duelists = new List<BasePlayer>();
  165.             public List<BasePlayer> Ready = new List<BasePlayer>();
  166.             private List<BasePlayer> Evil = new List<BasePlayer>();
  167.             private List<BasePlayer> Good = new List<BasePlayer>();
  168.             public GoodVersusEvilMatch match;
  169.             private Timer _notify;
  170.  
  171.             public List<BasePlayer> Players
  172.             {
  173.                 get
  174.                 {
  175.                     Duelists.RemoveAll(player => !player || !player.IsConnected);
  176.                     Good.RemoveAll(player => !player || !player.IsConnected);
  177.                     Evil.RemoveAll(player => !player || !player.IsConnected);
  178.                     Ready.RemoveAll(player => !player || !player.IsConnected);
  179.  
  180.                     if (match == null)
  181.                         return Duelists;
  182.                    
  183.                     var _list = new List<BasePlayer>();
  184.                    
  185.                     _list.AddRange(Good);
  186.                     _list.AddRange(Evil);
  187.                    
  188.                     return _list;
  189.                 }
  190.             }
  191.  
  192.             public bool HasPlayer(BasePlayer player)
  193.             {
  194.                 return Players.Contains(player);
  195.             }
  196.  
  197.             public bool AddRange(List<BasePlayer> players, Team team)
  198.             {
  199.                 foreach (var player in players)
  200.                 {
  201.                     if (!player || !player.IsConnected || _.InEvent(player) || Good.Contains(player) || Evil.Contains(player))
  202.                         break;
  203.  
  204.                     if (team == Team.Evil)
  205.                         Evil.Add(player);
  206.                     else
  207.                         Good.Add(player);
  208.                 }
  209.  
  210.                 return (team == Team.Evil ? Evil.Count : Good.Count) == players.Count;
  211.             }
  212.  
  213.             public bool IsReady(BasePlayer player)
  214.             {
  215.                 if (_.InEvent(player) || !_.IsNewman(player) || _.duelsData.Bans.ContainsKey(player.UserIDString))
  216.                     return false;
  217.  
  218.                 return true;
  219.             }
  220.  
  221.             public bool IsReady()
  222.             {
  223.                 if (Players.Exists(player => !IsReady(player)))
  224.                 {
  225.                     Reset("RematchFailed2");
  226.                     return false;
  227.                 }
  228.  
  229.                 return Ready.Count == (match == null ? 2 : match.TeamSize * 2);
  230.             }
  231.  
  232.             private void Reset(string key)
  233.             {
  234.                 _.tdmMatches.Remove(match);
  235.                 MessageAll(key);
  236.                 Duelists.Clear();
  237.                 Good.Clear();
  238.                 Evil.Clear();
  239.                 Ready.Clear();
  240.                 _notify?.Destroy();
  241.                 _.rematches.Remove(this);
  242.                 _.matchUpdateRequired = true;
  243.             }
  244.  
  245.             public void MessageAll(string key, params object[] args)
  246.             {
  247.                 foreach (var player in Players)
  248.                 {
  249.                     Message(player, _.msg(key, player.UserIDString, args));
  250.                 }
  251.             }
  252.  
  253.             public void Notify()
  254.             {
  255.                 MessageAll("RematchNotify", 60f, match == null ? _.szDuelChatCommand : _.szMatchChatCommand);
  256.  
  257.                 foreach (var player in Players)
  258.                     if (_.duelsData.AutoReady.Contains(player.UserIDString))
  259.                         Ready.Add(player);
  260.  
  261.                 if (IsReady())
  262.                 {
  263.                     Start();
  264.                     _.rematches.Remove(this);
  265.                 }
  266.                 else if (match == null || !match.IsPublic)
  267.                     _notify = _.timer.Once(60f, Cancel);
  268.             }
  269.  
  270.             private void Cancel()
  271.             {
  272.                 if (match != null && match.IsPublic)
  273.                     return;
  274.  
  275.                 if (_.rematches.Contains(this))
  276.                 {
  277.                     if (_.sendHomeSpectatorWhenRematchTimesOut)
  278.                     {
  279.                         foreach (var player in Players)
  280.                         {
  281.                             if (_.IsSpectator(player))
  282.                             {
  283.                                 _.EndSpectate(player);
  284.                                 _.SendHome(player);
  285.                             }
  286.                         }
  287.                     }
  288.  
  289.                     if (match != null)
  290.                     {
  291.                         match.Reuse();
  292.                     }
  293.  
  294.                     _.tdmMatches.Remove(match);
  295.                     Reset("RematchTimedOut");
  296.                 }
  297.             }
  298.  
  299.             public void Start()
  300.             {
  301.                 if (match == null)
  302.                 {
  303.                     var player = Ready[0];
  304.                     var target = Ready[1];
  305.  
  306.                     if (!_.SelectZone(player, target))
  307.                     {
  308.                         Message(player, _.msg("AllZonesFull", player.UserIDString, _.duelingZones.Count, _.playersPerZone));
  309.                         Message(target, _.msg("AllZonesFull", target.UserIDString, _.duelingZones.Count, _.playersPerZone));
  310.                     }
  311.                 }
  312.                 else
  313.                 {
  314.                     match.Reuse();
  315.                     _.tdmMatches.Add(match);
  316.  
  317.                     if (!AddMatchPlayers(Good, Team.Good) || !AddMatchPlayers(Evil, Team.Evil))
  318.                     {
  319.                         Reset("RematchFailed");
  320.                         match.Reuse();
  321.                     }
  322.                 }
  323.  
  324.                 _notify?.Destroy();
  325.                 _.rematches.Remove(this);
  326.             }
  327.  
  328.             private bool AddMatchPlayers(List<BasePlayer> players, Team team)
  329.             {
  330.                 foreach (var player in players)
  331.                     if (!match.AddMatchPlayer(player, team))
  332.                         return false;
  333.  
  334.                 return true;
  335.             }
  336.         }
  337.  
  338.         public class AttackerInfo
  339.         {
  340.             public string AttackerName = "";
  341.             public string AttackerId = "";
  342.             public string BoneName = "";
  343.             public string Distance = "";
  344.             public string Weapon = "";
  345.         }
  346.  
  347.         public class GoodVersusEvilMatch
  348.         {
  349.             public GoodVersusEvilMatch(Duelist _)
  350.             {
  351.                 this._ = _;
  352.             }
  353.             private Duelist _;
  354.             private HashSet<ulong> _banned = new HashSet<ulong>();
  355.             private HashSet<BasePlayer> _evil = new HashSet<BasePlayer>();
  356.             private HashSet<ulong> _evilKIA = new HashSet<ulong>();
  357.             private List<BasePlayer> _evilRematch = new List<BasePlayer>();
  358.             private HashSet<BasePlayer> _good = new HashSet<BasePlayer>();
  359.             private HashSet<ulong> _goodKIA = new HashSet<ulong>();
  360.             private List<BasePlayer> _goodRematch = new List<BasePlayer>();
  361.             private string _goodHostName = "";
  362.             private string _evilHostName = "";
  363.             private string _goodHostId = "";
  364.             private string _evilHostId = "";
  365.             private string _goodCode = "";
  366.             private string _evilCode = "";
  367.             private int _teamSize = 2;
  368.             private bool _started;
  369.             private bool _ended;
  370.             private string _kit = "";
  371.             private DuelingZone _zone;
  372.             private Timer _queueTimer;
  373.             private bool _enteredQueue;
  374.             private bool _public;
  375.             public bool CanRematch = true;
  376.  
  377.             public string Id
  378.             {
  379.                 get
  380.                 {
  381.                     return _goodHostId + _evilHostId;
  382.                 }
  383.             }
  384.  
  385.             public string Versus
  386.             {
  387.                 get
  388.                 {
  389.                     return string.Format("{0} / {1} {2}v{2}", _goodHostName, _evilHostName, _teamSize);
  390.                 }
  391.             }
  392.  
  393.             public bool IsPublic
  394.             {
  395.                 get
  396.                 {
  397.                     return _public;
  398.                 }
  399.                 set
  400.                 {
  401.                     _public = value;
  402.                     _.matchUpdateRequired = true;
  403.                     MessageAll(_public ? "MatchPublic" : "MatchPrivate");
  404.                 }
  405.             }
  406.  
  407.             public int TeamSize
  408.             {
  409.                 get
  410.                 {
  411.                     return _teamSize;
  412.                 }
  413.                 set
  414.                 {
  415.                     if (IsStarted)
  416.                         return;
  417.  
  418.                     _teamSize = value;
  419.                     _.matchUpdateRequired = true;
  420.                     MessageAll("MatchSizeChanged", _teamSize);
  421.                 }
  422.             }
  423.  
  424.             public DuelingZone Zone
  425.             {
  426.                 get
  427.                 {
  428.                     return _zone;
  429.                 }
  430.             }
  431.  
  432.             public bool EitherEmpty
  433.             {
  434.                 get
  435.                 {
  436.                     return _good.Count == 0 || _evil.Count == 0;
  437.                 }
  438.             }
  439.  
  440.             public bool IsStarted
  441.             {
  442.                 get
  443.                 {
  444.                     return _started;
  445.                 }
  446.                 set
  447.                 {
  448.                     _started = value;
  449.                     _.matchUpdateRequired = true;
  450.                 }
  451.             }
  452.  
  453.             public bool IsOver
  454.             {
  455.                 get
  456.                 {
  457.                     return _ended;
  458.                 }
  459.                 set
  460.                 {
  461.                     _ended = value;
  462.                     _.matchUpdateRequired = true;
  463.                 }
  464.             }
  465.  
  466.             public string Kit
  467.             {
  468.                 get
  469.                 {
  470.                     return _kit;
  471.                 }
  472.                 set
  473.                 {
  474.                     _kit = value;
  475.  
  476.                     if (!EitherEmpty)
  477.                     {
  478.                         _good.RemoveWhere(target => !target || !target.IsConnected);
  479.                         _evil.RemoveWhere(target => !target || !target.IsConnected);
  480.  
  481.                         _good.ToList().ForEach(player => _.duelsData.Kits[player.UserIDString] = _kit);
  482.                         _evil.ToList().ForEach(player => _.duelsData.Kits[player.UserIDString] = _kit);
  483.  
  484.                         MessageAll("MatchKitSet", _kit);
  485.                     }
  486.                 }
  487.             }
  488.  
  489.             public void Reuse()
  490.             {
  491.                 if (_zone != null)
  492.                     _zone.IsLocked = false;
  493.  
  494.                 _evilRematch.Clear();
  495.                 _goodRematch.Clear();
  496.                 _good.Clear();
  497.                 _evil.Clear();
  498.                 _goodKIA.Clear();
  499.                 _evilKIA.Clear();
  500.                 _started = false;
  501.                 _ended = false;                
  502.                 _enteredQueue = false;
  503.                 _kit = _.GetRandomKit();
  504.                 _goodHostId = BasePlayer.activePlayerList.FirstOrDefault(x => x.displayName == _goodHostName)?.UserIDString ?? _goodHostId;
  505.                 _evilHostId = BasePlayer.activePlayerList.FirstOrDefault(x => x.displayName == _evilHostName)?.UserIDString ?? _evilHostId;
  506.                 _.matchUpdateRequired = true;
  507.             }
  508.  
  509.             public void Setup(BasePlayer player, BasePlayer target)
  510.             {
  511.                 _.tdmMatches.Add(this);
  512.                 _goodHostName = player.displayName;
  513.                 _goodHostId = player.UserIDString;
  514.                 _evilHostName = target.displayName;
  515.                 _evilHostId = target.UserIDString;
  516.                 _goodCode = Random.Range(10000, 99999).ToString();
  517.                 _evilCode = Random.Range(10000, 99999).ToString();
  518.  
  519.                 if (_teamSize < _.minDeathmatchSize)
  520.                     _teamSize = _.minDeathmatchSize;
  521.  
  522.                 AddMatchPlayer(player, Team.Good);
  523.                 AddMatchPlayer(target, Team.Evil);
  524.  
  525.                 if (_.tdmKits.ContainsKey(player.UserIDString))
  526.                 {
  527.                     Kit = _.tdmKits[player.UserIDString];
  528.                     _.tdmKits.Remove(player.UserIDString);
  529.                 }
  530.                 else if (_.tdmKits.ContainsKey(target.UserIDString))
  531.                 {
  532.                     Kit = _.tdmKits[target.UserIDString];
  533.                     _.tdmKits.Remove(target.UserIDString);
  534.                 }
  535.                 else
  536.                     Kit = _.GetRandomKit();
  537.  
  538.                 if (TeamSize > 1)
  539.                 {
  540.                     Message(player, _.msg("MatchOpened", player.UserIDString, _.szMatchChatCommand, _goodCode));
  541.                     Message(target, _.msg("MatchOpened", target.UserIDString, _.szMatchChatCommand, _evilCode));
  542.                 }
  543.  
  544.                 _.matchUpdateRequired = true;
  545.             }
  546.  
  547.             public bool IsFull()
  548.             {
  549.                 return _good.Count == _teamSize && _evil.Count == _teamSize;
  550.             }
  551.  
  552.             public bool IsFull(Team team)
  553.             {
  554.                 return team == Team.Good ? _good.Count == _teamSize : _evil.Count == _teamSize;
  555.             }
  556.  
  557.             public void MessageAll(string key, params object[] args)
  558.             {
  559.                 _.Message(_good, key, args);
  560.                 _.Message(_evil, key, args);
  561.             }
  562.  
  563.             public Team GetTeam(BasePlayer player)
  564.             {
  565.                 return _good.Contains(player) ? Team.Good : _evil.Contains(player) ? Team.Evil : Team.None;
  566.             }
  567.  
  568.             public bool IsHost(BasePlayer player)
  569.             {
  570.                 return player.UserIDString == _goodHostId || player.UserIDString == _evilHostId;
  571.             }
  572.  
  573.             public void SetCode(BasePlayer player, string code)
  574.             {
  575.                 if (GetTeam(player) == Team.Evil)
  576.                     _evilCode = code;
  577.                 else if (GetTeam(player) == Team.Good)
  578.                     _goodCode = code;
  579.             }
  580.  
  581.             public string Code(Team team)
  582.             {
  583.                 return team == Team.Good ? _goodCode : _evilCode;
  584.             }
  585.  
  586.             public bool AlliedTo(BasePlayer player, Team team)
  587.             {
  588.                 return _.IsAllied(player.UserIDString, team == Team.Good ? _goodHostId : _evilHostId);
  589.             }
  590.  
  591.             public bool IsBanned(ulong targetId)
  592.             {
  593.                 return _banned.Contains(targetId);
  594.             }
  595.  
  596.             public bool Ban(BasePlayer target)
  597.             {
  598.                 if (target.UserIDString == _goodHostId || target.UserIDString == _evilHostId || IsBanned(target.userID))
  599.                     return false;
  600.  
  601.                 _banned.Add(target.userID);
  602.                 RemoveMatchPlayer(target);
  603.                 return true;
  604.             }
  605.  
  606.             public bool Equals(GoodVersusEvilMatch match)
  607.             {
  608.                 return match._good.Equals(_good) && match._evil.Equals(_evil);
  609.             }
  610.  
  611.             public string GetNames(Team team)
  612.             {
  613.                 return string.Join(", ", team == Team.Good ? _good.ToList().Select(player => player.displayName) : _evil.ToList().Select(player => player.displayName));
  614.             }
  615.  
  616.             public void GiveShirt(BasePlayer player)
  617.             {
  618.                 Item item = ItemManager.CreateByName(_.teamShirt, 1, GetTeam(player) == Team.Evil ? _.teamEvilShirt : _.teamGoodShirt);
  619.  
  620.                 if (item == null)
  621.                     return;
  622.  
  623.                 if (item.info.category != ItemCategory.Attire)
  624.                 {
  625.                     item.Remove(0.01f);
  626.                     return;
  627.                 }
  628.  
  629.                 foreach (Item wear in player.inventory.containerWear.itemList)
  630.                 {
  631.                     if (wear.info.shortname.Contains("shirt"))
  632.                     {
  633.                         wear.RemoveFromContainer();
  634.                         wear.Remove(0.01f);
  635.                         break;
  636.                     }
  637.                 }
  638.  
  639.                 item.MoveToContainer(player.inventory.containerWear, -1, false);
  640.  
  641.                 if (!player.inventory.containerWear.HasFlag(ItemContainer.Flag.IsLocked))
  642.                     player.inventory.containerWear.SetFlag(ItemContainer.Flag.IsLocked, true);
  643.             }
  644.  
  645.             public bool AddMatchPlayer(BasePlayer player, Team team)
  646.             {
  647.                 if (_started)
  648.                 {
  649.                     Message(player, _.msg("MatchStartedAlready", player.UserIDString));
  650.                     return false;
  651.                 }
  652.  
  653.                 _good.RemoveWhere(target => !target || !target.IsConnected);
  654.                 _evil.RemoveWhere(target => !target || !target.IsConnected);
  655.  
  656.                 if (_banned.Contains(player.userID))
  657.                     return false;
  658.  
  659.                 if (!_.IsNewman(player))
  660.                 {
  661.                     Message(player, _.msg("MustBeNaked", player.UserIDString));
  662.                     return false;
  663.                 }
  664.  
  665.                 switch (team)
  666.                 {
  667.                     case Team.Good:
  668.                         if (_good.Count == _teamSize)
  669.                         {
  670.                             Message(player, _.msg("MatchTeamFull", player.UserIDString, _teamSize));
  671.                             return false;
  672.                         }
  673.  
  674.                         _good.Add(player);
  675.                         MessageAll("MatchJoinedTeam", player.displayName, _goodHostName, _good.Count, _teamSize, _evilHostName, _evil.Count);
  676.                         break;
  677.                     case Team.Evil:
  678.                         if (_evil.Count == _teamSize)
  679.                         {
  680.                             Message(player, _.msg("MatchTeamFull", player.UserIDString, _teamSize));
  681.                             return false;
  682.                         }
  683.  
  684.                         _evil.Add(player);
  685.                         MessageAll("MatchJoinedTeam", player.displayName, _evilHostName, _evil.Count, _teamSize, _goodHostName, _good.Count);
  686.                         break;
  687.                 }
  688.  
  689.                 if (_good.Count == _teamSize && _evil.Count == _teamSize)
  690.                     Queue();
  691.  
  692.                 return true;
  693.             }
  694.  
  695.             public bool RemoveMatchPlayer(BasePlayer player)
  696.             {
  697.                 if (player == null)
  698.                     return false;
  699.  
  700.                 if (player.inventory.containerWear.HasFlag(ItemContainer.Flag.IsLocked))
  701.                     player.inventory.containerWear.SetFlag(ItemContainer.Flag.IsLocked, false);
  702.  
  703.                 _.Metabolize(player, false);
  704.                 _.Track(player, false);
  705.                 _.RemoveEntities(player.userID);
  706.                 Interface.Oxide.CallHook("DisableBypass", player.userID);
  707.  
  708.                 if (_.DuelTerritory(player.transform.position))
  709.                 {
  710.                     if (_.sendDefeatedHome)
  711.                         _.SendHome(player);
  712.                     else
  713.                         _.StartSpectate(player);
  714.                 }
  715.  
  716.                 if (IsOver)
  717.                 {
  718.                     _good.Remove(player);
  719.                     _evil.Remove(player);
  720.                     return true;
  721.                 }
  722.  
  723.                 if (_good.Remove(player))
  724.                 {
  725.                     if (_good.Count == 0)
  726.                     {
  727.                         if (_started)
  728.                         {
  729.                             _goodKIA.Add(player.userID);
  730.                             _goodRematch.Add(player);
  731.                         }
  732.                         else
  733.                             MessageAll("MatchNoPlayersLeft");
  734.  
  735.                         EndMatch(Team.Evil);
  736.                         return true;
  737.                     }
  738.                     if (_started)
  739.                     {
  740.                         _goodKIA.Add(player.userID);
  741.                         _goodRematch.Add(player);
  742.                     }
  743.  
  744.                     if (player.UserIDString == _goodHostId)
  745.                         AssignGoodHostId();
  746.  
  747.                     return true;
  748.                 }
  749.  
  750.                 if (_evil.Remove(player))
  751.                 {
  752.                     if (_evil.Count == 0)
  753.                     {
  754.                         if (_started)
  755.                         {
  756.                             _evilKIA.Add(player.userID);
  757.                             _evilRematch.Add(player);
  758.                         }
  759.                         else
  760.                             MessageAll("MatchNoPlayersLeft");
  761.  
  762.                         EndMatch(Team.Good);
  763.                         return true;
  764.                     }
  765.                     if (_started)
  766.                     {
  767.                         _evilKIA.Add(player.userID);
  768.                         _evilRematch.Add(player);
  769.                     }
  770.  
  771.                     if (player.UserIDString == _evilHostId)
  772.                         AssignEvilHostId();
  773.  
  774.                     return true;
  775.                 }
  776.  
  777.                 return false;
  778.             }
  779.  
  780.             private void AssignGoodHostId()
  781.             {
  782.                 _good.RemoveWhere(player => !player || !player.IsConnected);
  783.  
  784.                 if (_good.Count > 0)
  785.                 {
  786.                     _goodHostId = _good.FirstOrDefault().UserIDString;
  787.                     _.matchUpdateRequired = true;
  788.                 }
  789.                 else
  790.                     EndMatch(Team.Evil);
  791.             }
  792.  
  793.             private void AssignEvilHostId()
  794.             {
  795.                 _evil.RemoveWhere(player => !player || !player.IsConnected);
  796.  
  797.                 if (_evil.Count > 0)
  798.                 {
  799.                     _evilHostId = _evil.FirstOrDefault().UserIDString;
  800.                     _.matchUpdateRequired = true;
  801.                 }
  802.                 else
  803.                     EndMatch(Team.Good);
  804.             }
  805.  
  806.             private void Finalize(Team team)
  807.             {
  808.                 Interface.CallHook("OnDuelistFinalized", team == Team.Good ? _goodKIA : _evilKIA);
  809.  
  810.                 switch (team)
  811.                 {
  812.                     case Team.Evil:
  813.                         {
  814.                             foreach (ulong playerId in _goodKIA)
  815.                             {
  816.                                 _.UpdateMatchStats(playerId.ToString(), false, true, false, false);
  817.                                 _.UpdateMatchSizeStats(playerId.ToString(), true, false, _teamSize);
  818.                             }
  819.  
  820.                             foreach (ulong playerId in _evilKIA)
  821.                             {
  822.                                 _.AwardPlayer(playerId, _.teamEconomicsMoney, _.teamServerRewardsPoints);
  823.                                 _.UpdateMatchStats(playerId.ToString(), true, false, false, false);
  824.                                 _.UpdateMatchSizeStats(playerId.ToString(), false, true, _teamSize);
  825.                             }
  826.  
  827.                             break;
  828.                         }
  829.                     case Team.Good:
  830.                         {
  831.                             foreach (ulong playerId in _evilKIA)
  832.                             {
  833.                                 _.UpdateMatchStats(playerId.ToString(), false, true, false, false);
  834.                                 _.UpdateMatchSizeStats(playerId.ToString(), true, false, _teamSize);
  835.                             }
  836.  
  837.                             foreach (ulong playerId in _goodKIA)
  838.                             {
  839.                                 _.AwardPlayer(playerId, _.teamEconomicsMoney, _.teamServerRewardsPoints);
  840.                                 _.UpdateMatchStats(playerId.ToString(), true, false, false, false);
  841.                                 _.UpdateMatchSizeStats(playerId.ToString(), false, true, _teamSize);
  842.                             }
  843.  
  844.                             break;
  845.                         }
  846.                 }
  847.  
  848.                 _goodKIA.Clear();
  849.                 _evilKIA.Clear();
  850.             }
  851.  
  852.             private bool SetupRematch()
  853.             {
  854.                 if (!CanRematch || _goodRematch.Exists(player => !player || !player.IsConnected) || _evilRematch.Exists(player => !player || !player.IsConnected))
  855.                     return false;
  856.  
  857.                 var rematch = new Rematch(_);
  858.  
  859.                 if (rematch.AddRange(_evilRematch, Team.Evil) && rematch.AddRange(_goodRematch, Team.Good))
  860.                 {
  861.                     rematch.match = this;
  862.                     rematch.Notify();
  863.                     _.rematches.Add(rematch);
  864.                     return true;
  865.                 }
  866.  
  867.                 return false;
  868.             }
  869.  
  870.             private void EndMatch(Team team)
  871.             {
  872.                 if (!_ended && _started)
  873.                 {
  874.                     Finalize(team);
  875.                     _.Puts(_.msg("MatchDefeat", null, team == Team.Evil ? _evilHostName : _goodHostName, team == Team.Evil ? _goodHostName : _evilHostName, _teamSize));
  876.                     IsOver = true;
  877.                     IsStarted = false;
  878.  
  879.                     foreach (var player in _evil.ToList())
  880.                     {
  881.                         RemoveMatchPlayer(player);
  882.  
  883.                         if (player != null && player.IsConnected && !_evilRematch.Contains(player))
  884.                             _evilRematch.Add(player);
  885.                     }
  886.  
  887.                     foreach (var player in _good.ToList())
  888.                     {
  889.                         RemoveMatchPlayer(player);
  890.  
  891.                         if (player != null && player.IsConnected && !_goodRematch.Contains(player))
  892.                             _goodRematch.Add(player);
  893.                     }
  894.  
  895.                     foreach (var target in BasePlayer.activePlayerList.Where(p => p?.displayName != null))
  896.                     {
  897.                         if (_.guiAnnounceUITime > 0f && (_goodKIA.Contains(target.userID) || _evilKIA.Contains(target.userID)))
  898.                             _.CreateAnnouncementUI(target, _.msg("MatchDefeat", target.UserIDString, team == Team.Evil ? _evilHostName : _goodHostName, team == Team.Evil ? _goodHostName : _evilHostName, _teamSize));
  899.  
  900.                         if (_.duelsData.Chat.Contains(target.UserIDString) && !_goodKIA.Contains(target.userID) && !_evilKIA.Contains(target.userID))
  901.                             continue;
  902.  
  903.                         Message(target, _.msg("MatchDefeat", target.UserIDString, team == Team.Evil ? _evilHostName : _goodHostName, team == Team.Evil ? _goodHostName : _evilHostName, _teamSize));
  904.                     }
  905.  
  906.                     if (!SetupRematch())
  907.                     {
  908.                         var _list = new List<BasePlayer>();
  909.  
  910.                         _list.AddRange(_evilRematch);
  911.                         _list.AddRange(_goodRematch);
  912.                        
  913.                         foreach (var player in _list)
  914.                         {
  915.                             if (!player || !player.IsConnected) continue;
  916.                             Message(player, _.msg("RematchFailed2", player.UserIDString));
  917.                         }
  918.  
  919.                         Reuse();
  920.                     }
  921.                 }
  922.  
  923.                 End();
  924.             }
  925.  
  926.             public void End(bool forced = false)
  927.             {
  928.                 if (_zone != null)
  929.                     _zone.IsLocked = false;
  930.  
  931.                 _queueTimer?.Destroy();
  932.                 _good.RemoveWhere(player => !player);
  933.                 _evil.RemoveWhere(player => !player);
  934.  
  935.                 var _list = new List<BasePlayer>();
  936.  
  937.                 _list.AddRange(_good);
  938.                 _list.AddRange(_evil);
  939.  
  940.                 foreach (var player in _list)
  941.                 {
  942.                     if (player.inventory.containerWear.HasFlag(ItemContainer.Flag.IsLocked))
  943.                         player.inventory.containerWear.SetFlag(ItemContainer.Flag.IsLocked, false);
  944.  
  945.                     if (IsStarted || IsOver)
  946.                     {
  947.                         if (_.DuelTerritory(player.transform.position))
  948.                         {
  949.                             player.inventory.Strip();
  950.                             _.SendHome(player);
  951.                         }
  952.  
  953.                         _.Metabolize(player, false);
  954.                         _.Track(player, false);
  955.                     }
  956.                 }
  957.  
  958.                 _good.Clear();
  959.                 _evil.Clear();
  960.                 _.tdmMatches.Remove(this);
  961.                 _.matchUpdateRequired = true;
  962.  
  963.                 if (_.dataDuelists.Count == 0 && _.tdmMatches.Count == 0)
  964.                     _.Unsubscribe(nameof(OnPlayerHealthChange));
  965.             }
  966.  
  967.             private void Queue()
  968.             {
  969.                 DuelingZone zone = null;
  970.  
  971.                 var _list = new List<BasePlayer>();
  972.  
  973.                 _list.AddRange(_good);
  974.                 _list.AddRange(_evil);
  975.  
  976.                 foreach (var player in _list)
  977.                 {
  978.                     if (player == null)
  979.                         continue;
  980.  
  981.                     if (!_.IsNewman(player))
  982.                     {
  983.                         Message(player, _.msg("MustBeNaked", player.UserIDString));
  984.                         MessageAll("MatchIsNotNaked", player.displayName);
  985.                         _queueTimer = _.timer.Once(30f, Queue);
  986.                         return;
  987.                     }
  988.  
  989.                     if (zone == null)
  990.                         zone = _.GetPlayerZone(player, TeamSize);
  991.                 }
  992.  
  993.                 var zones = _.duelingZones.Where(x => x.TotalPlayers == 0 && !x.IsLocked && x.Spawns.Count >= (_.requireTeamSize ? TeamSize * 2 : 2)).ToList();
  994.  
  995.                 if (zones == null || zones.Count == 0)
  996.                 {
  997.                     if (!_enteredQueue)
  998.                     {
  999.                         MessageAll("MatchQueued");
  1000.                         _enteredQueue = true;
  1001.                     }
  1002.  
  1003.                     _queueTimer = _.timer.Once(2f, Queue);
  1004.                     return;
  1005.                 }
  1006.  
  1007.                 _zone = zone ?? LastZone ?? zones.GetRandom();
  1008.                 _queueTimer?.Destroy();
  1009.                 Start();
  1010.             }
  1011.  
  1012.             public DuelingZone LastZone
  1013.             {
  1014.                 get
  1015.                 {
  1016.                     DuelingZone zone = null;
  1017.  
  1018.                     if (_good.Exists(player => _.DuelTerritory(player.transform.position)))
  1019.                         zone = _.GetDuelZone(_good.FirstOrDefault(player => _.DuelTerritory(player.transform.position)).transform.position);
  1020.  
  1021.                     if (_evil.Exists(player => _.DuelTerritory(player.transform.position)))
  1022.                         zone = _.GetDuelZone(_evil.FirstOrDefault(player => _.DuelTerritory(player.transform.position)).transform.position);
  1023.  
  1024.                     return zone == null || zone.TotalPlayers > 0 || zone.IsLocked ? null : zone;
  1025.                 }
  1026.             }
  1027.  
  1028.             private void Start()
  1029.             {
  1030.                 _.SubscribeHooks(true);
  1031.  
  1032.                 var goodSpawn = _zone.Spawns.GetRandom();
  1033.                 var evilSpawn = goodSpawn;
  1034.                 float dist = -100f;
  1035.  
  1036.                 foreach (var spawn in _zone.Spawns) // get the furthest spawn point away from the good team and assign it to the evil team
  1037.                 {
  1038.                     float distance = Vector3.Distance(spawn, goodSpawn);
  1039.  
  1040.                     if (distance > dist)
  1041.                     {
  1042.                         dist = distance;
  1043.                         evilSpawn = spawn;
  1044.                     }
  1045.                 }
  1046.  
  1047.                 _.Message(_good, "MatchStarted", GetNames(Team.Evil));
  1048.                 _.Message(_evil, "MatchStarted", GetNames(Team.Good));
  1049.                 _zone.IsLocked = true;
  1050.                 IsStarted = true;
  1051.  
  1052.                 Spawn(_good, goodSpawn);
  1053.                 Spawn(_evil, evilSpawn);
  1054.             }
  1055.  
  1056.             private void Spawn(HashSet<BasePlayer> players, Vector3 spawn)
  1057.             {
  1058.                 foreach (var player in players)
  1059.                 {
  1060.                     _.duelsData.Kits[player.UserIDString] = _kit;
  1061.  
  1062.                     if (!_.DuelTerritory(player.transform.position) || !_.duelsData.Homes.ContainsKey(player.UserIDString))
  1063.                     {
  1064.                         var ppos = player.transform.position;
  1065.                         if (_.IsOnConstruction(ppos)) ppos.y += 1; // prevent player from becoming stuck or dying when teleported home
  1066.                         _.duelsData.Homes[player.UserIDString] = ppos.ToString();
  1067.                     }
  1068.  
  1069.                     _.RemoveFromQueue(player.UserIDString);
  1070.                     _.Teleport(player, spawn);
  1071.  
  1072.                     if (_.immunityTime >= 1)
  1073.                     {
  1074.                         _.dataImmunity[player.UserIDString] = TimeStamp() + _.immunityTime;
  1075.                         _.dataImmunitySpawns[player.UserIDString] = spawn;
  1076.                     }
  1077.                 }
  1078.             }
  1079.         }
  1080.  
  1081.         public class DuelKitItem
  1082.         {
  1083.             public string ammo;
  1084.             public int amount;
  1085.             public string container;
  1086.             public List<string> mods;
  1087.             public string shortname;
  1088.             public ulong skin;
  1089.             public int slot;
  1090.         }
  1091.  
  1092.         public class BetInfo
  1093.         {
  1094.             public string trigger; // the trigger used to request this as a bet
  1095.             public int amount; // amount the player bet
  1096.             public int itemid; // the unique identifier of the item
  1097.             public int max; // the maximum amount allowed to bet on this item
  1098.  
  1099.             public bool Equals(BetInfo bet)
  1100.             {
  1101.                 return bet.amount == amount && bet.itemid == itemid;
  1102.             }
  1103.         }
  1104.  
  1105.         public class DuelingZone : FacepunchBehaviour // Thanks @Jake_Rich for helping me get this started!
  1106.         {
  1107.             private Duelist _ => Instance;
  1108.             private HashSet<BasePlayer> _players = new HashSet<BasePlayer>();
  1109.             private HashSet<BasePlayer> _waiting = new HashSet<BasePlayer>();
  1110.             private Vector3 _zonePos;
  1111.             private List<Vector3> _duelSpawns = new List<Vector3>(); // spawn points generated on the fly
  1112.             private List<SphereEntity> spheres = new List<SphereEntity>();
  1113.             public bool IsLocked;
  1114.             public int Kills;
  1115.  
  1116.             public int TotalPlayers
  1117.             {
  1118.                 get
  1119.                 {
  1120.                     return _players.Count;
  1121.                 }
  1122.             }
  1123.  
  1124.             public List<BasePlayer> Players
  1125.             {
  1126.                 get
  1127.                 {
  1128.                     return _players.ToList();
  1129.                 }
  1130.             }
  1131.  
  1132.             public List<Vector3> Spawns
  1133.             {
  1134.                 get
  1135.                 {
  1136.                     var spawns = _.GetSpawnPoints(this); // get custom spawn points if any exist
  1137.  
  1138.                     return spawns == null || spawns.Count < 2 ? _duelSpawns : spawns;
  1139.                 }
  1140.             }
  1141.  
  1142.             public bool IsFull
  1143.             {
  1144.                 get
  1145.                 {
  1146.                     return TotalPlayers + _waiting.Count + 2 > _.playersPerZone || IsLocked;
  1147.                 }
  1148.             }
  1149.  
  1150.             public Vector3 Position
  1151.             {
  1152.                 get
  1153.                 {
  1154.                     return _zonePos;
  1155.                 }
  1156.             }
  1157.  
  1158.             private void OnDestroy()
  1159.             {
  1160.                 Destroy(gameObject);
  1161.                 Destroy(this);
  1162.             }
  1163.  
  1164.             private void OnTriggerEnter(Collider col)
  1165.             {
  1166.                 if (col == null) return;
  1167.                 var entity = col.ToBaseEntity();
  1168.  
  1169.                 if (entity is BaseMountable)
  1170.                 {
  1171.                     var m = entity as BaseMountable;
  1172.  
  1173.                     if (m.GetParentEntity() is TrainCar)
  1174.                     {
  1175.                         return;
  1176.                     }
  1177.  
  1178.                     RemoveMountable(m, GetMountedPlayers(m));
  1179.                 }
  1180.                 else if (entity is BasePlayer)
  1181.                 {
  1182.                     var p = entity as BasePlayer;
  1183.  
  1184.                     if (_.InEvent(p) || _.IsSpectator(p))
  1185.                     {                        
  1186.                         return;
  1187.                     }
  1188.  
  1189.                     RemovePlayer(p);
  1190.                 }
  1191.             }
  1192.  
  1193.  
  1194.             public Vector3 GetEjectLocation(Vector3 a, float distance)
  1195.             {
  1196.                 var position = ((a.XZ3D() - _zonePos.XZ3D()).normalized * (_.zoneRadius + distance)) + _zonePos; // credits ZoneManager
  1197.                 float y = TerrainMeta.HighestPoint.y + 250f;
  1198.  
  1199.                 RaycastHit hit;
  1200.                 if (Physics.Raycast(position + new Vector3(0f, y, 0f), Vector3.down, out hit, Mathf.Infinity, targetLayer, QueryTriggerInteraction.Ignore))
  1201.                 {
  1202.                     position.y = hit.point.y + 0.75f;
  1203.                 }
  1204.                 else position.y = Mathf.Max(TerrainMeta.HeightMap.GetHeight(position), TerrainMeta.WaterMap.GetHeight(position)) + 0.75f;
  1205.  
  1206.                 return position;
  1207.             }
  1208.  
  1209.             public bool RemovePlayer(BasePlayer player)
  1210.             {
  1211.                 if (!player.IsHuman() || player.IsAdmin)
  1212.                 {
  1213.                     return false;
  1214.                 }
  1215.  
  1216.                 var m = player.GetMounted();
  1217.  
  1218.                 if (m.IsValid())
  1219.                 {
  1220.                     var players = GetMountedPlayers(m);
  1221.  
  1222.                     players.RemoveAll(x => x == null || !x.IsHuman());
  1223.  
  1224.                     if (RemoveMountable(m, players))
  1225.                     {
  1226.                         return true;
  1227.                     }
  1228.                 }
  1229.  
  1230.                 var position = GetEjectLocation(player.transform.position, 10f);
  1231.  
  1232.                 if (player.IsFlying)
  1233.                 {
  1234.                     position.y = player.transform.position.y;
  1235.                 }
  1236.  
  1237.                 player.Teleport(position);
  1238.                 player.SendNetworkUpdateImmediate();
  1239.  
  1240.                 return true;
  1241.             }
  1242.  
  1243.             public void DismountAllPlayers(BaseMountable m)
  1244.             {
  1245.                 foreach (var target in GetMountedPlayers(m))
  1246.                 {
  1247.                     if (target == null) continue;
  1248.  
  1249.                     m.DismountPlayer(target, false);
  1250.  
  1251.                     target.EnsureDismounted();
  1252.                 }
  1253.             }
  1254.  
  1255.             private List<BasePlayer> GetMountedPlayers(BaseMountable m)
  1256.             {
  1257.                 BaseVehicle vehicle = m.HasParent() ? m.VehicleParent() : m as BaseVehicle;
  1258.  
  1259.                 if (vehicle.IsValid())
  1260.                 {
  1261.                     return GetMountedPlayers(vehicle);
  1262.                 }
  1263.  
  1264.                 List<BasePlayer> players = new List<BasePlayer>();
  1265.  
  1266.                 var player = m.GetMounted();
  1267.  
  1268.                 if (player.IsValid() && player.IsHuman())
  1269.                 {
  1270.                     players.Add(player);
  1271.                 }
  1272.  
  1273.                 return players;
  1274.             }
  1275.  
  1276.             private List<BasePlayer> GetMountedPlayers(BaseVehicle vehicle)
  1277.             {
  1278.                 List<BasePlayer> players = new List<BasePlayer>();
  1279.  
  1280.                 if (!vehicle.HasMountPoints())
  1281.                 {
  1282.                     var player = vehicle.GetMounted();
  1283.  
  1284.                     if (player.IsValid() && player.IsHuman())
  1285.                     {
  1286.                         players.Add(player);
  1287.                     }
  1288.  
  1289.                     return players;
  1290.                 }
  1291.  
  1292.                 for (int i = 0; i < vehicle.mountPoints.Count; i++)
  1293.                 {
  1294.                     var mountPoint = vehicle.mountPoints[i];
  1295.  
  1296.                     if (mountPoint.mountable == null)
  1297.                     {
  1298.                         continue;
  1299.                     }
  1300.  
  1301.                     var player = mountPoint.mountable.GetMounted();
  1302.  
  1303.                     if (player.IsValid() && player.IsHuman())
  1304.                     {
  1305.                         players.Add(player);
  1306.                     }
  1307.                 }
  1308.  
  1309.                 return players;
  1310.             }
  1311.  
  1312.             private bool RemoveMountable(BaseMountable m, List<BasePlayer> players)
  1313.             {
  1314.                 if (players.Count == 0)
  1315.                 {
  1316.                     return EjectMountable(m, 10f, players);
  1317.                 }
  1318.  
  1319.                 BaseVehicle vehicle = m.HasParent() ? m.VehicleParent() : m as BaseVehicle;
  1320.  
  1321.                 if (!vehicle.IsKilled())
  1322.                 {
  1323.                     var e = vehicle.transform.eulerAngles; // credits k1lly0u
  1324.  
  1325.                     vehicle.transform.rotation = Quaternion.Euler(e.x, e.y - 180f, e.z);
  1326.  
  1327.                     if (vehicle.rigidBody != null)
  1328.                     {
  1329.                         vehicle.rigidBody.velocity *= -1f;
  1330.                     }
  1331.  
  1332.                     return true;
  1333.                 }
  1334.  
  1335.                 return EjectMountable(m, 2f, players);
  1336.             }
  1337.  
  1338.             private bool IsFlying(BasePlayer player)
  1339.             {
  1340.                 return player?.modelState?.onground == false && TerrainMeta.HeightMap.GetHeight(player.transform.position) < player.transform.position.y - 1f;
  1341.             }
  1342.  
  1343.             private bool EjectMountable(BaseMountable m, float distance, List<BasePlayer> players)
  1344.             {
  1345.                 if (m is BaseVehicle)
  1346.                 {
  1347.                     var vehicle = m as BaseVehicle;
  1348.  
  1349.                     foreach (var mp in vehicle.mountPoints)
  1350.                     {
  1351.                         if (mp.mountable.IsValid() && mp.mountable.GetMounted().IsValid())
  1352.                         {
  1353.                             if (mp.mountable.GetMounted().IsAdmin)
  1354.                             {
  1355.                                 return false;
  1356.                             }
  1357.                         }
  1358.                     }
  1359.                 }
  1360.                 else if (m.GetMounted().IsValid())
  1361.                 {
  1362.                     if (m.GetMounted().IsAdmin)
  1363.                     {
  1364.                         return false;
  1365.                     }
  1366.                 }
  1367.  
  1368.                 var j = TerrainMeta.HeightMap.GetHeight(m.transform.position) - m.transform.position.y;
  1369.  
  1370.                 if (j > 5f)
  1371.                 {
  1372.                     distance += j;
  1373.                 }
  1374.  
  1375.                 var position = ((m.transform.position.XZ3D() - _zonePos.XZ3D()).normalized * (_.zoneRadius + distance)) + _zonePos;
  1376.                 var e = m.transform.eulerAngles;
  1377.  
  1378.                 if (m is MiniCopter || m is CH47Helicopter || players.Exists(player => IsFlying(player)))
  1379.                 {
  1380.                     position.y = Mathf.Max(m.transform.position.y + 5f, GetSpawnHeight(position) + 1f);
  1381.                 }
  1382.                 else
  1383.                 {
  1384.                     position.y = GetSpawnHeight(position) + 1f;
  1385.                 }
  1386.  
  1387.                 m.transform.rotation = Quaternion.Euler(e.x, e.y - 180f, e.z);
  1388.  
  1389.                 Rigidbody rigidbody;
  1390.                 if (m.TryGetComponent(out rigidbody))
  1391.                 {
  1392.                     rigidbody.velocity *= -1f;
  1393.                 }
  1394.  
  1395.                 if (m.mountAnchor != null && m.mountAnchor.transform != null)
  1396.                 {
  1397.                     m.transform.position = m.mountAnchor.transform.position = position;
  1398.                     m.mountAnchor.Rotate(m.transform.eulerAngles);
  1399.                 }
  1400.                 else m.transform.position = position;
  1401.  
  1402.                 m.TransformChanged();
  1403.  
  1404.                 return true;
  1405.             }
  1406.  
  1407.             private int targetLayer = ~(Layers.Mask.Invisible | Layers.Mask.Trigger | Layers.Mask.Prevent_Movement | Layers.Mask.Prevent_Building); // credits ZoneManager
  1408.  
  1409.             private static float GetSpawnHeight(Vector3 target)
  1410.             {
  1411.                 float y = TerrainMeta.HeightMap.GetHeight(target);
  1412.                 float w = TerrainMeta.WaterMap.GetHeight(target);
  1413.                 float p = TerrainMeta.HighestPoint.y + 250f;
  1414.                 RaycastHit hit;
  1415.  
  1416.                 if (Physics.Raycast(new Vector3(target.x, w, target.z), Vector3.up, out hit, p, Layers.Mask.World))
  1417.                 {
  1418.                     y = Mathf.Max(y, hit.point.y);
  1419.  
  1420.                     if (Physics.Raycast(new Vector3(target.x, hit.point.y + 0.5f, target.z), Vector3.up, out hit, p, Layers.Mask.World))
  1421.                     {
  1422.                         y = Mathf.Max(y, hit.point.y);
  1423.                     }
  1424.                 }
  1425.  
  1426.                 return Mathf.Max(y, w);
  1427.             }
  1428.  
  1429.             public void Setup(Vector3 position)
  1430.             {
  1431.                 transform.position = _zonePos = position;
  1432.                 _duelSpawns = _.GetAutoSpawns(this);
  1433.  
  1434.                 if (_.removePlayers)
  1435.                 {
  1436.                     var collider = gameObject.GetComponent<SphereCollider>() ?? gameObject.AddComponent<SphereCollider>();
  1437.                     collider.radius = _.zoneRadius + 1.5f;
  1438.                     collider.isTrigger = true;
  1439.                     collider.center = Vector3.zero;
  1440.                     gameObject.layer = (int)Layer.Trigger;
  1441.                 }
  1442.  
  1443.                 if (_.autoOvens || _.autoFlames || _.autoTurrets)
  1444.                 {
  1445.                     var entities = new List<BaseCombatEntity>();
  1446.                     Vis.Entities(_zonePos, _.zoneRadius, entities);
  1447.  
  1448.                     foreach (var e in entities)
  1449.                     {
  1450.                         _.SetupPower(e);
  1451.                     }
  1452.                 }
  1453.  
  1454.                 CreateSpheres();
  1455.             }
  1456.  
  1457.             private void RemoveSpheres()
  1458.             {
  1459.                 spheres.ForEach(sphere => sphere.SafelyKill());
  1460.             }
  1461.  
  1462.             private void CreateSpheres()
  1463.             {
  1464.                 if (_.sphereAmount <= 0)
  1465.                 {
  1466.                     return;
  1467.                 }
  1468.  
  1469.                 var prefab = StringPool.Get(3211242734);
  1470.  
  1471.                 for (int i = 0; i < _.sphereAmount; i++)
  1472.                 {
  1473.                     var sphere = GameManager.server.CreateEntity(prefab, _zonePos) as SphereEntity;
  1474.  
  1475.                     if (sphere == null)
  1476.                     {
  1477.                         break;
  1478.                     }
  1479.  
  1480.                     sphere.currentRadius = 1f;
  1481.                     sphere.Spawn();
  1482.                     sphere.LerpRadiusTo(_.zoneRadius * 2f, _.zoneRadius * 0.75f);
  1483.                     spheres.Add(sphere);
  1484.                 }
  1485.             }
  1486.  
  1487.             public float Distance(Vector3 position)
  1488.             {
  1489.                 position.y = 0f;
  1490.                 return Vector3.Distance(new Vector3(_zonePos.x, 0f, _zonePos.z), position);
  1491.             }
  1492.  
  1493.             public bool? AddWaiting(BasePlayer player, BasePlayer target)
  1494.             {
  1495.                 if (IsFull)
  1496.                     return false;
  1497.  
  1498.                 double requiredDuelMoney = _.requiredDuelMoney;
  1499.  
  1500.                 if (requiredDuelMoney > 0.0 && _.Economics.CanCall())
  1501.                 {
  1502.                     double playerMoney = Convert.ToDouble(_.Economics.Call("Balance", player.userID));
  1503.                     double targetMoney = Convert.ToDouble(_.Economics.Call("Balance", target.userID));
  1504.  
  1505.                     if (playerMoney < _.requiredDuelMoney || targetMoney < requiredDuelMoney)
  1506.                     {
  1507.                         _.RemoveFromQueue(player.UserIDString);
  1508.                         _.RemoveFromQueue(target.UserIDString);
  1509.                         Message(player, _.msg("MoneyRequired", player.UserIDString, requiredDuelMoney));
  1510.                         Message(target, _.msg("MoneyRequired", target.UserIDString, requiredDuelMoney));
  1511.                         return null;
  1512.                     }
  1513.  
  1514.                     bool playerWithdrawn = Convert.ToBoolean(_.Economics.Call("Withdraw", player.userID, requiredDuelMoney));
  1515.                     bool targetWithdrawn = Convert.ToBoolean(_.Economics.Call("Withdraw", target.userID, requiredDuelMoney));
  1516.  
  1517.                     if (!playerWithdrawn || !targetWithdrawn)
  1518.                     {
  1519.                         _.RemoveFromQueue(player.UserIDString);
  1520.                         _.RemoveFromQueue(target.UserIDString);
  1521.                         return null;
  1522.                     }
  1523.                 }
  1524.  
  1525.                 _waiting.Add(player);
  1526.                 _waiting.Add(target);
  1527.  
  1528.                 return true;
  1529.             }
  1530.  
  1531.             public bool IsWaiting(BasePlayer player)
  1532.             {
  1533.                 return _waiting.Contains(player);
  1534.             }
  1535.  
  1536.             public void AddPlayer(BasePlayer player)
  1537.             {
  1538.                 _waiting.Remove(player);
  1539.                 _players.Add(player);
  1540.             }
  1541.  
  1542.             public void RemovePlayer(string playerId)
  1543.             {
  1544.                 _players.RemoveWhere(player => !player);
  1545.  
  1546.                 foreach (var player in _players)
  1547.                 {
  1548.                     if (player.UserIDString == playerId)
  1549.                     {
  1550.                         _players.Remove(player);
  1551.                         _waiting.Remove(player);
  1552.                         break;
  1553.                     }
  1554.                 }
  1555.             }
  1556.  
  1557.             public bool HasPlayer(string playerId)
  1558.             {
  1559.                 return _players.Exists(player => player.UserIDString == playerId);
  1560.             }
  1561.  
  1562.             public void Kill()
  1563.             {
  1564.                 foreach (var player in _players.ToList())
  1565.                     _.EjectPlayer(player);
  1566.  
  1567.                 foreach (var player in BasePlayer.allPlayerList)
  1568.                 {
  1569.                     if (Distance(player.transform.position) <= _.zoneRadius)
  1570.                     {
  1571.                         _.EndSpectate(player);
  1572.                         _.SendHome(player);
  1573.                     }
  1574.                 }
  1575.  
  1576.                 _.duelingZones.Remove(this);
  1577.                 _players.Clear();
  1578.                 RemoveSpheres();
  1579.                 Destroy(gameObject);
  1580.                 Destroy(this);
  1581.             }
  1582.         }
  1583.  
  1584.         private object OnDangerousOpen(Vector3 treasurePos)
  1585.         {
  1586.             return DuelTerritory(treasurePos) ? (object)false : null;
  1587.         }
  1588.  
  1589.         private object OnPlayerDeathMessage(BasePlayer victim, HitInfo info) // private plugin hook
  1590.         {
  1591.             return DuelTerritory(victim.transform.position) ? (object)false : null;
  1592.         }
  1593.  
  1594.         private void Init()
  1595.         {
  1596.             Instance = this;
  1597.             SubscribeHooks(false); // turn off all hooks immediately
  1598.         }
  1599.  
  1600.         private void OnServerInitialized()
  1601.         {
  1602.             LoadVariables();
  1603.             SetupDefinitions();
  1604.  
  1605.             monuments = UnityEngine.Object.FindObjectsOfType<MonumentInfo>().Select(monument => monument.transform.position).ToList();
  1606.             duelsFile = Interface.Oxide.DataFileSystem.GetFile(Name);
  1607.  
  1608.             try
  1609.             {
  1610.                 duelsData = duelsFile.ReadObject<StoredData>();
  1611.             }
  1612.             catch { }
  1613.  
  1614.             if (duelsData == null)
  1615.                 duelsData = new StoredData();
  1616.            
  1617.             foreach (var bet in duelingBets.ToList()) // 0.1.5 fix - check itemList after server has initialized
  1618.             {
  1619.                 if (ItemManager.itemList.Find(def => def.itemid == bet.itemid) == null)
  1620.                 {
  1621.                     Puts("Bet itemid {0} is invalid.", bet.itemid);
  1622.                     duelingBets.Remove(bet);
  1623.                 }
  1624.             }
  1625.  
  1626.             if (useAnnouncement && announceTime > 0f)
  1627.                 announceTimer = timer.Repeat(announceTime, 0, () => DuelAnnouncement(false));
  1628.  
  1629.             eventTimer = timer.Once(0.5f, CheckDuelistMortality); // kill players who haven't finished their duel in time. remove temporary immunity for duelers when it expires
  1630.  
  1631.             if (!resetDuelists && IsEmptyMap())
  1632.             {
  1633.                 if (duelsData.VictoriesSeed.Count > 0 && duelsData.VictoriesSeed.Values.Exists(x => x > 0))
  1634.                 {
  1635.                     resetDuelists = true;
  1636.                 }
  1637.             }
  1638.  
  1639.             if (resetDuelists) // map wipe detected - award duelers and reset the data for the seed only
  1640.             {
  1641.                 ResetDuelists();
  1642.                 resetDuelists = false;
  1643.             }
  1644.  
  1645.             if (BasePlayer.activePlayerList.Count == 0)
  1646.             {
  1647.                 RemoveZeroStats();
  1648.                 ResetTemporaryData();
  1649.             }
  1650.  
  1651.             if (ZoneManager.CanCall())
  1652.                 SetupZoneManager();
  1653.  
  1654.             SetupZones();
  1655.  
  1656.             if (duelingZones.Count > 0 && autoEnable)
  1657.                 duelsData.DuelsEnabled = true;
  1658.  
  1659.             UpdateStability();
  1660.             CheckZoneHooks(true);
  1661.  
  1662.             if (guiAutoEnable)
  1663.             {
  1664.                 Subscribe(nameof(OnPlayerConnected));
  1665.  
  1666.                 foreach (var player in BasePlayer.activePlayerList)
  1667.                     OnPlayerConnected(player);
  1668.             }
  1669.  
  1670.             if (useWorkshopSkins)
  1671.                 webrequest.Enqueue("http://s3.amazonaws.com/s3.playrust.com/icons/inventory/rust/schema.json", null, GetWorkshopIDs, this, Core.Libraries.RequestMethod.GET);
  1672.         }
  1673.  
  1674.         private void OnServerSave()
  1675.         {
  1676.             timer.Once(5f, SaveData);
  1677.         }
  1678.  
  1679.         private void OnNewSave(string filename)
  1680.         {
  1681.             resetDuelists = true;
  1682.         }
  1683.  
  1684.         private bool IsEmptyMap()
  1685.         {
  1686.             foreach (var b in BuildingManager.server.buildingDictionary)
  1687.             {
  1688.                 if (b.Value.HasDecayEntities() && b.Value.decayEntities.Exists(de => de != null && de.OwnerID.IsSteamId()))
  1689.                 {
  1690.                     return false;
  1691.                 }
  1692.             }
  1693.             return true;
  1694.         }
  1695.  
  1696.         public void SaveData()
  1697.         {
  1698.             if (duelsFile != null && duelsData != null)
  1699.             {
  1700.                 duelsFile.WriteObject(duelsData);
  1701.             }
  1702.         }
  1703.  
  1704.         private void DestroyAll()
  1705.         {
  1706.             foreach (var zone in duelingZones.ToList())
  1707.             {
  1708.                 UnityEngine.Object.Destroy(zone.gameObject);
  1709.             }
  1710.         }
  1711.  
  1712.         private void Unload()
  1713.         {
  1714.             var objects = UnityEngine.Object.FindObjectsOfType(typeof(Tracker));
  1715.  
  1716.             if (objects != null)
  1717.                 foreach (var gameObj in objects)
  1718.                     UnityEngine.Object.Destroy(gameObj);
  1719.  
  1720.             DestroyAll();
  1721.             announceTimer?.Destroy();
  1722.             eventTimer?.Destroy();
  1723.  
  1724.             foreach (var match in tdmMatches.ToList())
  1725.                 match.End();
  1726.  
  1727.             foreach (var zone in duelingZones.ToList())
  1728.             {
  1729.                 RemoveEntities(zone);
  1730.                 zone.Kill();
  1731.             }
  1732.  
  1733.             tdmMatches.Clear();
  1734.             duelingZones.Clear();
  1735.             ResetTemporaryData();
  1736.             DestroyAllUI();
  1737.             Instance = null;
  1738.         }
  1739.  
  1740.         private void OnPlayerDisconnected(BasePlayer player, string reason)
  1741.         {
  1742.             var match = GetMatch(player);
  1743.  
  1744.             if (match != null && !match.IsStarted && match.EitherEmpty)
  1745.                 match.End();
  1746.  
  1747.             if (dataDuelists.ContainsKey(player.UserIDString))
  1748.             {
  1749.                 string uid = player.UserIDString;
  1750.  
  1751.                 if (!dcsBlock.Contains(uid))
  1752.                 {
  1753.                     dcsBlock.Add(uid);
  1754.                     timer.Once(60f, () => dcsBlock.Remove(uid));
  1755.                 }
  1756.  
  1757.                 duelsData.AutoReady.Remove(player.UserIDString);
  1758.                 OnDuelistLost(player, true);
  1759.                 RemoveDuelist(player.UserIDString);
  1760.                 ResetDuelist(player.UserIDString, false);
  1761.             }
  1762.             else if (match != null && match.IsStarted && !match.IsOver)
  1763.             {
  1764.                 string uid = player.UserIDString;
  1765.  
  1766.                 if (!dcsBlock.Contains(uid))
  1767.                 {
  1768.                     dcsBlock.Add(uid);
  1769.                     timer.Once(60f, () => dcsBlock.Remove(uid));
  1770.                 }
  1771.  
  1772.                 duelsData.AutoReady.Remove(player.UserIDString);
  1773.                 player.inventory.Strip();
  1774.                 DefeatMessage(player, match);
  1775.                 match.CanRematch = false;
  1776.                 match.RemoveMatchPlayer(player);
  1777.             }
  1778.             else if (IsSpectator(player))
  1779.             {
  1780.                 EndSpectate(player);
  1781.                 SendHome(player);
  1782.             }
  1783.  
  1784.             if (dataDuelists.Count == 0 && tdmMatches.Count == 0 && spectators.Count == 0)
  1785.                 Unsubscribe(nameof(OnPlayerDisconnected)); // nothing else to do right now, unsubscribe the hook
  1786.         }
  1787.  
  1788.         private void OnPlayerConnected(BasePlayer player)
  1789.         {
  1790.             if (!player.IsValid())
  1791.                 return;
  1792.  
  1793.             if (!player.CanInteract())
  1794.             {
  1795.                 timer.Once(1f, () => OnPlayerConnected(player));
  1796.                 return;
  1797.             }
  1798.  
  1799.             createUI.Remove(player.UserIDString);
  1800.             cmdDUI(player, szUIChatCommand, new string[0]);
  1801.         }
  1802.  
  1803.         private List<ulong> executes = new List<ulong>();
  1804.  
  1805.         private void OnPlayerSleepEnded(BasePlayer player) // setup the player
  1806.         {
  1807.             if (IsDueling(player))
  1808.             {
  1809.                 if (IsExploiting(player, true))
  1810.                 {
  1811.                     return;
  1812.                 }
  1813.  
  1814.                 foreach (var zone in duelingZones)
  1815.                 {
  1816.                     if (zone.IsWaiting(player))
  1817.                     {
  1818.                         if (deathTime > 0)
  1819.                         {
  1820.                             if (!executes.Contains(player.userID))
  1821.                             {
  1822.                                 Message(player, msg("ExecutionTime", player.UserIDString, deathTime));
  1823.                                 executes.Add(player.userID);
  1824.                             }
  1825.  
  1826.                             dataDeath[player.UserIDString] = TimeStamp() + deathTime * 60;
  1827.                         }
  1828.  
  1829.                         EndSpectate(player);
  1830.                         GivePlayerKit(player);
  1831.                         Track(player, true);
  1832.                         Metabolize(player, true);
  1833.  
  1834.                         if (DestroyUI(player) && !createUI.Contains(player.UserIDString))
  1835.                             createUI.Add(player.UserIDString);
  1836.  
  1837.                         CheckAutoReady(player);
  1838.                         zone.AddPlayer(player);
  1839.                         Interface.Oxide.CallHook("EnableBypass", player.userID);
  1840.                     }
  1841.                 }
  1842.  
  1843.                 return;
  1844.             }
  1845.             else if (InDeathmatch(player))
  1846.             {
  1847.                 if (IsExploiting(player, false))
  1848.                 {
  1849.                     return;
  1850.                 }
  1851.  
  1852.                 var match = GetMatch(player);
  1853.  
  1854.                 if (deathTime > 0)
  1855.                 {
  1856.                     Message(player, msg("ExecutionTime", player.UserIDString, deathTime));
  1857.                     dataDeath[player.UserIDString] = TimeStamp() + deathTime * 60;
  1858.                 }
  1859.  
  1860.                 if (DestroyUI(player) && !createUI.Contains(player.UserIDString))
  1861.                 {
  1862.                     createUI.Add(player.UserIDString);
  1863.                 }
  1864.  
  1865.                 EndSpectate(player);
  1866.                 GivePlayerKit(player);
  1867.                 Track(player, true);
  1868.                 Metabolize(player, true);
  1869.                 match.GiveShirt(player);
  1870.                 CheckAutoReady(player);
  1871.                 Interface.Oxide.CallHook("EnableBypass", player.userID);
  1872.                 return;
  1873.             }
  1874.             else SetPlayerTime(player, false);
  1875.  
  1876.             if (announcements.ContainsKey(player.UserIDString))
  1877.             {
  1878.                 CreateAnnouncementUI(player, announcements[player.UserIDString]);
  1879.                 announcements.Remove(player.UserIDString);
  1880.             }
  1881.  
  1882.             if (dataDuelists.Count == 0 && tdmMatches.Count == 0 && announcements.Count == 0)
  1883.             {
  1884.                 // nothing else to do right now, unsubscribe the hook
  1885.                 Unsubscribe(nameof(OnPlayerSleepEnded));
  1886.             }
  1887.         }
  1888.  
  1889.         private bool IsExploiting(BasePlayer player, bool duel)
  1890.         {
  1891.             if (Convert.ToBoolean(AimTrain?.Call("IsAimTraining", player.userID)))
  1892.             {
  1893.                 AimTrain?.Call("LeaveAimTrain", player);
  1894.                 duelsData.AutoReady.Remove(player.UserIDString);
  1895.                 DestroyUI(player);
  1896.  
  1897.                 if (duel)
  1898.                 {
  1899.                     OnDuelistLost(player, true);
  1900.                     RemoveDuelist(player.UserIDString);
  1901.                     ResetDuelist(player.UserIDString, false);
  1902.                     EndSpectate(player);
  1903.                     SendHome(player);
  1904.                 }
  1905.                 else
  1906.                 {
  1907.                     var match = GetMatch(player);
  1908.  
  1909.                     if (match != null && !match.IsStarted && match.EitherEmpty)
  1910.                     {
  1911.                         match.End();
  1912.                     }
  1913.  
  1914.                     if (match != null && match.IsStarted && !match.IsOver)
  1915.                     {
  1916.                         DefeatMessage(player, match);
  1917.                         match.CanRematch = false;
  1918.                         match.RemoveMatchPlayer(player);
  1919.                     }
  1920.                 }
  1921.  
  1922.                 return true;
  1923.             }
  1924.  
  1925.             return false;
  1926.         }
  1927.  
  1928.         private void OnPlayerRespawned(BasePlayer player)
  1929.         {
  1930.             if (DuelTerritory(player.transform.position) && !InEvent(player) && !spectators.Contains(player.UserIDString))
  1931.             {
  1932.                 var spawnPoint = ServerMgr.FindSpawnPoint();
  1933.                 int retries = 25;
  1934.  
  1935.                 while (DuelTerritory(spawnPoint.pos) && --retries > 0)
  1936.                     spawnPoint = ServerMgr.FindSpawnPoint();
  1937.  
  1938.                 Teleport(player, spawnPoint.pos);
  1939.             }
  1940.         }
  1941.  
  1942.         private void OnEntityKill(SimpleBuildingBlock e)
  1943.         {
  1944.             if (respawnWalls)
  1945.             {
  1946.                 if (e?.transform != null && e.ShortPrefabName.Contains("wall.external.high"))
  1947.                 {
  1948.                     RecreateZoneWall(e.PrefabName, e.transform.position, e.transform.rotation, e.OwnerID);
  1949.                 }
  1950.             }
  1951.         }
  1952.  
  1953.         public void SetupPower(BaseEntity e)
  1954.         {
  1955.             if (autoOvens && e is BaseOven)
  1956.             {
  1957.                 e.SetFlag(BaseEntity.Flags.On, true);
  1958.             }
  1959.             else if (autoFlames && e is FlameTurret)
  1960.             {
  1961.                 var ft = e as FlameTurret;
  1962.  
  1963.                 if (!ft.HasFuel())
  1964.                 {
  1965.                     ft.inventory.AddItem(ItemManager.FindItemDefinition("lowgradefuel"), 5);
  1966.                 }
  1967.  
  1968.                 ft.fuelPerSec = 0f;
  1969.             }
  1970.             else if (autoTurrets && e is AutoTurret)
  1971.             {
  1972.                 var at = e as AutoTurret;
  1973.  
  1974.                 at.InitiateStartup();
  1975.                 at.SetPeacekeepermode(false);
  1976.             }
  1977.         }
  1978.  
  1979.         public void Track(BasePlayer player, bool enable)
  1980.         {
  1981.             Tracker tracker;
  1982.             if (enable && !player.TryGetComponent(out tracker))
  1983.             {
  1984.                 tracker = player.gameObject.AddComponent<Tracker>();
  1985.                 tracker.Init(this);
  1986.             }
  1987.             else if (!enable && player.TryGetComponent(out tracker))
  1988.             {
  1989.                 UnityEngine.Object.Destroy(tracker);
  1990.             }
  1991.         }
  1992.  
  1993.         public void RecreateZoneWall(string prefab, Vector3 pos, Quaternion rot, ulong ownerId)
  1994.         {
  1995.             if (DuelTerritory(pos) && duelsData.DuelZones.Exists(entry => GetOwnerId(entry.Key) == ownerId))
  1996.                 CreateZoneWall(prefab, pos, rot, ownerId);
  1997.         }
  1998.  
  1999.         public BaseEntity CreateZoneWall(string prefab, Vector3 pos, Quaternion rot, ulong ownerId)
  2000.         {
  2001.             var e = GameManager.server.CreateEntity(prefab, pos, rot, false);
  2002.  
  2003.             if (e != null)
  2004.             {
  2005.                 e.OwnerID = ownerId;
  2006.                 e.Spawn();
  2007.                 e.gameObject.SetActive(true);
  2008.                 return e;
  2009.             }
  2010.  
  2011.             return null;
  2012.         }
  2013.  
  2014.         private void OnEntityDeath(BaseEntity entity, HitInfo hitInfo) // 0.1.16 fix for player suiciding
  2015.         {
  2016.             if (entity == null)
  2017.                 return;
  2018.  
  2019.             if (respawnWalls && entity.transform != null && entity.ShortPrefabName.Contains("wall.external.high"))
  2020.             {
  2021.                 RecreateZoneWall(entity.PrefabName, entity.transform.position, entity.transform.rotation, entity.OwnerID);
  2022.                 return;
  2023.             }
  2024.  
  2025.             var victim = entity as BasePlayer;
  2026.  
  2027.             if (victim == null)
  2028.                 return;
  2029.  
  2030.             if (spectators.Contains(victim.UserIDString))
  2031.                 EndSpectate(victim);
  2032.  
  2033.             if (IsDueling(victim))
  2034.             {
  2035.                 victim.inventory.Strip();
  2036.                 OnDuelistLost(victim, true);
  2037.             }
  2038.             else if (InDeathmatch(victim))
  2039.             {
  2040.                 victim.inventory.Strip();
  2041.                 var match = GetMatch(victim);
  2042.  
  2043.                 DefeatMessage(victim, match);
  2044.                 match.RemoveMatchPlayer(victim);
  2045.             }
  2046.         }
  2047.  
  2048.         private void OnPlayerHealthChange(BasePlayer player, float oldValue, float newValue)
  2049.         {
  2050.             if (newValue < 6f && player.IsHuman())
  2051.             {
  2052.                 if (IsDueling(player))
  2053.                 {
  2054.                     player.health = 6f;
  2055.                     player.inventory.Strip();
  2056.                     OnDuelistLost(player, false);
  2057.                 }
  2058.                 else if (InDeathmatch(player))
  2059.                 {
  2060.                     player.health = 6f;
  2061.                     player.inventory.Strip();
  2062.  
  2063.                     var match = GetMatch(player);
  2064.                     DefeatMessage(player, match);
  2065.                     match.RemoveMatchPlayer(player);
  2066.                 }
  2067.             }
  2068.         }
  2069.  
  2070.         private void DefeatMessage(BasePlayer victim, GoodVersusEvilMatch match)
  2071.         {
  2072.             if (tdmAttackers.ContainsKey(victim.UserIDString))
  2073.             {
  2074.                 var info = tdmAttackers[victim.UserIDString];
  2075.  
  2076.                 if (tdmServerDeaths || duelsData.ChatEx.Count > 0)
  2077.                 {
  2078.                     foreach (var target in BasePlayer.activePlayerList.Where(p => p?.displayName != null))
  2079.                     {
  2080.                         if (duelsData.Chat.Contains(target.UserIDString) && target != victim)
  2081.                             continue;
  2082.  
  2083.                         Message(target, msg("MatchPlayerDefeated", target.UserIDString, victim.displayName, info.AttackerName, info.Weapon, info.BoneName, info.Distance));
  2084.                     }
  2085.                 }
  2086.                 else if (tdmMatchDeaths)
  2087.                     match.MessageAll("MatchPlayerDefeated", victim.displayName, info.AttackerName, info.Weapon, info.BoneName, info.Distance);
  2088.  
  2089.                 if (guiAnnounceUITime > 0f)
  2090.                 {
  2091.                     if (sendDefeatedHome)
  2092.                         announcements[victim.UserIDString] = msg("MatchPlayerDefeated", victim.UserIDString, victim.displayName, info.AttackerName, info.Weapon, info.BoneName, info.Distance);
  2093.                     else
  2094.                         CreateAnnouncementUI(victim, msg("MatchPlayerDefeated", victim.UserIDString, victim.displayName, info.AttackerName, info.Weapon, info.BoneName, info.Distance));
  2095.                 }
  2096.  
  2097.                 tdmAttackers.Remove(victim.UserIDString);
  2098.                 UpdateMatchStats(victim.UserIDString, false, false, true, false);
  2099.                 UpdateMatchStats(info.AttackerId, false, false, false, true);
  2100.             }
  2101.         }
  2102.  
  2103.         private void OnDuelistLost(BasePlayer victim, bool sendHome)
  2104.         {
  2105.             RemoveEntities(victim.userID);
  2106.  
  2107.             if (!dataDuelists.ContainsKey(victim.UserIDString))
  2108.             {
  2109.                 NextTick(() => SendHome(victim));
  2110.                 return;
  2111.             }
  2112.  
  2113.             string attackerId = dataDuelists[victim.UserIDString];
  2114.             var attacker = BasePlayer.Find(attackerId);
  2115.             string attackerName = attacker?.displayName ?? GetDisplayName(attackerId); // get the attackers name. null check for self inflicted
  2116.  
  2117.             dataDeath.Remove(victim.UserIDString); // remove them from automatic deaths
  2118.             dataDeath.Remove(attackerId);
  2119.             dataDuelists.Remove(victim.UserIDString); // unset their status as duelers
  2120.             dataDuelists.Remove(attackerId);
  2121.             victim.inventory.Strip();
  2122.             Metabolize(victim, false);
  2123.             Track(victim, false);
  2124.  
  2125.             if (!duelsData.LossesSeed.ContainsKey(victim.UserIDString)) duelsData.LossesSeed.Add(victim.UserIDString, 1);
  2126.             else duelsData.LossesSeed[victim.UserIDString]++;
  2127.             if (!duelsData.Losses.ContainsKey(victim.UserIDString)) duelsData.Losses.Add(victim.UserIDString, 1);
  2128.             else duelsData.Losses[victim.UserIDString]++;
  2129.             if (!duelsData.VictoriesSeed.ContainsKey(attackerId)) duelsData.VictoriesSeed.Add(attackerId, 1);
  2130.             else duelsData.VictoriesSeed[attackerId]++;
  2131.             if (!duelsData.Victories.ContainsKey(attackerId)) duelsData.Victories.Add(attackerId, 1);
  2132.             else duelsData.Victories[attackerId]++;
  2133.             duelsData.TotalDuels++;
  2134.  
  2135.             int victimLossesSeed = duelsData.LossesSeed[victim.UserIDString];
  2136.             int victimVictoriesSeed = duelsData.VictoriesSeed.ContainsKey(victim.UserIDString) ? duelsData.VictoriesSeed[victim.UserIDString] : 0;
  2137.             int attackerLossesSeed = duelsData.LossesSeed.ContainsKey(attackerId) ? duelsData.LossesSeed[attackerId] : 0;
  2138.             int attackerVictoriesSeed = duelsData.VictoriesSeed[attackerId];
  2139.             var bet = duelsData.Bets.ContainsKey(attackerId) && duelsData.Bets.ContainsKey(victim.UserIDString) && duelsData.Bets[attackerId].Equals(duelsData.Bets[victim.UserIDString]) && !IsAllied(victim, attacker) ? duelsData.Bets[attackerId] : null; // victim bet his attacker and lost, use later to add a claim for the attacker
  2140.  
  2141.             Puts(RemoveFormatting(msg("DuelDeathMessage", null, attackerName, attackerVictoriesSeed, attackerLossesSeed, victim.displayName, victimVictoriesSeed, victimLossesSeed, Math.Round(attacker?.health ?? 0f, 2), bet != null ? msg("BetWon", null, bet.trigger, bet.amount) : ""))); // send message to console
  2142.             Interface.CallHook("OnDuelistDefeated", attacker, victim);
  2143.  
  2144.             if (guiAnnounceUITime > 0f)
  2145.             {
  2146.                 if (sendDefeatedHome)
  2147.                 {
  2148.                     announcements[victim.UserIDString] = msg("DuelDeathMessage", victim.UserIDString, attackerName, attackerVictoriesSeed, attackerLossesSeed, victim.displayName, victimVictoriesSeed, victimLossesSeed, Math.Round(attacker?.health ?? 0f, 2), bet != null ? msg("BetWon", null, bet.trigger, bet.amount) : "");
  2149.                     announcements[attackerId] = msg("DuelDeathMessage", attackerId, attackerName, attackerVictoriesSeed, attackerLossesSeed, victim.displayName, victimVictoriesSeed, victimLossesSeed, Math.Round(attacker?.health ?? 0f, 2), bet != null ? msg("BetWon", null, bet.trigger, bet.amount) : "");
  2150.                 }
  2151.                 else
  2152.                 {
  2153.                     CreateAnnouncementUI(victim, msg("DuelDeathMessage", victim.UserIDString, attackerName, attackerVictoriesSeed, attackerLossesSeed, victim.displayName, victimVictoriesSeed, victimLossesSeed, Math.Round(attacker?.health ?? 0f, 2), bet != null ? msg("BetWon", null, bet.trigger, bet.amount) : ""));
  2154.                     CreateAnnouncementUI(attacker, msg("DuelDeathMessage", attackerId, attackerName, attackerVictoriesSeed, attackerLossesSeed, victim.displayName, victimVictoriesSeed, victimLossesSeed, Math.Round(attacker?.health ?? 0f, 2), bet != null ? msg("BetWon", null, bet.trigger, bet.amount) : ""));
  2155.                 }
  2156.             }
  2157.  
  2158.             foreach (var target in BasePlayer.activePlayerList.Where(p => p?.displayName != null))
  2159.             {
  2160.                 if (duelsData.Chat.Contains(target.UserIDString) && target != victim && target != attacker)
  2161.                     continue;
  2162.  
  2163.                 if (!broadcastDefeat && !duelsData.ChatEx.Contains(target.UserIDString) && target != victim && target != attacker)
  2164.                     continue;
  2165.  
  2166.                 string betWon = bet != null ? msg("BetWon", target.UserIDString, bet.trigger, bet.amount) : "";
  2167.                 Message(target, msg("DuelDeathMessage", target.UserIDString, attackerName, attackerVictoriesSeed, attackerLossesSeed, victim.displayName, victimVictoriesSeed, victimLossesSeed, Math.Round(attacker?.health ?? 0f, 2), betWon));
  2168.             }
  2169.  
  2170.             if (bet != null && attacker != null) // award the bet to the attacker
  2171.             {
  2172.                 var claimBet = new BetInfo
  2173.                 {
  2174.                     itemid = bet.itemid,
  2175.                     amount = bet.amount * 2,
  2176.                     trigger = bet.trigger
  2177.                 };
  2178.  
  2179.                 if (!duelsData.ClaimBets.ContainsKey(attackerId))
  2180.                     duelsData.ClaimBets.Add(attackerId, new List<BetInfo>());
  2181.  
  2182.                 duelsData.ClaimBets[attackerId].Add(claimBet);
  2183.                 duelsData.Bets.Remove(attackerId);
  2184.                 duelsData.Bets.Remove(victim.UserIDString);
  2185.                 Puts(msg("ConsoleBetWon", null, attacker.displayName, attacker.UserIDString, victim.displayName, victim.UserIDString));
  2186.                 Message(attacker, msg("NotifyBetWon", attacker.UserIDString, szDuelChatCommand));
  2187.             }
  2188.  
  2189.             ulong attackeruId = Convert.ToUInt64(attackerId);
  2190.  
  2191.             RemoveDuelist(attackerId);
  2192.             RemoveEntities(attackeruId);
  2193.             AwardPlayer(attackeruId, economicsMoney, serverRewardsPoints);
  2194.  
  2195.             Interface.Oxide.CallHook("DisableBypass", victim.userID);
  2196.             Interface.Oxide.CallHook("DisableBypass", attackeruId);
  2197.  
  2198.             if (attacker != null)
  2199.             {
  2200.                 attacker.inventory.Strip();
  2201.                 Metabolize(attacker, false);
  2202.                 Track(attacker, false);
  2203.             }
  2204.  
  2205.             var zone = RemoveDuelist(victim.UserIDString);
  2206.  
  2207.             if (zoneCounter > 0 && zone != null) // if new zones are set to spawn every X duels then increment by 1
  2208.             {
  2209.                 if (++zone.Kills >= zoneCounter && zone.TotalPlayers == 0)
  2210.                 {
  2211.                     RemoveDuelZone(zone);
  2212.                     SetupDuelZone(null, GetZoneName()); // x amount of duels completed. time to relocate and start all over! changing the dueling zones location keeps things mixed up and entertaining for everyone. especially when there's issues with terrain
  2213.                     SaveData();
  2214.                 }
  2215.             }
  2216.  
  2217.             if (dataDuelists.Count == 0 && tdmMatches.Count == 0)
  2218.                 Unsubscribe(nameof(OnPlayerHealthChange));
  2219.  
  2220.             if (sendHome || dcsBlock.Contains(victim.UserIDString))
  2221.             {
  2222.                 NextTick(() =>
  2223.                 {
  2224.                     SendHome(attacker);
  2225.                     SendHome(victim);
  2226.                 });
  2227.  
  2228.                 if (dcsBlock.Contains(victim.UserIDString))
  2229.                     return;
  2230.             }
  2231.  
  2232.             if (attacker != null)
  2233.             {
  2234.                 if (victim.IsConnected && attacker.IsConnected)
  2235.                 {
  2236.                     var rematch = new Rematch(this);
  2237.                     rematches.Add(rematch);
  2238.                     rematch.Duelists.Add(attacker);
  2239.                     rematch.Duelists.Add(victim);
  2240.                     rematch.Notify();
  2241.                 }
  2242.  
  2243.                 if (!InEvent(attacker) && !InEvent(victim) && !sendHome)
  2244.                 {
  2245.                     StartSpectate(attacker);
  2246.                     StartSpectate(victim);
  2247.                 }
  2248.             }
  2249.         }
  2250.  
  2251.         public string GetZoneName()
  2252.         {
  2253.             return (duelsData.DuelZones.Count + 1).ToString();
  2254.         }
  2255.  
  2256.         public void SendDuelistsHome()
  2257.         {
  2258.             foreach (var entry in dataDuelists.ToList())
  2259.             {
  2260.                 if (duelsData.Homes.ContainsKey(entry.Key))
  2261.                 {
  2262.                     var target = BasePlayer.Find(entry.Key);
  2263.  
  2264.                     if (target != null && DuelTerritory(target.transform.position))
  2265.                     {
  2266.                         target.inventory.Strip();
  2267.                         SendHome(target);
  2268.                     }
  2269.                 }
  2270.  
  2271.                 ResetDuelist(entry.Key);
  2272.             }
  2273.         }
  2274.  
  2275.         public void SendSpectatorsHome()
  2276.         {
  2277.             foreach (var player in BasePlayer.activePlayerList)
  2278.             {
  2279.                 if (IsSpectator(player))
  2280.                 {
  2281.                     EndSpectate(player);
  2282.                 }
  2283.             }
  2284.         }
  2285.  
  2286.         private void StartSpectate(BasePlayer player)
  2287.         {
  2288.             if (!player || !player.IsConnected)
  2289.                 return;
  2290.  
  2291.             if (GetDuelZone(player.transform.position) == null)
  2292.             {
  2293.                 SendHome(player);
  2294.                 return;
  2295.             }
  2296.  
  2297.             if (!player.CanInteract())
  2298.             {
  2299.                 if (player.IsDead())
  2300.                     player.RespawnAt(player.transform.position, default(Quaternion));
  2301.  
  2302.                 timer.Once(1f, () => StartSpectate(player));
  2303.                 return;
  2304.             }
  2305.  
  2306.             spectators.Add(player.UserIDString);
  2307.             Message(player, msg("BeginSpectating", player.UserIDString));
  2308.             player.inventory.Strip();
  2309.             player.health = 100f;
  2310.             player.metabolism.bleeding.value = 0f;
  2311.             player.StopWounded();
  2312.             CreateDefeatUI(player);
  2313.         }
  2314.  
  2315.         private void EndSpectate(BasePlayer player)
  2316.         {
  2317.             CuiHelper.DestroyUi(player, "DuelistUI_Defeat");
  2318.  
  2319.             if (spectators.Contains(player.UserIDString))
  2320.             {
  2321.                 if (playerHealth > 0f && player.IsAlive())
  2322.                     player.health = playerHealth;
  2323.  
  2324.                 spectators.Remove(player.UserIDString);
  2325.                 player.SendNetworkUpdate();
  2326.                 Message(player, msg("EndSpectating", player.UserIDString));
  2327.             }
  2328.         }
  2329.  
  2330.         public void HealDamage(BaseCombatEntity entity)
  2331.         {
  2332.             timer.Once(1f, () =>
  2333.             {
  2334.                 if (!entity.IsKilled() && entity.health < entity.MaxHealth())
  2335.                 {
  2336.                     entity.health = entity.MaxHealth();
  2337.                     entity.SendNetworkUpdate();
  2338.                 }
  2339.             });
  2340.         }
  2341.  
  2342.         public void CancelDamage(HitInfo hitInfo)
  2343.         {
  2344.             if (hitInfo != null)
  2345.             {
  2346.                 hitInfo.damageTypes = new DamageTypeList();
  2347.                 hitInfo.DidHit = false;
  2348.                 hitInfo.HitEntity = null;
  2349.                 hitInfo.Initiator = null;
  2350.                 hitInfo.DoHitEffects = false;
  2351.                 hitInfo.HitMaterial = 0;
  2352.             }
  2353.         }
  2354.  
  2355.         private HashSet<string> _deployables = new HashSet<string>();
  2356.  
  2357.         private bool IsProtectedEntity(BaseEntity entity)
  2358.         {
  2359.             if (_deployables.Count == 0)
  2360.             {
  2361.                 foreach (var def in ItemManager.GetItemDefinitions())
  2362.                 {
  2363.                     var imd = def.GetComponent<ItemModDeployable>();
  2364.                     if (imd == null) continue;
  2365.                     _deployables.Add(imd.entityPrefab.resourcePath);
  2366.                 }
  2367.             }
  2368.             return entity.PrefabName.Contains("building") || _deployables.Contains(entity.PrefabName);
  2369.         }
  2370.  
  2371.         private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
  2372.         {
  2373.             if (entity.IsKilled() || entity.net == null || entity is BaseNpc)
  2374.                 return null;
  2375.  
  2376.             if (DuelTerritory(entity.transform.position, 1f) && IsProtectedEntity(entity))
  2377.             {
  2378.                 CancelDamage(hitInfo);
  2379.                 HealDamage(entity);
  2380.                 return true;
  2381.             }
  2382.            
  2383.             if (hitInfo == null || hitInfo.Initiator.IsKilled() || !hitInfo.hasDamage)
  2384.                 return null;
  2385.  
  2386.             var victim = entity as BasePlayer;
  2387.             var attacker = hitInfo.Initiator as BasePlayer;
  2388.             var pointStart = hitInfo.Initiator?.transform?.position ?? hitInfo.PointStart; // 0.1.6 border fix
  2389.             var pointEnd = entity.transform.position;
  2390.             bool adt = DuelTerritory(pointStart);
  2391.             bool vdt = DuelTerritory(pointEnd);
  2392.  
  2393.             if (adt && entity is BaseHelicopter)
  2394.                 return true;
  2395.  
  2396.             if (vdt && victim?.transform != null && hitInfo.Initiator != null && hitInfo.Initiator.ShortPrefabName.Contains("wall.external.high")) // 1.0.2 - exploit fix
  2397.                 return true;
  2398.  
  2399.             if (victim != null && victim.transform != null && attacker != null && victim == attacker) // allow player to suicide and self inflict
  2400.             {
  2401.                 if (hitInfo.damageTypes.Has(DamageType.Suicide) && InEvent(victim))
  2402.                 {
  2403.                     string uid = victim.UserIDString;
  2404.  
  2405.                     if (!dcsBlock.Contains(uid))
  2406.                     {
  2407.                         dcsBlock.Add(uid);
  2408.                         timer.Once(60f, () => dcsBlock.Remove(uid));
  2409.                     }
  2410.                 }
  2411.  
  2412.                 return null;
  2413.             }
  2414.  
  2415.             if (victim != null && hitInfo.damageTypes.GetMajorityDamageType() == DamageType.Fall && !dataImmunity.ContainsKey(victim.UserIDString))
  2416.                 return null;
  2417.  
  2418.             if (attacker?.transform != null && spectators.Contains(attacker.UserIDString)) // 0.1.27: someone will find a way to abuse spectate mode so we'll prevent that now
  2419.             {
  2420.                 if (!adt)
  2421.                 {
  2422.                     EndSpectate(attacker);
  2423.                     SendHome(attacker);
  2424.                 }
  2425.  
  2426.                 CancelDamage(hitInfo);
  2427.                 return true;
  2428.             }
  2429.  
  2430.             if ((adt || vdt) && !hitInfo.Initiator.IsKilled() && hitInfo.Initiator.IsNpc) // 1.2.0
  2431.             {
  2432.                 if (hitInfo.Initiator is BaseNpc)
  2433.                 {
  2434.                     var npc = hitInfo.Initiator as BaseNpc;
  2435.  
  2436.                     if (npc != null)
  2437.                     {
  2438.                         if (putToSleep)
  2439.                         {
  2440.                             npc.SetAiFlag(BaseNpc.AiFlags.Sleeping, true);
  2441.                             npc.CurrentBehaviour = BaseNpc.Behaviour.Sleep;
  2442.                         }
  2443.                         else if (killNpc)
  2444.                         {
  2445.                             npc.Kill();
  2446.                         }
  2447.  
  2448.                         return true;
  2449.                     }
  2450.                 }
  2451.                 else if (hitInfo.Initiator is BasePlayer)
  2452.                 {
  2453.                     var npc = hitInfo.Initiator as BasePlayer;
  2454.  
  2455.                     if (npc != null)
  2456.                     {
  2457.                         if (npc is global::HumanNPC)
  2458.                         {
  2459.                             (npc as global::HumanNPC).LootSpawnSlots = new LootContainer.LootSpawnSlot[0];
  2460.                         }
  2461.  
  2462.                         npc.Kill();
  2463.                         CancelDamage(hitInfo);
  2464.                         return true;
  2465.                     }
  2466.                 }
  2467.             }
  2468.  
  2469.             if (dataDuelists.Count > 0)
  2470.             {
  2471.                 if (attacker?.transform != null && IsDueling(attacker) && victim != null && dataDuelists[attacker.UserIDString] != victim.UserIDString) // 0.1.8 check attacker then victim
  2472.                     return true; // prevent attacker from doing damage to others
  2473.  
  2474.                 if (victim?.transform != null && IsDueling(victim)) // 1.2.0 NRE get_transform
  2475.                 {
  2476.                     if (victim.health == 6f)
  2477.                         return true;
  2478.  
  2479.                     if (dataImmunity.ContainsKey(victim.UserIDString))
  2480.                         return true; // immunity timer
  2481.  
  2482.                     if (hitInfo.Initiator is BaseHelicopter)
  2483.                         return true; // protect duelers from helicopters
  2484.  
  2485.                     if (attacker?.transform != null && dataDuelists[victim.UserIDString] != attacker.UserIDString)
  2486.                         return true; // prevent attacker from doing damage to others
  2487.  
  2488.                     hitInfo.damageTypes.ScaleAll(damageScaleAmount);
  2489.                     return null;
  2490.                 }
  2491.             }
  2492.  
  2493.             if (tdmMatches.Count > 0)
  2494.             {
  2495.                 if (victim != null && attacker?.transform != null && InDeathmatch(attacker))
  2496.                 {
  2497.                     var match = GetMatch(attacker);
  2498.  
  2499.                     if (match.GetTeam(victim) == Team.None)
  2500.                         return true;
  2501.  
  2502.                     if (!dmFF && match.GetTeam(victim) == match.GetTeam(attacker))
  2503.                         return true; // FF
  2504.                 }
  2505.  
  2506.                 if (victim?.transform != null && InDeathmatch(victim))
  2507.                 {
  2508.                     if (dataImmunity.ContainsKey(victim.UserIDString))
  2509.                         return true;
  2510.  
  2511.                     if (hitInfo.Initiator is BaseHelicopter)
  2512.                         return true;
  2513.  
  2514.                     if (attacker?.transform != null)
  2515.                     {
  2516.                         if (GetMatch(attacker) == null)
  2517.                             return true;
  2518.  
  2519.                         if (victim.health == 6f)
  2520.                             return true;
  2521.  
  2522.                         if (tdmAttackers.ContainsKey(victim.UserIDString))
  2523.                             tdmAttackers.Remove(victim.UserIDString);
  2524.  
  2525.                         string weapon = attacker.GetActiveItem()?.info?.displayName?.english ?? hitInfo?.WeaponPrefab?.ShortPrefabName ?? "??";
  2526.  
  2527.                         if (weapon.EndsWith(".entity"))
  2528.                         {
  2529.                             var def = ItemManager.FindItemDefinition(weapon.Replace(".entity", "").Replace("_", "."));
  2530.                             weapon = def?.displayName.translated ?? weapon.Replace(".entity", "").Replace("_", "").SentenceCase();
  2531.                         }
  2532.  
  2533.                         tdmAttackers.Add(victim.UserIDString, new AttackerInfo());
  2534.                         tdmAttackers[victim.UserIDString].AttackerName = attacker.displayName;
  2535.                         tdmAttackers[victim.UserIDString].AttackerId = attacker.UserIDString;
  2536.                         tdmAttackers[victim.UserIDString].Distance = Math.Round(Vector3.Distance(attacker.transform.position, victim.transform.position), 2).ToString();
  2537.                         tdmAttackers[victim.UserIDString].BoneName = FormatBone(hitInfo.boneName).TrimEnd(); //StringPool.Get(hitInfo.boneName)
  2538.                         tdmAttackers[victim.UserIDString].Weapon = weapon;
  2539.                     }
  2540.  
  2541.                     hitInfo.damageTypes.ScaleAll(damageScaleAmount);
  2542.                     return null;
  2543.                 }
  2544.             }
  2545.            
  2546.             if (victim?.transform != null && attacker?.transform != null) // 1.1.1 - fix for players standing on the edge of a zone for protection
  2547.             {
  2548.                 if (vdt && !InEvent(victim))
  2549.                     return null;
  2550.  
  2551.                 if (adt && !InEvent(attacker))
  2552.                     return null;
  2553.             }
  2554.            
  2555.             if (adt && !vdt)
  2556.                 return true; // block all damage to the outside
  2557.  
  2558.             if (!adt && vdt)
  2559.                 return true; // block all damage to the inside
  2560.  
  2561.             return null;
  2562.         }
  2563.  
  2564.         class Explosives : FacepunchBehaviour
  2565.         {
  2566.             WorldItem worldItem;
  2567.             float minExplosionRadius = 1f;
  2568.             float explosionRadius = 5f;
  2569.             int layers = 141568;
  2570.  
  2571.             void Awake()
  2572.             {
  2573.                 worldItem = GetComponent<WorldItem>();
  2574.             }
  2575.  
  2576.             void OnDestroy()
  2577.             {
  2578.                 var damageTypes = new List<DamageTypeEntry>
  2579.                 {
  2580.                     new DamageTypeEntry()
  2581.                     {
  2582.                         amount = 25f,
  2583.                         type = DamageType.Explosion
  2584.                     }
  2585.                 };
  2586.  
  2587.                 Effect.server.Run("some resource", worldItem.PivotPoint(), worldItem.transform.forward, null, true);
  2588.                 DamageUtil.RadiusDamage(worldItem, worldItem.LookupPrefab(), worldItem.CenterPoint(), minExplosionRadius, explosionRadius, damageTypes, layers, true);
  2589.                 Destroy(this);
  2590.             }
  2591.         }
  2592.  
  2593.         void OnEntitySpawned(WorldItem worldItem)
  2594.         {
  2595.             NextTick(() =>
  2596.             {
  2597.                 if (!worldItem.IsKilled() && worldItem.item != null && worldItem.item.info.shortname == "arrow.bone")
  2598.                 {
  2599.                     worldItem.gameObject.AddComponent<Explosives>();
  2600.                 }
  2601.             });
  2602.         }
  2603.  
  2604.         private void OnEntitySpawned(BaseEntity entity)
  2605.         {
  2606.             if (entity == null || entity.transform == null || !DuelTerritory(entity.transform.position))
  2607.                 return;
  2608.  
  2609.             if (entity.IsNpc)
  2610.             {
  2611.                 NextTick(entity.SafelyKill);
  2612.                 return;
  2613.             }
  2614.  
  2615.             if (autoOvens && entity is BaseOven || autoTurrets && entity is AutoTurret || autoFlames && entity is FlameTurret)
  2616.             {
  2617.                 SetupPower(entity);
  2618.                 return;
  2619.             }
  2620.  
  2621.             if (noStability && entity is BuildingBlock)
  2622.             {
  2623.                 var block = entity as BuildingBlock;
  2624.  
  2625.                 if (block.OwnerID == 0 || permission.UserHasGroup(block.OwnerID.ToString(), "admin"))
  2626.                 {
  2627.                     block.grounded = true;
  2628.                     return;
  2629.                 }
  2630.             }
  2631.  
  2632.             if (dataDuelists.Count == 0 && tdmMatches.Count == 0)
  2633.                 return;
  2634.  
  2635.             if (prefabs.ContainsKey(entity.PrefabName) && entity.name.Contains("barricade."))
  2636.             {
  2637.                 if (morphBarricadesStoneWalls || morphBarricadesWoodenWalls)
  2638.                 {
  2639.                     var wall = CreateZoneWall(morphBarricadesStoneWalls ? heswPrefab : hewwPrefab, entity.transform.position, entity.transform.rotation, entity.OwnerID);
  2640.  
  2641.                     if (wall != null)
  2642.                     {
  2643.                         entity.SafelyKill();
  2644.  
  2645.                         if (!duelEntities.ContainsKey(wall.OwnerID))
  2646.                             duelEntities.Add(wall.OwnerID, new List<BaseEntity>() { wall });
  2647.                         else duelEntities[wall.OwnerID].Add(wall);
  2648.                     }
  2649.  
  2650.                     return;
  2651.                 }
  2652.             }
  2653.  
  2654.             if (entity is PlayerCorpse || entity.name.Contains("item_drop_backpack"))
  2655.             {
  2656.                 NextTick(entity.SafelyKill);
  2657.             }
  2658.             else if (entity is WorldItem)
  2659.             {
  2660.                 if (duelsData.Homes.Count > 0)
  2661.                 {
  2662.                     NextTick(() => // prevent rpc kick by using NextTick since we're also hooking OnItemDropped
  2663.                     {
  2664.                         if (!entity.IsKilled()) // we must check this or you will still be rpc kicked
  2665.                         {
  2666.                             var worldItem = entity as WorldItem; // allow thrown weapons / destroy items which are dropped by players and on death
  2667.  
  2668.                             if (worldItem != null && worldItem.item != null && !IsThrownWeapon(worldItem.item))
  2669.                                 entity.Kill();
  2670.                         }
  2671.                     });
  2672.  
  2673.                     if (!entity.IsKilled())
  2674.                     {
  2675.                         timer.Repeat(0.1f, 20, () => // track the item to make sure it wasn't thrown out of the dueling zone
  2676.                         {
  2677.                             if (!entity.IsKilled() && !DuelTerritory(entity.transform.position))
  2678.                                 entity.Kill(); // destroy items which are dropped from inside to outside of the zone
  2679.                         });
  2680.                     }
  2681.                 }
  2682.             }
  2683.         }
  2684.  
  2685.         object CanBuild(Planner planner, Construction prefab, Construction.Target target)
  2686.         {
  2687.             var player = planner.GetOwnerPlayer();
  2688.  
  2689.             if (player.IsAdmin)
  2690.                 return null;
  2691.  
  2692.             var position = player.transform.position;
  2693.             var buildPos = position + player.eyes.BodyForward() * 4f; // get the estimated position of where the player is trying to build at
  2694.             var up = buildPos + Vector3.up + new Vector3(0f, 0.6f, 0f);
  2695.  
  2696.             buildPos.y = Mathf.Max(position.y, up.y); // adjust the cursor position to our best estimate
  2697.  
  2698.             if (DuelTerritory(buildPos, buildingBlockExtensionRadius)) // extend the distance slightly
  2699.             {
  2700.                 if (deployables.Count > 0)
  2701.                 {
  2702.                     var kvp = prefabs.FirstOrDefault(x => x.Key == prefab.fullName);
  2703.  
  2704.                     if (!string.IsNullOrEmpty(kvp.Value) && deployables.ContainsKey(kvp.Value) && deployables[kvp.Value])
  2705.                     {
  2706.                         if (dataDuelists.ContainsKey(player.UserIDString) || InMatch(player))
  2707.                         {
  2708.                             return null;
  2709.                         }
  2710.                     }
  2711.                 }
  2712.  
  2713.                 Message(player, msg("Building is blocked!", player.UserIDString));
  2714.                 return false;
  2715.             }
  2716.  
  2717.             return null;
  2718.         }
  2719.  
  2720.         private void OnLootEntity(BasePlayer player, BaseEntity entity) // stop all players from looting anything inside of dueling zones. this allows server owners to setup duels anywhere without worry.
  2721.         {
  2722.             if (player != null && (IsDueling(player) || InDeathmatch(player) || IsSpectator(player)))
  2723.                 timer.Once(0.01f, player.EndLooting);
  2724.  
  2725.             if (dataDuelists.Count == 0 && tdmMatches.Count == 0 && spectators.Count == 0)
  2726.                 Unsubscribe(nameof(OnLootEntity));
  2727.         }
  2728.  
  2729.         private object OnCreateWorldProjectile(HitInfo info, Item item) // prevents thrown items from becoming stuck in players when they respawn and requiring them to relog to remove them
  2730.         {
  2731.             if (info == null)
  2732.                 return null;
  2733.  
  2734.             if (dataDuelists.Count == 0 && tdmMatches.Count == 0)
  2735.             {
  2736.                 Unsubscribe(nameof(OnCreateWorldProjectile));
  2737.                 return null;
  2738.             }
  2739.  
  2740.             var victim = info.HitEntity as BasePlayer;
  2741.             var attacker = info.Initiator as BasePlayer;
  2742.  
  2743.             if (victim != null && (IsDueling(victim) || InDeathmatch(victim)))
  2744.                 return false; // block it
  2745.  
  2746.             if (attacker != null && (IsDueling(attacker) || InDeathmatch(attacker)))
  2747.                 return false;
  2748.  
  2749.             return null;
  2750.         }
  2751.  
  2752.         private void OnItemDropped(Item item, BaseEntity entity)
  2753.         {
  2754.             if (dataDuelists.Count == 0 && tdmMatches.Count == 0) // nothing left to do here, unsubscribe the hook
  2755.             {
  2756.                 Unsubscribe(nameof(OnItemDropped));
  2757.                 return;
  2758.             }
  2759.  
  2760.             if (item.GetOwnerPlayer() == null)
  2761.                 return;
  2762.  
  2763.             var player = item.GetOwnerPlayer();
  2764.  
  2765.             if (!IsThrownWeapon(item) && (IsDueling(player) || InDeathmatch(player)))
  2766.                 item.Remove(0.01f); // do NOT allow players to drop items
  2767.         }
  2768.  
  2769.         private object IsPrisoner(BasePlayer player) // Random Warps
  2770.         {
  2771.             return IsDueling(player) || InDeathmatch(player) ? (object)true : null;
  2772.         }
  2773.  
  2774.         private object CanEventJoin(BasePlayer player) // EventManager
  2775.         {
  2776.             return IsDueling(player) || InDeathmatch(player) ? msg("CannotEventJoin", player.UserIDString) : null;
  2777.         }
  2778.  
  2779.         private object canRemove(BasePlayer player) // RemoverTool
  2780.         {
  2781.             return DuelTerritory(player.transform.position) ? (object)false : null;
  2782.         }
  2783.  
  2784.         private object CanTrade(BasePlayer player) // Trade
  2785.         {
  2786.             return DuelTerritory(player.transform.position) ? (object)false : null;
  2787.         }
  2788.  
  2789.         private object CanBank(BasePlayer player)
  2790.         {
  2791.             return DuelTerritory(player.transform.position) ? msg("CannotBank", player.UserIDString) : null;
  2792.         }
  2793.  
  2794.         private object CanOpenBackpack(BasePlayer player)
  2795.         {
  2796.             return DuelTerritory(player.transform.position) ? msg("CommandNotAllowed", player.UserIDString) : null;
  2797.         }
  2798.  
  2799.         private object canShop(BasePlayer player) // Shop and ServerRewards
  2800.         {
  2801.             return DuelTerritory(player.transform.position) ? msg("CannotShop", player.UserIDString) : null;
  2802.         }
  2803.  
  2804.         private object CanShop(BasePlayer player)
  2805.         {
  2806.             return DuelTerritory(player.transform.position) ? msg("CannotShop", player.UserIDString) : null;
  2807.         }
  2808.  
  2809.         private object CanBePenalized(BasePlayer player) // ZLevels Remastered
  2810.         {
  2811.             return DuelTerritory(player.transform.position) || dataDuelists.ContainsKey(player.UserIDString) ? (object)false : null;
  2812.         }
  2813.  
  2814.         private object canTeleport(BasePlayer player) // 0.1.2: block teleport from NTeleportation plugin
  2815.         {
  2816.             return DuelTerritory(player.transform.position) ? msg("CannotTeleport", player.UserIDString) : null;
  2817.         }
  2818.  
  2819.         private object CanTeleport(BasePlayer player) // 0.1.2: block teleport from MagicTeleportation plugin
  2820.         {
  2821.             return DuelTerritory(player.transform.position) ? msg("CannotTeleport", player.UserIDString) : null;
  2822.         }
  2823.  
  2824.         private object CanJoinTDMEvent(BasePlayer player)
  2825.         {
  2826.             return DuelTerritory(player.transform.position) ? (object)false : null;
  2827.         }
  2828.  
  2829.         private object CanEntityTakeDamage(BaseEntity entity, HitInfo hitinfo) // TruePVE!!!! <3 @ignignokt84
  2830.         {
  2831.             return entity is BasePlayer && DuelTerritory(entity.transform.position) ? (object)true : null;
  2832.         }
  2833.  
  2834.         private object CanJoinAimTrain(BasePlayer player)
  2835.         {
  2836.             if (InQueue(player.UserIDString) || InEvent(player) || DuelTerritory(player.transform.position))
  2837.             {
  2838.                 return false;
  2839.             }
  2840.  
  2841.             return null;
  2842.         }
  2843.  
  2844.         private bool InQueue(string userid)
  2845.         {
  2846.             foreach (var element in duelsData.Queued)
  2847.             {
  2848.                 if (element.Value == userid)
  2849.                 {
  2850.                     return true;
  2851.                 }
  2852.             }
  2853.            
  2854.             return false;
  2855.         }
  2856.  
  2857.         object OnPlayerCommand(BasePlayer player, string command, string[] args)
  2858.         {
  2859.             if (!player.IsValid() || player.transform == null)
  2860.             {
  2861.                 return null;
  2862.             }
  2863.  
  2864.             bool inQueue = InQueue(player.UserIDString);
  2865.  
  2866.             if (inQueue || DuelTerritory(player.transform.position))
  2867.             {
  2868.                 if (useBlacklistCommands && blacklistCommands.Exists(entry => entry.Replace("/", "").Equals(command, StringComparison.OrdinalIgnoreCase)))
  2869.                 {
  2870.                     Message(player, msg(inQueue ? "CommandNotAllowedQueued" : "CommandNotAllowed", player.UserIDString));
  2871.                     return true;
  2872.                 }
  2873.  
  2874.                 if (useWhitelistCommands && !whitelistCommands.Exists(entry => entry.Replace("/", "").Equals(command, StringComparison.OrdinalIgnoreCase)))
  2875.                 {
  2876.                     Message(player, msg(inQueue ? "CommandNotAllowedQueued" : "CommandNotAllowed", player.UserIDString));
  2877.                     return true;
  2878.                 }
  2879.             }
  2880.  
  2881.             return null;
  2882.         }
  2883.  
  2884.         object OnServerCommand(ConsoleSystem.Arg arg)
  2885.         {
  2886.             var player = arg.Player();
  2887.  
  2888.             if (!player.IsValid())
  2889.             {
  2890.                 return null;
  2891.             }
  2892.  
  2893.             bool inQueue = InQueue(player.UserIDString);
  2894.  
  2895.             if (inQueue || DuelTerritory(player.transform.position))
  2896.             {
  2897.                 string command = arg.cmd.FullName;
  2898.  
  2899.                 if (useBlacklistCommands && blacklistCommands.Exists(entry => entry.Replace("/", "").Equals(command, StringComparison.OrdinalIgnoreCase)))
  2900.                 {
  2901.                     Message(player, msg(inQueue ? "CommandNotAllowedQueued" : "CommandNotAllowed", player.UserIDString));
  2902.                     return true;
  2903.                 }
  2904.  
  2905.                 if (useWhitelistCommands && !whitelistCommands.Exists(entry => entry.Replace("/", "").Equals(command, StringComparison.OrdinalIgnoreCase)))
  2906.                 {
  2907.                     Message(player, msg(inQueue ? "CommandNotAllowedQueued" : "CommandNotAllowed", player.UserIDString));
  2908.                     return true;
  2909.                 }
  2910.             }
  2911.  
  2912.             return null;
  2913.         }
  2914.  
  2915.         public void CheckAutoReady(BasePlayer player)
  2916.         {
  2917.             if (duelsData.AutoReady.Contains(player.UserIDString))
  2918.             {
  2919.                 if (!readyUiList.Contains(player.UserIDString))
  2920.                 {
  2921.                     ToggleReadyUI(player);
  2922.                 }
  2923.             }
  2924.             else if (readyUiList.Contains(player.UserIDString))
  2925.             {
  2926.                 CuiHelper.DestroyUi(player, "DuelistUI_Ready");
  2927.                 readyUiList.Remove(player.UserIDString);
  2928.             }
  2929.         }
  2930.  
  2931.         public void ToggleAutoReady(BasePlayer player)
  2932.         {
  2933.             if (duelsData.AutoReady.Contains(player.UserIDString))
  2934.                 duelsData.AutoReady.Remove(player.UserIDString);
  2935.             else
  2936.                 duelsData.AutoReady.Add(player.UserIDString);
  2937.  
  2938.             Message(player, msg(duelsData.AutoReady.Contains(player.UserIDString) ? "RematchAutoOn" : "RematchAutoOff", player.UserIDString));
  2939.  
  2940.             if (DuelTerritory(player.transform.position))
  2941.                 CreateDefeatUI(player);
  2942.  
  2943.             if (duelistUI.Contains(player.UserIDString))
  2944.                 RefreshUI(player);
  2945.         }
  2946.  
  2947.         public void ReadyUp(BasePlayer player)
  2948.         {
  2949.             var rematch = rematches.FirstOrDefault(x => x.HasPlayer(player));
  2950.  
  2951.             if (rematch == null)
  2952.             {
  2953.                 ToggleAutoReady(player);
  2954.                 Message(player, msg("RematchNone", player.UserIDString));
  2955.                 return;
  2956.             }
  2957.  
  2958.             if (rematch.Ready.Contains(player))
  2959.             {
  2960.                 Message(player, msg("RematchAcceptedAlready", player.UserIDString));
  2961.                 ToggleAutoReady(player);
  2962.             }
  2963.             else
  2964.             {
  2965.                 Message(player, msg("RematchAccepted", player.UserIDString));
  2966.                 rematch.Ready.Add(player);
  2967.             }
  2968.  
  2969.             if (rematch.IsReady())
  2970.             {
  2971.                 rematch.Start();
  2972.                 rematches.Remove(rematch);
  2973.             }
  2974.         }
  2975.  
  2976.         public void cmdTDM(BasePlayer player, string command, string[] args)
  2977.         {
  2978.             if (player.IsAdmin && args.Length == 1 && args[0] == "showall" && tdmMatches.Count > 0)
  2979.             {
  2980.                 foreach (var match in tdmMatches)
  2981.                 {
  2982.                     Message(player, msg("InMatchListGood", player.UserIDString, match.GetNames(Team.Good)));
  2983.                     Message(player, msg("InMatchListEvil", player.UserIDString, match.GetNames(Team.Evil)));
  2984.                 }
  2985.  
  2986.                 return;
  2987.             }
  2988.  
  2989.             if (!autoAllowAll && !duelsData.Allowed.Contains(player.UserIDString))
  2990.             {
  2991.                 Message(player, msg("MustAllowDuels", player.UserIDString, szDuelChatCommand));
  2992.                 return;
  2993.             }
  2994.  
  2995.             if (IsDueling(player))
  2996.             {
  2997.                 Message(player, msg("AlreadyInADuel", player.UserIDString));
  2998.                 return;
  2999.             }
  3000.  
  3001.             var deathmatch = tdmMatches.FirstOrDefault(x => x.GetTeam(player) != Team.None);
  3002.  
  3003.             if (deathmatch != null && deathmatch.IsStarted)
  3004.             {
  3005.                 Message(player, msg("MatchStartedAlready", player.UserIDString));
  3006.                 return;
  3007.             }
  3008.  
  3009.             if (args.Length == 0)
  3010.             {
  3011.                 if (deathmatch == null)
  3012.                 {
  3013.                     if (!autoAllowAll)
  3014.                         Message(player, msg("HelpAllow", player.UserIDString, szDuelChatCommand));
  3015.  
  3016.                     Message(player, msg("MatchChallenge0", player.UserIDString, szMatchChatCommand));
  3017.                     Message(player, msg("MatchChallenge2", player.UserIDString, szMatchChatCommand));
  3018.                     Message(player, msg("MatchChallenge3", player.UserIDString, szMatchChatCommand));
  3019.                     Message(player, msg("MatchAccept", player.UserIDString, szMatchChatCommand));
  3020.                     Message(player, msg("MatchCancel", player.UserIDString, szMatchChatCommand));
  3021.                     Message(player, msg("MatchLeave", player.UserIDString, szMatchChatCommand));
  3022.                     Message(player, msg("MatchSize", player.UserIDString, szMatchChatCommand, minDeathmatchSize));
  3023.                     Message(player, msg("MatchKickBan", player.UserIDString, szMatchChatCommand));
  3024.                     Message(player, msg("MatchSetCode", player.UserIDString, szMatchChatCommand));
  3025.                     Message(player, msg("MatchTogglePublic", player.UserIDString, szMatchChatCommand));
  3026.                     Message(player, msg("MatchKit", player.UserIDString, szMatchChatCommand));
  3027.                     Message(player, msg("UI_Help", player.UserIDString, szUIChatCommand));
  3028.                 }
  3029.                 else
  3030.                 {
  3031.                     Message(player, msg("MatchLeave", player.UserIDString, szMatchChatCommand));
  3032.  
  3033.                     if (!deathmatch.IsHost(player))
  3034.                         return;
  3035.  
  3036.                     Message(player, msg("MatchCancel", player.UserIDString, szMatchChatCommand));
  3037.                     Message(player, msg("MatchSize", player.UserIDString, szMatchChatCommand, minDeathmatchSize));
  3038.                     Message(player, msg("MatchKickBan", player.UserIDString, szMatchChatCommand));
  3039.                     Message(player, msg("MatchSetCode", player.UserIDString, szMatchChatCommand));
  3040.                     Message(player, msg("MatchTogglePublic", player.UserIDString, szMatchChatCommand));
  3041.                     Message(player, msg("MatchKit", player.UserIDString, szMatchChatCommand));
  3042.                     Message(player, msg("InMatchListGood", player.UserIDString, deathmatch.GetNames(Team.Good)));
  3043.                     Message(player, msg("InMatchListEvil", player.UserIDString, deathmatch.GetNames(Team.Evil)));
  3044.                 }
  3045.  
  3046.                 return;
  3047.             }
  3048.  
  3049.             RemoveRequests(player);
  3050.  
  3051.             switch (args[0].ToLower())
  3052.             {
  3053.                 case "autoready":
  3054.                     {
  3055.                         ToggleAutoReady(player);
  3056.                         return;
  3057.                     }
  3058.                 case "rematch":
  3059.                 case "ready":
  3060.                     {
  3061.                         ReadyUp(player);
  3062.                         return;
  3063.                     }
  3064.                 case "kit":
  3065.                     {
  3066.                         if (deathmatch != null)
  3067.                         {
  3068.                             if (!deathmatch.IsHost(player))
  3069.                             {
  3070.                                 Message(player, msg("MatchKitSet", player.UserIDString, deathmatch.Kit));
  3071.                                 return;
  3072.                             }
  3073.  
  3074.                             if (args.Length == 2)
  3075.                             {
  3076.                                 string kit = GetVerifiedKit(args[1]);
  3077.  
  3078.                                 if (string.IsNullOrEmpty(kit))
  3079.                                 {
  3080.                                     Message(player, msg("MatchChallenge0", player.UserIDString, szMatchChatCommand));
  3081.                                     Message(player, msg("KitDoesntExist", player.UserIDString, args[1]));
  3082.  
  3083.                                     string kits = string.Join(", ", VerifiedKits.ToArray());
  3084.  
  3085.                                     if (!string.IsNullOrEmpty(kits))
  3086.                                         Message(player, "Kits: " + kits);
  3087.                                 }
  3088.                                 else
  3089.                                     deathmatch.Kit = kit;
  3090.                             }
  3091.                             else
  3092.                                 Message(player, msg("MatchKit", player.UserIDString));
  3093.                         }
  3094.                         else
  3095.                             Message(player, msg("MatchDoesntExist", player.UserIDString, szMatchChatCommand));
  3096.  
  3097.                         return;
  3098.                     }
  3099.                 case "kickban":
  3100.                     {
  3101.                         if (deathmatch != null)
  3102.                         {
  3103.                             if (!deathmatch.IsHost(player))
  3104.                             {
  3105.                                 Message(player, msg("MatchNotAHost", player.UserIDString));
  3106.                                 return;
  3107.                             }
  3108.  
  3109.                             if (args.Length == 2)
  3110.                             {
  3111.                                 var target = BasePlayer.Find(args[1]);
  3112.  
  3113.                                 if (target != null)
  3114.                                 {
  3115.                                     if (deathmatch.GetTeam(target) == deathmatch.GetTeam(player))
  3116.                                     {
  3117.                                         if (deathmatch.Ban(target))
  3118.                                             Message(player, msg("MatchBannedUser", player.UserIDString, target.displayName));
  3119.                                         else
  3120.                                             Message(player, msg("MatchCannotBan", player.UserIDString));
  3121.                                     }
  3122.                                     else
  3123.                                         Message(player, msg("MatchPlayerNotFound", player.UserIDString, target.displayName));
  3124.                                 }
  3125.                                 else
  3126.                                     Message(player, msg("PlayerNotFound", player.UserIDString, args[1]));
  3127.                             }
  3128.                             else
  3129.                                 Message(player, msg("MatchKickBan", player.UserIDString));
  3130.                         }
  3131.                         else
  3132.                             Message(player, msg("MatchDoesntExist", player.UserIDString, szMatchChatCommand));
  3133.  
  3134.                         break;
  3135.                     }
  3136.                 case "setcode":
  3137.                     {
  3138.                         if (deathmatch != null)
  3139.                         {
  3140.                             if (deathmatch.IsHost(player))
  3141.                             {
  3142.                                 if (args.Length == 2)
  3143.                                     deathmatch.SetCode(player, args[1]);
  3144.  
  3145.                                 Message(player, msg("MatchCodeIs", player.UserIDString, deathmatch.GetTeam(player) == Team.Evil ? deathmatch.Code(Team.Evil) : deathmatch.Code(Team.Good)));
  3146.                             }
  3147.                             else
  3148.                                 Message(player, msg("MatchNotAHost", player.UserIDString));
  3149.                         }
  3150.                         else
  3151.                             Message(player, msg("MatchDoesntExist", player.UserIDString, szMatchChatCommand));
  3152.  
  3153.                         break;
  3154.                     }
  3155.                 case "cancel":
  3156.                 case "decline":
  3157.                     {
  3158.                         if (deathmatch != null)
  3159.                         {
  3160.                             if (deathmatch.IsHost(player))
  3161.                             {
  3162.                                 deathmatch.MessageAll("MatchCancelled", player.displayName);
  3163.                                 deathmatch.End();
  3164.  
  3165.                                 if (tdmMatches.Contains(deathmatch))
  3166.                                 {
  3167.                                     tdmMatches.Remove(deathmatch);
  3168.                                     matchUpdateRequired = true;
  3169.                                 }
  3170.                             }
  3171.                             else
  3172.                                 Message(player, msg("MatchNotAHost", player.UserIDString));
  3173.                         }
  3174.                         else // also handle cancelling a match request
  3175.                         {
  3176.                             if (tdmRequests.ContainsValue(player.UserIDString))
  3177.                             {
  3178.                                 var entry = tdmRequests.FirstOrDefault(kvp => kvp.Value == player.UserIDString);
  3179.                                 var target = BasePlayer.Find(entry.Key);
  3180.  
  3181.                                 if (target != null)
  3182.                                     Message(target, msg("MatchCancelled", target.UserIDString, player.displayName));
  3183.  
  3184.                                 Message(player, msg("MatchCancelled", player.UserIDString, player.displayName));
  3185.                                 tdmRequests.Remove(entry.Key);
  3186.                                 return;
  3187.                             }
  3188.  
  3189.                             if (tdmRequests.ContainsKey(player.UserIDString))
  3190.                             {
  3191.                                 var target = BasePlayer.Find(tdmRequests[player.UserIDString]);
  3192.  
  3193.                                 if (target != null)
  3194.                                     Message(target, msg("MatchCancelled", player.UserIDString, player.displayName));
  3195.  
  3196.                                 Message(player, msg("MatchCancelled", player.UserIDString, player.displayName));
  3197.                                 tdmRequests.Remove(player.UserIDString);
  3198.                                 return;
  3199.                             }
  3200.  
  3201.                             Message(player, msg("MatchDoesntExist", player.UserIDString, szMatchChatCommand));
  3202.                         }
  3203.  
  3204.                         break;
  3205.                     }
  3206.                 case "size":
  3207.                     {
  3208.                         if (deathmatch != null)
  3209.                         {
  3210.                             if (args.Length == 2)
  3211.                             {
  3212.                                 if (args[1].All(char.IsDigit))
  3213.                                 {
  3214.                                     if (deathmatch.IsHost(player))
  3215.                                     {
  3216.                                         int size = Convert.ToInt32(args[1]);
  3217.  
  3218.                                         if (size < minDeathmatchSize)
  3219.                                             size = deathmatch.TeamSize;
  3220.  
  3221.                                         if (size > maxDeathmatchSize)
  3222.                                             size = maxDeathmatchSize;
  3223.  
  3224.                                         if (deathmatch.TeamSize != size)
  3225.                                             deathmatch.TeamSize = size; // sends message to all players in the match
  3226.                                     }
  3227.                                     else
  3228.                                         Message(player, msg("MatchNotAHost", player.UserIDString));
  3229.                                 }
  3230.                                 else
  3231.                                     Message(player, msg("InvalidNumber", player.UserIDString, args[1]));
  3232.                             }
  3233.                             else
  3234.                                 Message(player, msg("MatchSizeSyntax", player.UserIDString, szMatchChatCommand));
  3235.                         }
  3236.                         else
  3237.                             Message(player, msg("MatchDoesntExist", player.UserIDString, szMatchChatCommand));
  3238.  
  3239.                         break;
  3240.                     }
  3241.                 case "accept":
  3242.                     {
  3243.                         if (InEvent(player))
  3244.                         {
  3245.                             Message(player, msg("AlreadyDueling", player.UserIDString));
  3246.                             return;
  3247.                         }
  3248.  
  3249.                         if (!tdmRequests.ContainsValue(player.UserIDString))
  3250.                         {
  3251.                             Message(player, msg("MatchNoneRequested", player.UserIDString));
  3252.                             return;
  3253.                         }
  3254.  
  3255.                         var kvp = tdmRequests.FirstOrDefault(entry => entry.Value == player.UserIDString);
  3256.                         var target = BasePlayer.Find(kvp.Key);
  3257.  
  3258.                         tdmRequests.Remove(kvp.Key);
  3259.  
  3260.                         if (!target || !target.IsConnected)
  3261.                         {
  3262.                             Message(player, msg("MatchPlayerOffline", player.UserIDString));
  3263.                             break;
  3264.                         }
  3265.  
  3266.                         SetupTeams(player, target);
  3267.                         break;
  3268.                     }
  3269.                 case "leave":
  3270.                     {
  3271.                         if (deathmatch != null)
  3272.                         {
  3273.                             deathmatch.RemoveMatchPlayer(player);
  3274.                             Message(player, msg("MatchPlayerLeft", player.UserIDString));
  3275.                         }
  3276.                         else
  3277.                             Message(player, msg("MatchDoesntExist", player.UserIDString, szMatchChatCommand));
  3278.  
  3279.                         break;
  3280.                     }
  3281.                 case "any":
  3282.                     {
  3283.                         if (tdmMatches.Count == 0)
  3284.                         {
  3285.                             Message(player, msg("MatchNoMatchesExist", player.UserIDString, szMatchChatCommand));
  3286.                             return;
  3287.                         }
  3288.  
  3289.                         if (deathmatch != null)
  3290.                         {
  3291.                             deathmatch.RemoveMatchPlayer(player);
  3292.                             Message(player, msg("MatchPlayerLeft", player.UserIDString));
  3293.                         }
  3294.  
  3295.                         foreach (var match in tdmMatches)
  3296.                         {
  3297.                             if (match.IsBanned(player.userID) || match.IsFull())
  3298.                                 continue;
  3299.  
  3300.                             if (!match.IsFull(Team.Good) && match.AlliedTo(player, Team.Good))
  3301.                             {
  3302.                                 match.AddMatchPlayer(player, Team.Good);
  3303.                                 return;
  3304.                             }
  3305.  
  3306.                             if (!match.IsFull(Team.Evil) && match.AlliedTo(player, Team.Evil))
  3307.                             {
  3308.                                 match.AddMatchPlayer(player, Team.Evil);
  3309.                                 return;
  3310.                             }
  3311.  
  3312.                             if (match.IsPublic)
  3313.                             {
  3314.                                 if (!match.IsFull(Team.Good))
  3315.                                 {
  3316.                                     match.AddMatchPlayer(player, Team.Good);
  3317.                                     return;
  3318.                                 }
  3319.  
  3320.                                 if (!match.IsFull(Team.Evil))
  3321.                                 {
  3322.                                     match.AddMatchPlayer(player, Team.Evil);
  3323.                                     return;
  3324.                                 }
  3325.                             }
  3326.                         }
  3327.  
  3328.                         Message(player, msg("MatchNoTeamFoundAny", player.UserIDString, args[0]));
  3329.                         break;
  3330.                     }
  3331.                 case "public":
  3332.                     {
  3333.                         if (deathmatch != null)
  3334.                         {
  3335.                             if (!deathmatch.IsHost(player))
  3336.                             {
  3337.                                 Message(player, msg("MatchNotAHost", player.UserIDString));
  3338.                                 return;
  3339.                             }
  3340.  
  3341.                             deathmatch.IsPublic = !deathmatch.IsPublic;
  3342.                         }
  3343.                         else
  3344.                             Message(player, msg("MatchDoesntExist", player.UserIDString, szMatchChatCommand));
  3345.  
  3346.                         break;
  3347.                     }
  3348.                 default:
  3349.                     {
  3350.                         if (args.Length > 1)
  3351.                         {
  3352.                             SetPlayerZone(player, args);
  3353.  
  3354.                             foreach (string arg in args)
  3355.                             {
  3356.                                 string kit = GetVerifiedKit(arg);
  3357.  
  3358.                                 if (!string.IsNullOrEmpty(kit))
  3359.                                 {
  3360.                                     tdmKits[player.UserIDString] = kit;
  3361.                                     break;
  3362.                                 }
  3363.                             }
  3364.                         }
  3365.  
  3366.                         var target = BasePlayer.Find(args[0]);
  3367.  
  3368.                         if (target != null)
  3369.                         {
  3370.                             if (target == player)
  3371.                             {
  3372.                                 Message(player, msg("PlayerNotFound", player.UserIDString, args[0]));
  3373.                                 return;
  3374.                             }
  3375.  
  3376.                             if (deathmatch != null)
  3377.                             {
  3378.                                 Message(player, msg("MatchCannotChallengeAgain", player.UserIDString));
  3379.                                 return;
  3380.                             }
  3381.  
  3382.                             if (InMatch(target) || tdmRequests.ContainsValue(target.UserIDString))
  3383.                             {
  3384.                                 Message(player, msg("MatchCannotChallenge", player.UserIDString, target.displayName));
  3385.                                 return;
  3386.                             }
  3387.  
  3388.                             if (!IsNewman(player))
  3389.                             {
  3390.                                 Message(player, msg("MustBeNaked", player.UserIDString));
  3391.                                 return;
  3392.                             }
  3393.  
  3394.                             if (!IsNewman(target))
  3395.                             {
  3396.                                 Message(player, msg("TargetMustBeNaked", player.UserIDString));
  3397.                                 return;
  3398.                             }
  3399.  
  3400.                             Message(player, msg("MatchRequestSent", player.UserIDString, target.displayName));
  3401.                             Message(target, msg("MatchRequested", target.UserIDString, player.displayName, szMatchChatCommand));
  3402.  
  3403.                             string uid = player.UserIDString;
  3404.                             tdmRequests.Remove(uid);
  3405.                             tdmRequests.Add(uid, target.UserIDString);
  3406.                             timer.Once(60f, () => tdmRequests.Remove(uid));
  3407.                             return;
  3408.                         }
  3409.  
  3410.                         if (tdmMatches.Count == 0)
  3411.                         {
  3412.                             Message(player, msg("MatchNoMatchesExist", player.UserIDString, szMatchChatCommand));
  3413.                             return;
  3414.                         }
  3415.  
  3416.                         if (deathmatch != null)
  3417.                         {
  3418.                             deathmatch.RemoveMatchPlayer(player);
  3419.                             Message(player, msg("MatchPlayerLeft", player.UserIDString));
  3420.                         }
  3421.  
  3422.                         foreach (var match in tdmMatches)
  3423.                         {
  3424.                             if (match.IsBanned(player.userID))
  3425.                                 continue;
  3426.  
  3427.                             if (match.Code(Team.Good).Equals(args[0], StringComparison.OrdinalIgnoreCase))
  3428.                             {
  3429.                                 match.AddMatchPlayer(player, Team.Good);
  3430.                                 return;
  3431.                             }
  3432.                             else if (match.Code(Team.Evil).Equals(args[0], StringComparison.OrdinalIgnoreCase))
  3433.                             {
  3434.                                 match.AddMatchPlayer(player, Team.Evil);
  3435.                                 return;
  3436.                             }
  3437.                         }
  3438.  
  3439.                         Message(player, msg("MatchNoTeamFoundCode", player.UserIDString, args[0]));
  3440.  
  3441.                     }
  3442.                     break;
  3443.             }
  3444.         }
  3445.  
  3446.         public void cmdQueue(BasePlayer player, string command, string[] args)
  3447.         {
  3448.             if (!player || !player.IsConnected || AimTrain.CanCall() && Convert.ToBoolean(AimTrain?.Call("IsAimTraining", player.userID)))
  3449.                 return;
  3450.  
  3451.             if (!player.CanInteract())
  3452.             {
  3453.                 timer.Once(1f, () => cmdQueue(player, command, args));
  3454.                 return;
  3455.             }
  3456.  
  3457.             if (!autoAllowAll && !duelsData.Allowed.Contains(player.UserIDString))
  3458.             {
  3459.                 Message(player, msg("MustAllowDuels", player.UserIDString, szDuelChatCommand));
  3460.                 return;
  3461.             }
  3462.  
  3463.             if (InMatch(player))
  3464.             {
  3465.                 Message(player, msg("MatchTeamed", player.UserIDString));
  3466.                 return;
  3467.             }
  3468.  
  3469.             if (IsDueling(player))
  3470.             {
  3471.                 Message(player, msg("AlreadyInADuel", player.UserIDString));
  3472.                 return;
  3473.             }
  3474.  
  3475.             RemoveRequests(player);
  3476.  
  3477.             if (!IsNewman(player))
  3478.             {
  3479.                 Message(player, msg("MustBeNaked", player.UserIDString));
  3480.                 return;
  3481.             }
  3482.  
  3483.             if (player.IsAdmin)
  3484.             {
  3485.                 Message(player, msg("InQueueList", player.UserIDString));
  3486.                 Message(player, string.Join(", ", duelsData.Queued.ToList().Select(kvp => GetDisplayName(kvp.Value))));
  3487.             }
  3488.  
  3489.             if (!InQueue(player.UserIDString))
  3490.             {
  3491.                 long stamp = TimeStamp();
  3492.  
  3493.                 while (duelsData.Queued.ContainsKey(stamp))
  3494.                     stamp++;
  3495.  
  3496.                 duelsData.Queued.Add(stamp, player.UserIDString);
  3497.                 Message(player, msg("InQueueSuccess", player.UserIDString));
  3498.                 CheckQueue();
  3499.                 return;
  3500.             }
  3501.  
  3502.             if (RemoveFromQueue(player.UserIDString))
  3503.                 Message(player, msg("NoLongerQueued", player.UserIDString));
  3504.         }
  3505.  
  3506.         private void cmdLadder(BasePlayer player, string command, string[] args)
  3507.         {
  3508.             bool onLadder = false;
  3509.             bool life = args.Exists(arg => arg.ToLower().Contains("life"));
  3510.             var sorted = life ? duelsData.Victories.ToList() : duelsData.VictoriesSeed.ToList();
  3511.             sorted.Sort((x, y) => y.Value.CompareTo(x.Value));
  3512.  
  3513.             Message(player, msg(life ? "TopAll" : "Top", player.UserIDString, sorted.Count));
  3514.  
  3515.             for (int i = 0; i < 10; i++)
  3516.             {
  3517.                 if (i >= sorted.Count)
  3518.                     break;
  3519.  
  3520.                 if (sorted[i].Key == player.UserIDString)
  3521.                     onLadder = true; // 0.1.2: fix for ranks showing user on ladder twice
  3522.  
  3523.                 string name = GetDisplayName(sorted[i].Key);
  3524.                 int losses = 0;
  3525.  
  3526.                 if (life)
  3527.                     losses = duelsData.Losses.ContainsKey(sorted[i].Key) ? duelsData.Losses[sorted[i].Key] : 0;
  3528.                 else
  3529.                     losses = duelsData.LossesSeed.ContainsKey(sorted[i].Key) ? duelsData.LossesSeed[sorted[i].Key] : 0;
  3530.  
  3531.                 double ratio = losses > 0 ? Math.Round(sorted[i].Value / (double)losses, 2) : sorted[i].Value;
  3532.                 string message = msg("TopFormat", player.UserIDString, (i + 1).ToString(), name, sorted[i].Value, losses, ratio);
  3533.                 Player.Message(player, message, Convert.ToUInt64(sorted[i].Key));
  3534.             }
  3535.  
  3536.             if (!onLadder && !life && duelsData.VictoriesSeed.ContainsKey(player.UserIDString))
  3537.             {
  3538.                 int index = sorted.FindIndex(kvp => kvp.Key == player.UserIDString);
  3539.                 int losses = duelsData.LossesSeed.ContainsKey(player.UserIDString) ? duelsData.LossesSeed[player.UserIDString] : 0;
  3540.                 double ratio = losses > 0 ? Math.Round(duelsData.VictoriesSeed[player.UserIDString] / (double)losses, 2) : duelsData.VictoriesSeed[player.UserIDString];
  3541.                 string message = msg("TopFormat", player.UserIDString, index, player.displayName, duelsData.VictoriesSeed[player.UserIDString], losses, ratio);
  3542.                 Player.Message(player, message, player.userID);
  3543.             }
  3544.  
  3545.             if (!onLadder && life && duelsData.Victories.ContainsKey(player.UserIDString))
  3546.             {
  3547.                 int index = sorted.FindIndex(kvp => kvp.Key == player.UserIDString);
  3548.                 int losses = duelsData.Losses.ContainsKey(player.UserIDString) ? duelsData.Losses[player.UserIDString] : 0;
  3549.                 double ratio = losses > 0 ? Math.Round(duelsData.Victories[player.UserIDString] / (double)losses, 2) : duelsData.Victories[player.UserIDString];
  3550.                 string message = msg("TopFormat", player.UserIDString, index, player.displayName, duelsData.Victories[player.UserIDString], losses, ratio);
  3551.                 Player.Message(player, message, player.userID);
  3552.             }
  3553.  
  3554.             if (!life) Message(player, msg("LadderLife", player.UserIDString, szDuelChatCommand));
  3555.             sorted.Clear();
  3556.             sorted = null;
  3557.         }
  3558.  
  3559.         private void ccmdDuel(ConsoleSystem.Arg arg)
  3560.         {
  3561.             if (!arg.IsAdmin)
  3562.                 return;
  3563.  
  3564.             string id = arg.Player()?.UserIDString ?? null;
  3565.  
  3566.             if (arg.HasArgs(1))
  3567.             {
  3568.                 switch (arg.Args[0].ToLower())
  3569.                 {
  3570.                     case "resetseed":
  3571.                         {
  3572.                             duelsData.VictoriesSeed.Clear();
  3573.                             duelsData.LossesSeed.Clear();
  3574.                             duelsData.MatchKillsSeed.Clear();
  3575.                             duelsData.MatchDeathsSeed.Clear();
  3576.                             duelsData.MatchLossesSeed.Clear();
  3577.                             duelsData.MatchVictoriesSeed.Clear();
  3578.                             duelsData.MatchSizesVictoriesSeed.Clear();
  3579.                             duelsData.MatchSizesVictoriesSeed.Clear();
  3580.                             arg.ReplyWith(msg("ResetSeed", arg.Player()?.UserIDString));
  3581.                             break;
  3582.                         }
  3583.                     case "removeall":
  3584.                         {
  3585.                             if (duelingZones.Count > 0)
  3586.                             {
  3587.                                 foreach (var zone in duelingZones.ToList())
  3588.                                 {
  3589.                                     EjectPlayers(zone);
  3590.                                     arg.ReplyWith(msg("RemovedZoneAt", id, zone.Position));
  3591.                                     RemoveDuelZone(zone);
  3592.                                 }
  3593.  
  3594.                                 duelsData.DuelZones.Clear();
  3595.                                 SaveData();
  3596.                             }
  3597.                             else
  3598.                                 arg.ReplyWith(msg("NoZoneExists", id));
  3599.  
  3600.                             break;
  3601.                         }
  3602.                     case "1":
  3603.                     case "enable":
  3604.                     case "on":
  3605.                         {
  3606.                             if (duelsData.DuelsEnabled)
  3607.                             {
  3608.                                 arg.ReplyWith(msg("DuelsEnabledAlready", id));
  3609.                                 return;
  3610.                             }
  3611.  
  3612.                             duelsData.DuelsEnabled = true;
  3613.                             arg.ReplyWith(msg("DuelsNowEnabled", id));
  3614.                             DuelAnnouncement(false);
  3615.                             SaveData();
  3616.                             return;
  3617.                         }
  3618.                     case "0":
  3619.                     case "disable":
  3620.                     case "off":
  3621.                         {
  3622.                             if (!duelsData.DuelsEnabled)
  3623.                             {
  3624.                                 arg.ReplyWith(msg("DuelsDisabledAlready", id));
  3625.                                 return;
  3626.                             }
  3627.  
  3628.                             duelsData.DuelsEnabled = false;
  3629.                             arg.ReplyWith(msg(dataDuelists.Count > 0 ? "DuelsNowDisabled" : "DuelsNowDisabledEmpty", id));
  3630.                             SendDuelistsHome();
  3631.                             SendSpectatorsHome();
  3632.                             SaveData();
  3633.                             return;
  3634.                         }
  3635.                     case "new":
  3636.                         {
  3637.                             if (duelsData.DuelZones.Count >= zoneAmount)
  3638.                             {
  3639.                                 arg.ReplyWith(msg("ZoneLimit", id, zoneAmount));
  3640.                                 return;
  3641.                             }
  3642.  
  3643.                             string zoneName = arg.Args.Length > 1 ? string.Join(" ", arg.Args.Skip(1)) : GetZoneName();
  3644.  
  3645.                             if (SetupDuelZone(null, zoneName) != Vector3.zero)
  3646.                                 arg.ReplyWith(msg("ZoneCreated", id));
  3647.  
  3648.                             return;
  3649.                         }
  3650.                     default:
  3651.                         {
  3652.                             arg.ReplyWith(string.Format("{0} on|off|new|removeall|resetseed", szDuelChatCommand));
  3653.                             break;
  3654.                         }
  3655.                 }
  3656.             }
  3657.             else
  3658.                 arg.ReplyWith(string.Format("{0} on|off|new|removeall|resetseed", szDuelChatCommand));
  3659.         }
  3660.  
  3661.         private void CommandDuelist(IPlayer player, string command, string[] args)
  3662.         {
  3663.             switch (args[0])
  3664.             {
  3665.                 case "kit":
  3666.                     {
  3667.                         string kits = string.Join(", ", VerifiedKits.ToArray());
  3668.  
  3669.                         player.Reply(string.IsNullOrEmpty(kits) ? msg("KitsNotConfigured", player.Id) : "Kits: " + kits);
  3670.                         return;
  3671.                     }
  3672.             }
  3673.         }
  3674.  
  3675.         private void cmdDuel(BasePlayer player, string command, string[] args)
  3676.         {
  3677.             if (AimTrain.CanCall() && Convert.ToBoolean(AimTrain?.Call("IsAimTraining", player.userID)))
  3678.                 return;
  3679.  
  3680.             if (IsEventBanned(player.UserIDString))
  3681.             {
  3682.                 Message(player, msg("Banned", player.UserIDString));
  3683.                 return;
  3684.             }
  3685.  
  3686.             if (dcsBlock.Contains(player.UserIDString))
  3687.             {
  3688.                 Message(player, msg("SuicideBlock", player.UserIDString));
  3689.                 return;
  3690.             }
  3691.  
  3692.             if (args.Length >= 1 && args[0] == "ladder")
  3693.             {
  3694.                 cmdLadder(player, command, args);
  3695.                 return;
  3696.             }
  3697.  
  3698.             if (!duelsData.DuelsEnabled)
  3699.             {
  3700.                 if (!args.Exists(arg => arg.ToLower() == "on"))
  3701.                     Message(player, msg("DuelsDisabled", player.UserIDString));
  3702.  
  3703.                 if (!player.IsAdmin)
  3704.                     return;
  3705.             }
  3706.  
  3707.             bool noZone = duelsData.DuelZones.Count == 0 || duelingZones.Count == 0;
  3708.  
  3709.             if (noZone)
  3710.             {
  3711.                 if (!args.Exists(arg => arg.ToLower() == "new") && !args.Exists(arg => arg.ToLower() == "removeall") && !args.Exists(arg => arg.ToLower() == "custom"))
  3712.                     Message(player, msg("NoZoneExists", player.UserIDString));
  3713.  
  3714.                 if (!player.IsAdmin)
  3715.                     return;
  3716.             }
  3717.  
  3718.             if (!noZone && !duelsData.DuelsEnabled && !args.Exists(arg => arg.ToLower() == "on"))
  3719.                 Message(player, msg("DuelsMustBeEnabled", player.UserIDString, szDuelChatCommand));
  3720.  
  3721.             if (IsDueling(player) && !player.IsAdmin)
  3722.                 return;
  3723.  
  3724.             if (args.Length == 0)
  3725.             {
  3726.                 Message(player, msg("HelpDuels", player.UserIDString, duelsData.TotalDuels.ToString("N0")));
  3727.                 Message(player, msg("ZoneNames", player.UserIDString, duelsData.DuelZones.Count, string.Join(" ", duelsData.DuelZones.Values.ToList().Take(10).ToArray())));
  3728.  
  3729.                 if (!autoAllowAll)
  3730.                     Message(player, msg("HelpAllow", player.UserIDString, szDuelChatCommand));
  3731.  
  3732.                 Message(player, msg("HelpBlock", player.UserIDString, szDuelChatCommand));
  3733.                 Message(player, msg("HelpChallenge", player.UserIDString, szDuelChatCommand));
  3734.                 Message(player, msg("HelpAccept", player.UserIDString, szDuelChatCommand));
  3735.                 Message(player, msg("HelpCancel", player.UserIDString, szDuelChatCommand));
  3736.                 Message(player, msg("HelpChat", player.UserIDString, szDuelChatCommand));
  3737.                 Message(player, msg("HelpQueue", player.UserIDString, szQueueChatCommand));
  3738.                 Message(player, msg("HelpLadder", player.UserIDString, szDuelChatCommand));
  3739.                 Message(player, msg("HelpKit", player.UserIDString, szDuelChatCommand));
  3740.  
  3741.                 if (allowBets)
  3742.                     Message(player, msg("HelpBet", player.UserIDString, szDuelChatCommand));
  3743.  
  3744.                 if (tdmEnabled)
  3745.                     Message(player, msg("HelpTDM", player.UserIDString, szMatchChatCommand));
  3746.  
  3747.                 Message(player, msg("UI_Help", player.UserIDString, szUIChatCommand));
  3748.  
  3749.                 if (player.IsAdmin)
  3750.                 {
  3751.                     Message(player, msg("HelpDuelAdmin", player.UserIDString, szDuelChatCommand));
  3752.                     Message(player, msg("HelpDuelAdminRefundAll", player.UserIDString, szDuelChatCommand));
  3753.                 }
  3754.  
  3755.                 return;
  3756.             }
  3757.  
  3758.             switch (args[0].ToLower())
  3759.             {
  3760.                 case "autoready":
  3761.                     {
  3762.                         ToggleAutoReady(player);
  3763.                         return;
  3764.                     }
  3765.                 case "rematch":
  3766.                 case "ready":
  3767.                     {
  3768.                         ReadyUp(player);
  3769.                         return;
  3770.                     }
  3771.                 case "resetseed":
  3772.                     {
  3773.                         if (player.IsAdmin)
  3774.                         {
  3775.                             duelsData.VictoriesSeed.Clear();
  3776.                             duelsData.LossesSeed.Clear();
  3777.                             duelsData.MatchKillsSeed.Clear();
  3778.                             duelsData.MatchDeathsSeed.Clear();
  3779.                             duelsData.MatchLossesSeed.Clear();
  3780.                             duelsData.MatchVictoriesSeed.Clear();
  3781.                             duelsData.MatchSizesVictoriesSeed.Clear();
  3782.                             duelsData.MatchSizesVictoriesSeed.Clear();
  3783.                             Message(player, msg("ResetSeed", player.UserIDString));
  3784.                             Puts("{0] ({1}): {2}", player.displayName, player.UserIDString, msg("ResetSeed", player.UserIDString));
  3785.                         }
  3786.                         break;
  3787.                     }
  3788.                 case "remove_all_walls":
  3789.                     {
  3790.                         if (player.IsAdmin)
  3791.                         {
  3792.                             int removed = 0;
  3793.  
  3794.                             if (respawnWalls)
  3795.                             {
  3796.                                 Unsubscribe(nameof(OnEntityKill));
  3797.                             }
  3798.  
  3799.                             foreach (var entity in GetWallEntities().ToList())
  3800.                             {
  3801.                                 if (entity.OwnerID > 0 && !entity.OwnerID.IsSteamId())
  3802.                                 {
  3803.                                     entity.Kill();
  3804.                                     removed++;
  3805.                                 }
  3806.                             }
  3807.  
  3808.                             if (respawnWalls)
  3809.                             {
  3810.                                 Subscribe(nameof(OnEntityKill));
  3811.                             }
  3812.  
  3813.                             Message(player, msg("RemovedXWalls", player.UserIDString, removed));
  3814.                         }
  3815.                         break;
  3816.                     }
  3817.                 case "remove_all":
  3818.                     {
  3819.                         if (player.IsAdmin && args.Length == 2 && args[1].All(char.IsDigit))
  3820.                         {
  3821.                             ulong ownerId = ulong.Parse(args[1]);
  3822.                             int removed = 0;
  3823.  
  3824.                             if (respawnWalls)
  3825.                             {
  3826.                                 Unsubscribe(nameof(OnEntityKill));
  3827.                             }
  3828.  
  3829.                             foreach (var entity in GetWallEntities().ToList())
  3830.                             {
  3831.                                 if (entity.OwnerID == ownerId)
  3832.                                 {
  3833.                                     entity.Kill();
  3834.                                     removed++;
  3835.                                 }
  3836.                             }
  3837.  
  3838.                             if (respawnWalls)
  3839.                             {
  3840.                                 Subscribe(nameof(OnEntityKill));
  3841.                             }
  3842.                            
  3843.                             Message(player, msg("RemovedXWalls", player.UserIDString, removed));
  3844.                         }
  3845.                         break;
  3846.                     }
  3847.                 case "0":
  3848.                 case "disable":
  3849.                 case "off":
  3850.                     {
  3851.                         if (player.IsAdmin)
  3852.                         {
  3853.                             if (!duelsData.DuelsEnabled)
  3854.                             {
  3855.                                 Message(player, msg("DuelsDisabledAlready", player.UserIDString));
  3856.                                 return;
  3857.                             }
  3858.  
  3859.                             duelsData.DuelsEnabled = false;
  3860.                             Message(player, msg(dataDuelists.Count > 0 ? "DuelsNowDisabled" : "DuelsNowDisabledEmpty", player.UserIDString));
  3861.                             SendDuelistsHome();
  3862.                             SendSpectatorsHome();
  3863.                             SaveData();
  3864.                         }
  3865.                         break;
  3866.                     }
  3867.                 case "1":
  3868.                 case "enable":
  3869.                 case "on":
  3870.                     {
  3871.                         if (player.IsAdmin)
  3872.                         {
  3873.                             if (duelsData.DuelsEnabled)
  3874.                             {
  3875.                                 Message(player, msg("DuelsEnabledAlready", player.UserIDString));
  3876.                                 return;
  3877.                             }
  3878.  
  3879.                             duelsData.DuelsEnabled = true;
  3880.                             Message(player, msg("DuelsNowEnabled", player.UserIDString));
  3881.                             DuelAnnouncement(false);
  3882.                             SaveData();
  3883.                         }
  3884.                         break;
  3885.                     }
  3886.                 case "custom":
  3887.                 case "me":
  3888.                     {
  3889.                         if (player.IsAdmin)
  3890.                         {
  3891.                             if (duelsData.DuelZones.Count >= zoneAmount)
  3892.                             {
  3893.                                 Message(player, msg("ZoneLimit", player.UserIDString, zoneAmount));
  3894.                                 return;
  3895.                             }
  3896.  
  3897.                             RaycastHit hit;
  3898.                             if (Physics.Raycast(player.eyes.HeadRay(), out hit, Mathf.Infinity, wallMask))
  3899.                             {
  3900.                                 if (DuelTerritory(hit.point, 5f))
  3901.                                 {
  3902.                                     Message(player, msg("ZoneExists", player.UserIDString));
  3903.                                     return;
  3904.                                 }
  3905.  
  3906.                                 string zoneName = args.Length > 1 ? string.Join(" ", args.Skip(1)) : GetZoneName();
  3907.                                 var zone = SetupDuelZone(hit.point, null, zoneName);
  3908.                                 int i = 0;
  3909.  
  3910.                                 foreach (var spawn in zone.Spawns)
  3911.                                     player.SendConsoleCommand("ddraw.text", 30f, Color.yellow, spawn, ++i);
  3912.  
  3913.                                 UpdateStability();
  3914.  
  3915.                                 if (zoneCounter > 0) Message(player, $"Zone will reset after {zoneCounter} duels. Disable by setting `Create New Zone Every X Duels [0 = disabled]` to `0` in the config.");
  3916.                             }
  3917.                             else
  3918.                                 Message(player, msg("FailedRaycast", player.UserIDString));
  3919.                         }
  3920.                         break;
  3921.                     }
  3922.                 case "remove":
  3923.                     {
  3924.                         if (player.IsAdmin)
  3925.                         {
  3926.                             if (duelingZones.Count > 0)
  3927.                             {
  3928.                                 var zone = GetDuelZone(player.transform.position);
  3929.  
  3930.                                 if (zone == null)
  3931.                                 {
  3932.                                     Message(player, msg("NoZoneFound", player.UserIDString));
  3933.                                     return;
  3934.                                 }
  3935.  
  3936.                                 EjectPlayers(zone);
  3937.                                 RemoveDuelZone(zone);
  3938.                                 Message(player, msg("RemovedZone", player.UserIDString));
  3939.                             }
  3940.  
  3941.                         }
  3942.                         break;
  3943.                     }
  3944.                 case "removeall":
  3945.                     {
  3946.                         if (player.IsAdmin)
  3947.                         {
  3948.                             if (duelingZones.Count > 0)
  3949.                             {
  3950.                                 foreach (var zone in duelingZones.ToList())
  3951.                                 {
  3952.                                     EjectPlayers(zone);
  3953.                                     Message(player, msg("RemovedZoneAt", player.UserIDString, zone.Position));
  3954.                                     RemoveDuelZone(zone);
  3955.                                 }
  3956.  
  3957.                                 duelsData.DuelZones.Clear();
  3958.                                 SaveData();
  3959.                             }
  3960.                             else
  3961.                                 Message(player, msg("NoZoneExists", player.UserIDString));
  3962.                         }
  3963.                         break;
  3964.                     }
  3965.                 case "spawns":
  3966.                     {
  3967.                         if (player.IsAdmin)
  3968.                         {
  3969.                             if (args.Length >= 2)
  3970.                             {
  3971.                                 switch (args[1].ToLower())
  3972.                                 {
  3973.                                     case "add":
  3974.                                         AddSpawnPoint(player, true);
  3975.                                         break;
  3976.                                     case "here":
  3977.                                         AddSpawnPoint(player, false);
  3978.                                         break;
  3979.                                     case "remove":
  3980.                                         RemoveSpawnPoint(player);
  3981.                                         break;
  3982.                                     case "removeall":
  3983.                                         RemoveSpawnPoints(player);
  3984.                                         break;
  3985.                                     case "wipe":
  3986.                                         WipeSpawnPoints(player);
  3987.                                         break;
  3988.                                     default:
  3989.                                         SendSpawnHelp(player);
  3990.                                         break;
  3991.                                 }
  3992.  
  3993.                                 return;
  3994.                             }
  3995.  
  3996.                             SendSpawnHelp(player);
  3997.  
  3998.                             int i = 0;
  3999.                             float dist = float.MaxValue;
  4000.                             DuelingZone destZone = null;
  4001.  
  4002.                             foreach (var zone in duelingZones)
  4003.                             {
  4004.                                 if (zone.Distance(player.transform.position) > zoneRadius + 200f)
  4005.                                     continue;
  4006.  
  4007.                                 float distance = zone.Distance(player.transform.position);
  4008.  
  4009.                                 if (distance < dist)
  4010.                                 {
  4011.                                     dist = distance;
  4012.                                     destZone = zone;
  4013.                                 }
  4014.                             }
  4015.  
  4016.                             if (destZone != null)
  4017.                                 foreach (var spawn in destZone.Spawns)
  4018.                                     player.SendConsoleCommand("ddraw.text", 30f, Color.yellow, spawn, ++i);
  4019.                         }
  4020.                         break;
  4021.                     }
  4022.                 case "rename":
  4023.                     {
  4024.                         if (player.IsAdmin)
  4025.                         {
  4026.                             if (args.Length > 1)
  4027.                             {
  4028.                                 var zone = GetDuelZone(player.transform.position);
  4029.  
  4030.                                 if (zone == null)
  4031.                                 {
  4032.                                     Message(player, msg("NoZoneFound", player.UserIDString));
  4033.                                     return;
  4034.                                 }
  4035.  
  4036.                                 string zoneName = string.Join(" ", args.Skip(1));
  4037.                                 duelsData.DuelZones[zone.Position.ToString()] = zoneName;
  4038.                                 Message(player, msg("ZoneRenamed", player.UserIDString, zoneName));
  4039.                             }
  4040.                             else
  4041.                                 Message(player, msg("ZoneRename", player.UserIDString, szDuelChatCommand));
  4042.  
  4043.                             return;
  4044.                         }
  4045.                         break;
  4046.                     }
  4047.                 case "new":
  4048.                     {
  4049.                         if (player.IsAdmin)
  4050.                         {
  4051.                             if (duelsData.DuelZones.Count >= zoneAmount)
  4052.                             {
  4053.                                 Message(player, msg("ZoneLimit", player.UserIDString, zoneAmount));
  4054.                                 return;
  4055.                             }
  4056.  
  4057.                             string zoneName = GetZoneName();
  4058.                             var _nameArgs = args.Where(arg => arg.ToLower() != "tp");
  4059.  
  4060.                             if (_nameArgs.ToList().Count > 0)
  4061.                                 zoneName = string.Join(" ", _nameArgs);
  4062.  
  4063.                             var zonePos = SetupDuelZone(null, zoneName);
  4064.  
  4065.                             if (zonePos != Vector3.zero)
  4066.                             {
  4067.                                 Message(player, msg("ZoneCreated", player.UserIDString));
  4068.  
  4069.                                 if (args.Exists(arg => arg.ToLower() == "tp"))
  4070.                                 {
  4071.                                     Player.Teleport(player, zonePos);
  4072.                                 }
  4073.                             }
  4074.  
  4075.                         }
  4076.                         break;
  4077.                     }
  4078.                 case "tpm":
  4079.                     {
  4080.                         if (player.IsAdmin)
  4081.                         {
  4082.                             float dist = float.MaxValue;
  4083.                             var dest = Vector3.zero;
  4084.                             var matches = tdmMatches.Exists(m => m.IsStarted) ? tdmMatches.Where(m => m.IsStarted).ToList() : tdmMatches.ToList(); // 0.1.17 if multiple zones then choose from active ones if any exist
  4085.  
  4086.                             foreach (var match in matches)
  4087.                             {
  4088.                                 float distance = match.Zone.Distance(player.transform.position);
  4089.  
  4090.                                 if (matches.Count > 1 && distance < zoneRadius * 4f) // move admin to the next nearest zone
  4091.                                     continue;
  4092.  
  4093.                                 if (distance < dist)
  4094.                                 {
  4095.                                     dist = distance;
  4096.                                     dest = match.Zone.Position;
  4097.                                 }
  4098.                             }
  4099.  
  4100.                             if (dest != Vector3.zero)
  4101.                                 Player.Teleport(player, dest);
  4102.                         }
  4103.                         break;
  4104.                     }
  4105.                 case "tp":
  4106.                     {
  4107.                         if (player.IsAdmin)
  4108.                         {
  4109.                             float dist = float.MaxValue;
  4110.                             var dest = Vector3.zero;
  4111.                             var zones = duelingZones.Count > 3 && duelingZones.Exists(zone => zone.TotalPlayers > 0) ? duelingZones.Where(zone => zone.TotalPlayers > 0).ToList() : duelingZones; // 0.1.17 if multiple zones then choose from active ones if any exist
  4112.  
  4113.                             foreach (var zone in zones)
  4114.                             {
  4115.                                 float distance = zone.Distance(player.transform.position);
  4116.  
  4117.                                 if (zones.Count > 1 && distance < zoneRadius * 4f) // move admin to the next nearest zone
  4118.                                     continue;
  4119.  
  4120.                                 if (distance < dist)
  4121.                                 {
  4122.                                     dist = distance;
  4123.                                     dest = zone.Position;
  4124.                                 }
  4125.                             }
  4126.  
  4127.                             if (dest != Vector3.zero)
  4128.                                 Player.Teleport(player, dest);
  4129.                         }
  4130.                         break;
  4131.                     }
  4132.                 case "save":
  4133.                     {
  4134.                         if (player.IsAdmin)
  4135.                         {
  4136.                             SaveData();
  4137.                             Message(player, msg("DataSaved", player.UserIDString));
  4138.                         }
  4139.                     }
  4140.                     break;
  4141.                 case "ban":
  4142.                     {
  4143.                         if (player.IsAdmin && args.Length >= 2)
  4144.                         {
  4145.                             string targetId = args[1].IsSteamId() ? args[1] : BasePlayer.Find(args[1])?.UserIDString ?? null;
  4146.  
  4147.                             if (string.IsNullOrEmpty(targetId))
  4148.                             {
  4149.                                 Message(player, msg("PlayerNotFound", player.UserIDString, args[1]));
  4150.                                 return;
  4151.                             }
  4152.  
  4153.                             if (!duelsData.Bans.ContainsKey(targetId))
  4154.                             {
  4155.                                 duelsData.Bans.Add(targetId, player.UserIDString);
  4156.                                 Message(player, msg("AddedBan", player.UserIDString, targetId));
  4157.                             }
  4158.                             else
  4159.                             {
  4160.                                 duelsData.Bans.Remove(targetId);
  4161.                                 Message(player, msg("RemovedBan", player.UserIDString, targetId));
  4162.                             }
  4163.  
  4164.                             SaveData();
  4165.                         }
  4166.                         break;
  4167.                     }
  4168.                 case "announce":
  4169.                     {
  4170.                         if (player.IsAdmin)
  4171.                             DuelAnnouncement(true);
  4172.  
  4173.                         break;
  4174.                     }
  4175.                 case "claim":
  4176.                     {
  4177.                         if (!duelsData.ClaimBets.ContainsKey(player.UserIDString))
  4178.                         {
  4179.                             Message(player, msg("NoBetsToClaim", player.UserIDString));
  4180.                             return;
  4181.                         }
  4182.  
  4183.                         foreach (var bet in duelsData.ClaimBets[player.UserIDString].ToList())
  4184.                         {
  4185.                             var item = ItemManager.CreateByItemID(bet.itemid, bet.amount);
  4186.  
  4187.                             if (item == null)
  4188.                             {
  4189.                                 continue;
  4190.                             }
  4191.  
  4192.                             if (!item.MoveToContainer(player.inventory.containerMain, -1))
  4193.                             {
  4194.                                 var position = player.transform.position;
  4195.                                 item.Drop(position + new Vector3(0f, 1f, 0f) + position / 2f, (position + new Vector3(0f, 0.2f, 0f)) * 8f); // Credit: Slack comment by @visagalis
  4196.                             }
  4197.  
  4198.                             string message = msg("PlayerClaimedBet", player.UserIDString, item.info.displayName.translated, item.amount);
  4199.  
  4200.                             Message(player, message);
  4201.                             Puts("{0} ({1}) - {2}", player.displayName, player.UserIDString, message);
  4202.                             duelsData.ClaimBets[player.UserIDString].Remove(bet);
  4203.  
  4204.                             if (duelsData.ClaimBets[player.UserIDString].Count == 0)
  4205.                             {
  4206.                                 duelsData.ClaimBets.Remove(player.UserIDString);
  4207.                                 Message(player, msg("AllBetsClaimed", player.UserIDString));
  4208.                             }
  4209.                         }
  4210.                         return;
  4211.                     }
  4212.                 case "queue":
  4213.                 case "que":
  4214.                 case "q":
  4215.                     {
  4216.                         if (!string.IsNullOrEmpty(szQueueChatCommand))
  4217.                             cmdQueue(player, command, args);
  4218.                         return;
  4219.                     }
  4220.                 case "chat":
  4221.                     {
  4222.                         if (broadcastDefeat)
  4223.                         {
  4224.                             if (!duelsData.Chat.Contains(player.UserIDString))
  4225.                                 duelsData.Chat.Add(player.UserIDString);
  4226.                             else
  4227.                                 duelsData.Chat.Remove(player.UserIDString);
  4228.  
  4229.                             Message(player, msg(duelsData.Chat.Contains(player.UserIDString) ? "DuelChatOff" : "DuelChatOn", player.UserIDString));
  4230.                         }
  4231.                         else
  4232.                         {
  4233.                             if (!duelsData.ChatEx.Contains(player.UserIDString))
  4234.                                 duelsData.ChatEx.Add(player.UserIDString);
  4235.                             else
  4236.                                 duelsData.ChatEx.Remove(player.UserIDString);
  4237.  
  4238.                             Message(player, msg(duelsData.ChatEx.Contains(player.UserIDString) ? "DuelChatOn" : "DuelChatOff", player.UserIDString));
  4239.                         }
  4240.                         return;
  4241.                     }
  4242.                 case "kit":
  4243.                     {
  4244.                         string kits = string.Join(", ", VerifiedKits.ToArray());
  4245.  
  4246.                         if (args.Length == 2 && !string.IsNullOrEmpty(kits))
  4247.                         {
  4248.                             string kit = GetVerifiedKit(args[1]);
  4249.  
  4250.                             if (!string.IsNullOrEmpty(kit))
  4251.                             {
  4252.                                 duelsData.CustomKits[player.UserIDString] = kit;
  4253.                                 Message(player, msg("KitSet", player.UserIDString, kit));
  4254.                             }
  4255.                             else
  4256.                                 Message(player, msg("KitDoesntExist", player.UserIDString, args[1]));
  4257.  
  4258.                             return;
  4259.                         }
  4260.  
  4261.                         if (duelsData.CustomKits.ContainsKey(player.UserIDString))
  4262.                         {
  4263.                             duelsData.CustomKits.Remove(player.UserIDString);
  4264.                             Message(player, msg("ResetKit", player.UserIDString));
  4265.                         }
  4266.  
  4267.                         Message(player, string.IsNullOrEmpty(kits) ? msg("KitsNotConfigured", player.UserIDString) : "Kits: " + kits);
  4268.                         return;
  4269.                     }
  4270.                 case "allow":
  4271.                     {
  4272.                         if (!duelsData.Allowed.Contains(player.UserIDString))
  4273.                         {
  4274.                             duelsData.Allowed.Add(player.UserIDString);
  4275.                             Message(player, msg("PlayerRequestsOn", player.UserIDString));
  4276.                             return;
  4277.                         }
  4278.  
  4279.                         duelsData.Allowed.Remove(player.UserIDString);
  4280.                         Message(player, msg("PlayerRequestsOff", player.UserIDString));
  4281.                         RemoveRequests(player);
  4282.                         return;
  4283.                     }
  4284.                 case "block":
  4285.                     {
  4286.                         if (args.Length >= 2)
  4287.                         {
  4288.                             var target = BasePlayer.Find(args[1]);
  4289.  
  4290.                             if (!target)
  4291.                             {
  4292.                                 Message(player, msg("PlayerNotFound", player.UserIDString, args[1]));
  4293.                                 return;
  4294.                             }
  4295.  
  4296.                             if (!duelsData.BlockedUsers.ContainsKey(player.UserIDString))
  4297.                             {
  4298.                                 duelsData.BlockedUsers.Add(player.UserIDString, new List<string>());
  4299.                             }
  4300.                             else if (duelsData.BlockedUsers[player.UserIDString].Contains(target.UserIDString))
  4301.                             {
  4302.                                 duelsData.BlockedUsers[player.UserIDString].Remove(target.UserIDString);
  4303.  
  4304.                                 if (duelsData.BlockedUsers[player.UserIDString].Count == 0)
  4305.                                     duelsData.BlockedUsers.Remove(player.UserIDString);
  4306.  
  4307.                                 Message(player, msg("UnblockedRequestsFrom", player.UserIDString, target.displayName));
  4308.                                 return;
  4309.                             }
  4310.  
  4311.                             duelsData.BlockedUsers[player.UserIDString].Add(target.UserIDString);
  4312.                             Message(player, msg("BlockedRequestsFrom", player.UserIDString, target.displayName));
  4313.                             return;
  4314.                         }
  4315.  
  4316.                         if (duelsData.Allowed.Contains(player.UserIDString))
  4317.                         {
  4318.                             duelsData.Allowed.Remove(player.UserIDString);
  4319.                             Message(player, msg("PlayerRequestsOff", player.UserIDString));
  4320.                             RemoveRequests(player);
  4321.                             return;
  4322.                         }
  4323.  
  4324.                         Message(player, msg("AlreadyBlocked", player.UserIDString));
  4325.                         return;
  4326.                     }
  4327.                 case "bet":
  4328.                     {
  4329.                         if (!allowBets)
  4330.                         {
  4331.                             Message(player, "Betting is disabled.");
  4332.                             break;
  4333.                         }
  4334.  
  4335.                         if (duelingBets.Count == 0)
  4336.                         {
  4337.                             Message(player, msg("NoBetsConfigured", player.UserIDString));
  4338.                             return;
  4339.                         }
  4340.  
  4341.                         if (args.Length == 2)
  4342.                         {
  4343.                             switch (args[1].ToLower())
  4344.                             {
  4345.                                 case "refundall":
  4346.                                     {
  4347.                                         if (player.IsAdmin)
  4348.                                         {
  4349.                                             if (duelsData.Bets.Count == 0)
  4350.                                             {
  4351.                                                 Message(player, msg("NoBetsToRefund", player.UserIDString));
  4352.                                                 return;
  4353.                                             }
  4354.  
  4355.                                             foreach (var kvp in duelsData.Bets.ToList())
  4356.                                             {
  4357.                                                 var target = BasePlayer.Find(kvp.Key);
  4358.                                                 if (target == null) continue;
  4359.  
  4360.                                                 Item item = ItemManager.CreateByItemID(kvp.Value.itemid, kvp.Value.amount);
  4361.  
  4362.                                                 if (item == null)
  4363.                                                     continue;
  4364.  
  4365.                                                 if (!item.MoveToContainer(target.inventory.containerMain, -1, true) && !item.MoveToContainer(target.inventory.containerBelt, -1, true))
  4366.                                                 {
  4367.                                                     item.Remove(0.01f);
  4368.                                                     continue;
  4369.                                                 }
  4370.  
  4371.                                                 Message(target, msg("RefundAllPlayerNotice", target.UserIDString, item.info.displayName.translated, item.amount));
  4372.                                                 Message(player, msg("RefundAllAdminNotice", player.UserIDString, target.displayName, target.UserIDString, item.info.displayName.english, item.amount));
  4373.                                                 duelsData.Bets.Remove(kvp.Key);
  4374.                                             }
  4375.  
  4376.                                             if (duelsData.Bets.Count > 0) Message(player, msg("BetsRemaining", player.UserIDString, duelsData.Bets.Count));
  4377.                                             else Message(player, msg("AllBetsRefunded", player.UserIDString));
  4378.                                             SaveData();
  4379.                                             return;
  4380.                                         }
  4381.  
  4382.                                         break;
  4383.                                     }
  4384.                                 case "forfeit":
  4385.                                     {
  4386.                                         if (allowBetRefund) // prevent operator error ;)
  4387.                                         {
  4388.                                             cmdDuel(player, command, new[] { "bet", "refund" });
  4389.                                             return;
  4390.                                         }
  4391.  
  4392.                                         if (!allowBetForfeit)
  4393.                                         {
  4394.                                             Message(player, msg("CannotForfeit", player.UserIDString));
  4395.                                             return;
  4396.                                         }
  4397.  
  4398.                                         if (duelsData.Bets.ContainsKey(player.UserIDString))
  4399.                                         {
  4400.                                             if (dataRequests.ContainsKey(player.UserIDString) || dataRequests.ContainsValue(player.UserIDString))
  4401.                                             {
  4402.                                                 Message(player, msg("CannotForfeitRequestDuel", player.UserIDString));
  4403.                                                 return;
  4404.                                             }
  4405.  
  4406.                                             if (dataDuelists.ContainsKey(player.UserIDString))
  4407.                                             {
  4408.                                                 Message(player, msg("CannotForfeitInDuel", player.UserIDString));
  4409.                                                 return;
  4410.                                             }
  4411.  
  4412.                                             duelsData.Bets.Remove(player.UserIDString);
  4413.                                             Message(player, msg("BetForfeit", player.UserIDString));
  4414.                                             SaveData();
  4415.                                         }
  4416.                                         else
  4417.                                             Message(player, msg("NoBetToForfeit", player.UserIDString));
  4418.  
  4419.                                         return;
  4420.                                     }
  4421.                                 case "cancel":
  4422.                                 case "refund":
  4423.                                     {
  4424.                                         if (!allowBetRefund && !player.IsAdmin)
  4425.                                         {
  4426.                                             Message(player, msg("CannotRefund", player.UserIDString));
  4427.                                             return;
  4428.                                         }
  4429.  
  4430.                                         if (duelsData.Bets.ContainsKey(player.UserIDString))
  4431.                                         {
  4432.                                             if (dataRequests.ContainsKey(player.UserIDString) || dataRequests.ContainsValue(player.UserIDString))
  4433.                                             {
  4434.                                                 Message(player, msg("CannotRefundRequestDuel", player.UserIDString));
  4435.                                                 return;
  4436.                                             }
  4437.  
  4438.                                             if (dataDuelists.ContainsKey(player.UserIDString))
  4439.                                             {
  4440.                                                 Message(player, msg("CannotRefundInDuel", player.UserIDString));
  4441.                                                 return;
  4442.                                             }
  4443.  
  4444.                                             var bet = duelsData.Bets[player.UserIDString];
  4445.  
  4446.                                             Item item = ItemManager.CreateByItemID(bet.itemid, bet.amount);
  4447.  
  4448.                                             if (!item.MoveToContainer(player.inventory.containerMain, -1, true))
  4449.                                             {
  4450.                                                 if (!item.MoveToContainer(player.inventory.containerBelt, -1, true))
  4451.                                                 {
  4452.                                                     var position = player.transform.position;
  4453.                                                     item.Drop(position + new Vector3(0f, 1f, 0f) + position / 2f, (position + new Vector3(0f, 0.2f, 0f)) * 8f); // Credit: Slack comment by @visagalis
  4454.                                                 }
  4455.                                             }
  4456.  
  4457.                                             duelsData.Bets.Remove(player.UserIDString);
  4458.                                             Message(player, msg("BetRefunded", player.UserIDString));
  4459.                                             SaveData();
  4460.                                         }
  4461.                                         else
  4462.                                             Message(player, msg("NoBetToRefund", player.UserIDString));
  4463.  
  4464.                                         return;
  4465.                                     }
  4466.                                 default:
  4467.                                     break;
  4468.                             }
  4469.                         }
  4470.  
  4471.                         if (duelsData.Bets.ContainsKey(player.UserIDString))
  4472.                         {
  4473.                             var bet = duelsData.Bets[player.UserIDString];
  4474.  
  4475.                             Message(player, msg("AlreadyBetting", player.UserIDString, bet.trigger, bet.amount));
  4476.  
  4477.                             if (allowBetRefund)
  4478.                                 Message(player, msg("ToRefundUse", player.UserIDString, szDuelChatCommand));
  4479.                             else if (allowBetForfeit)
  4480.                                 Message(player, msg("ToForfeitUse", player.UserIDString, szDuelChatCommand));
  4481.  
  4482.                             return;
  4483.                         }
  4484.  
  4485.                         if (args.Length < 3)
  4486.                         {
  4487.                             Message(player, msg("AvailableBets", player.UserIDString));
  4488.  
  4489.                             foreach (var betInfo in duelingBets)
  4490.                                 Message(player, string.Format("{0} (max: {1})", betInfo.trigger, betInfo.max));
  4491.  
  4492.                             Message(player, msg("BetSyntax", player.UserIDString, szDuelChatCommand));
  4493.                             return;
  4494.                         }
  4495.  
  4496.                         int betAmount;
  4497.                         if (!int.TryParse(args[2], out betAmount))
  4498.                         {
  4499.                             Message(player, msg("InvalidNumber", player.UserIDString, args[2]));
  4500.                             return;
  4501.                         }
  4502.  
  4503.                         if (betAmount > 500 && betAmount % 500 != 0)
  4504.                         {
  4505.                             Message(player, msg("MultiplesOnly", player.UserIDString));
  4506.                             return;
  4507.                         }
  4508.  
  4509.                         foreach (var betInfo in duelingBets)
  4510.                         {
  4511.                             if (betInfo.trigger.ToLower() == args[1].ToLower())
  4512.                             {
  4513.                                 CreateBet(player, betAmount, betInfo);
  4514.                                 return;
  4515.                             }
  4516.                         }
  4517.  
  4518.                         Message(player, msg("InvalidBet", player.UserIDString, args[1]));
  4519.                         return;
  4520.                     }
  4521.                 case "accept":
  4522.                 case "a":
  4523.                 case "y":
  4524.                 case "yes":
  4525.                     {
  4526.                         if (!autoAllowAll && !duelsData.Allowed.Contains(player.UserIDString))
  4527.                         {
  4528.                             Message(player, msg("MustAllowDuels", player.UserIDString, szDuelChatCommand));
  4529.                             return;
  4530.                         }
  4531.  
  4532.                         if (InEvent(player))
  4533.                         {
  4534.                             Message(player, msg("AlreadyDueling", player.UserIDString));
  4535.                             return;
  4536.                         }
  4537.  
  4538.                         if (!dataRequests.ContainsValue(player.UserIDString))
  4539.                         {
  4540.                             Message(player, msg("NoRequestsReceived", player.UserIDString));
  4541.                             return;
  4542.                         }
  4543.  
  4544.                         if (!IsNewman(player))
  4545.                         {
  4546.                             Message(player, msg("MustBeNaked", player.UserIDString));
  4547.                             return;
  4548.                         }
  4549.  
  4550.                         BasePlayer target = null;
  4551.  
  4552.                         foreach (var kvp in dataRequests)
  4553.                         {
  4554.                             if (kvp.Value == player.UserIDString)
  4555.                             {
  4556.                                 target = BasePlayer.Find(kvp.Key);
  4557.  
  4558.                                 if (target == null || !target.IsConnected)
  4559.                                 {
  4560.                                     Message(player, string.Format("DuelCancelledFor", player.UserIDString, GetDisplayName(kvp.Key)));
  4561.                                     dataRequests.Remove(kvp.Key);
  4562.                                     return;
  4563.                                 }
  4564.  
  4565.                                 break;
  4566.                             }
  4567.                         }
  4568.  
  4569.                         if (!IsNewman(target))
  4570.                         {
  4571.                             Message(player, msg("TargetMustBeNaked", player.UserIDString));
  4572.                             Message(target, msg("MustBeNaked", target.UserIDString));
  4573.                             return;
  4574.                         }
  4575.  
  4576.                         duelsData.Restricted.Remove(player.UserIDString);
  4577.                         duelsData.Restricted.Remove(target.UserIDString);
  4578.  
  4579.                         if (!SelectZone(player, target))
  4580.                         {
  4581.                             Message(player, msg("AllZonesFull", player.UserIDString, duelingZones.Count, playersPerZone));
  4582.                             Message(target, msg("AllZonesFull", target.UserIDString, duelingZones.Count, playersPerZone));
  4583.                         }
  4584.  
  4585.                         return;
  4586.                     }
  4587.                 case "cancel":
  4588.                 case "decline":
  4589.                     {
  4590.                         if (IsDueling(player))
  4591.                             return;
  4592.  
  4593.                         if (!autoAllowAll && !duelsData.Allowed.Contains(player.UserIDString))
  4594.                         {
  4595.                             Message(player, msg("MustAllowDuels", player.UserIDString, szDuelChatCommand));
  4596.                             return;
  4597.                         }
  4598.  
  4599.                         var entry = dataRequests.FirstOrDefault(kvp => dataRequests.ContainsKey(player.UserIDString) ? kvp.Key == player.UserIDString : kvp.Value == player.UserIDString);
  4600.  
  4601.                         if (!string.IsNullOrEmpty(entry.Key))
  4602.                         {
  4603.                             var target = BasePlayer.Find(entry.Key) ?? BasePlayer.Find(entry.Value);
  4604.  
  4605.                             if (target != null)
  4606.                                 Message(target, msg("DuelCancelledWith", target.UserIDString, player.displayName));
  4607.  
  4608.                             Message(player, msg("DuelCancelComplete", player.UserIDString));
  4609.                             dataRequests.Remove(entry.Key);
  4610.                             return;
  4611.                         }
  4612.  
  4613.                         Message(player, msg("NoPendingRequests", player.UserIDString));
  4614.                         return;
  4615.                     }
  4616.                 default:
  4617.                     {
  4618.                         if (!autoAllowAll && !duelsData.Allowed.Contains(player.UserIDString))
  4619.                         {
  4620.                             Message(player, msg("MustAllowDuels", player.UserIDString, szDuelChatCommand));
  4621.                             return;
  4622.                         }
  4623.  
  4624.                         if (IsDueling(player))
  4625.                         {
  4626.                             Message(player, msg("AlreadyDueling", player.UserIDString));
  4627.                             return;
  4628.                         }
  4629.  
  4630.                         if (duelsData.Restricted.Contains(player.UserIDString) && !player.IsAdmin)
  4631.                         {
  4632.                             Message(player, msg("MustWaitToRequestAgain", player.UserIDString, 1));
  4633.                             return;
  4634.                         }
  4635.  
  4636.                         if (!IsNewman(player))
  4637.                         {
  4638.                             Message(player, msg("MustBeNaked", player.UserIDString));
  4639.                             return;
  4640.                         }
  4641.  
  4642.                         var target = BasePlayer.Find(args[0]);
  4643.  
  4644.                         if (target == null || target == player) //if (target == null || (target == player && target.userID != 76561198212544308))
  4645.                         {
  4646.                             Message(player, msg("PlayerNotFound", player.UserIDString, args[0]));
  4647.                             return;
  4648.                         }
  4649.  
  4650.                         if (duelsData.BlockedUsers.ContainsKey(target.UserIDString) && duelsData.BlockedUsers[target.UserIDString].Contains(player.UserIDString))
  4651.                         {
  4652.                             Message(player, msg("CannotRequestThisPlayer", player.UserIDString));
  4653.                             return;
  4654.                         }
  4655.  
  4656.                         if (IsDueling(target))
  4657.                         {
  4658.                             Message(player, msg("TargetAlreadyDueling", player.UserIDString, target.displayName));
  4659.                             return;
  4660.                         }
  4661.  
  4662.                         if (!autoAllowAll && !duelsData.Allowed.Contains(target.UserIDString))
  4663.                         {
  4664.                             Message(player, msg("NotAllowedYet", player.UserIDString, target.displayName, szDuelChatCommand));
  4665.                             return;
  4666.                         }
  4667.  
  4668.                         if (dataRequests.ContainsKey(player.UserIDString))
  4669.                         {
  4670.                             Message(player, msg("MustWaitForAccept", player.UserIDString, GetDisplayName(dataRequests[player.UserIDString])));
  4671.                             return;
  4672.                         }
  4673.  
  4674.                         if (dataRequests.ContainsValue(target.UserIDString))
  4675.                         {
  4676.                             Message(player, msg("PendingRequestAlready", player.UserIDString));
  4677.                             return;
  4678.                         }
  4679.  
  4680.                         if (duelsData.Bets.ContainsKey(player.UserIDString) && !duelsData.Bets.ContainsKey(target.UserIDString))
  4681.                         {
  4682.                             var bet = duelsData.Bets[player.UserIDString];
  4683.  
  4684.                             Message(player, msg("TargetHasNoBet", player.UserIDString, target.displayName));
  4685.                             Message(player, msg("YourBet", player.UserIDString, bet.trigger, bet.amount));
  4686.                             return;
  4687.                         }
  4688.  
  4689.                         if (duelsData.Bets.ContainsKey(target.UserIDString) && !duelsData.Bets.ContainsKey(player.UserIDString))
  4690.                         {
  4691.                             var targetBet = duelsData.Bets[target.UserIDString];
  4692.                             Message(player, msg("MustHaveSameBet", player.UserIDString, target.displayName, targetBet.trigger, targetBet.amount));
  4693.                             return;
  4694.                         }
  4695.  
  4696.                         if (duelsData.Bets.ContainsKey(player.UserIDString) && duelsData.Bets.ContainsKey(target.UserIDString))
  4697.                         {
  4698.                             var playerBet = duelsData.Bets[player.UserIDString];
  4699.                             var targetBet = duelsData.Bets[target.UserIDString];
  4700.  
  4701.                             if (!playerBet.Equals(targetBet))
  4702.                             {
  4703.                                 Message(player, msg("BetsDoNotMatch", player.UserIDString, playerBet.trigger, playerBet.amount, targetBet.trigger, targetBet.amount));
  4704.                                 return;
  4705.                             }
  4706.                         }
  4707.  
  4708.                         if (args.Length > 1)
  4709.                             SetPlayerZone(player, args.Skip(1));
  4710.  
  4711.                         dataRequests.Add(player.UserIDString, target.UserIDString);
  4712.                         Message(target, msg("DuelRequestReceived", target.UserIDString, player.displayName, szDuelChatCommand));
  4713.                         Message(player, msg("DuelRequestSent", player.UserIDString, target.displayName, szDuelChatCommand));
  4714.  
  4715.                         if (RemoveFromQueue(player.UserIDString))
  4716.                             Message(player, msg("RemovedFromQueueRequest", player.UserIDString));
  4717.  
  4718.                         string targetName = target.displayName;
  4719.                         string playerId = player.UserIDString;
  4720.  
  4721.                         if (!duelsData.Restricted.Contains(playerId))
  4722.                             duelsData.Restricted.Add(playerId);
  4723.  
  4724.                         timer.In(60f, () =>
  4725.                         {
  4726.                             duelsData.Restricted.Remove(playerId);
  4727.  
  4728.                             if (dataRequests.ContainsKey(playerId))
  4729.                             {
  4730.                                 if (player != null && !IsDueling(player))
  4731.                                     Message(player, msg("RequestTimedOut", playerId, targetName));
  4732.  
  4733.                                 dataRequests.Remove(playerId);
  4734.                             }
  4735.                         });
  4736.  
  4737.                         break;
  4738.                     }
  4739.             } // end switch
  4740.         }
  4741.  
  4742.         public DuelingZone GetPlayerZone(BasePlayer player, int size)
  4743.         {
  4744.             if (playerZones.ContainsKey(player.UserIDString))
  4745.             {
  4746.                 var kvp = duelsData.DuelZones.FirstOrDefault(entry => entry.Value.Equals(playerZones[player.UserIDString], StringComparison.OrdinalIgnoreCase));
  4747.  
  4748.                 playerZones.Remove(player.UserIDString);
  4749.  
  4750.                 if (!string.IsNullOrEmpty(kvp.Key))
  4751.                 {
  4752.                     var zone = duelingZones.FirstOrDefault(x => x.Position.ToString() == kvp.Key);
  4753.  
  4754.                     if (size > 2)
  4755.                         return zone == null || zone.IsLocked || zone.Spawns.Count < (requireTeamSize ? size * 2 : 2) ? null : zone;
  4756.  
  4757.                     return zone == null || zone.IsLocked || zone.Spawns.Count < requiredMinSpawns || zone.Spawns.Count > requiredMaxSpawns ? null : zone;
  4758.                 }
  4759.             }
  4760.  
  4761.             return null;
  4762.         }
  4763.  
  4764.         private List<ulong> _times = new List<ulong>();
  4765.  
  4766.         public void SetPlayerTime(BasePlayer player, bool set)
  4767.         {
  4768.             if (!setPlayerTime || !set && !_times.Remove(player.userID))
  4769.             {
  4770.                 return;
  4771.             }
  4772.  
  4773.             var time = set ? "12" : "-1";
  4774.  
  4775.             ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"setenv {player.UserIDString} time {time}");
  4776.  
  4777.             if (set) _times.Add(player.userID);
  4778.         }
  4779.  
  4780.         public bool SetPlayerZone(BasePlayer player, string[] args)
  4781.         {
  4782.             foreach (string arg in args)
  4783.             {
  4784.                 if (duelsData.DuelZones.Values.Exists(zoneName => zoneName.Equals(arg, StringComparison.OrdinalIgnoreCase)))
  4785.                 {
  4786.                     string zoneName = duelsData.DuelZones.Values.FirstOrDefault(x => x.Equals(arg, StringComparison.OrdinalIgnoreCase));
  4787.                     Message(player, msg("ZoneSet", player.UserIDString, zoneName));
  4788.                     playerZones[player.UserIDString] = zoneName;
  4789.                     return true;
  4790.                 }
  4791.             }
  4792.  
  4793.             return false;
  4794.         }
  4795.  
  4796.         public void SetupTeams(BasePlayer player, BasePlayer target)
  4797.         {
  4798.             if (!IsNewman(player))
  4799.             {
  4800.                 Message(player, msg("MustBeNaked", player.UserIDString));
  4801.                 Message(target, msg("DuelMustBeNaked", target.UserIDString, player.displayName));
  4802.                 return;
  4803.             }
  4804.  
  4805.             if (!IsNewman(target))
  4806.             {
  4807.                 Message(target, msg("MustBeNaked", target.UserIDString));
  4808.                 Message(player, msg("DuelMustBeNaked", player.UserIDString, target.displayName));
  4809.                 return;
  4810.             }
  4811.  
  4812.             RemoveFromQueue(player.UserIDString);
  4813.             RemoveFromQueue(target.UserIDString);
  4814.  
  4815.             var match = new GoodVersusEvilMatch(this);
  4816.             match.Setup(player, target);
  4817.  
  4818.             SubscribeHooks(true);
  4819.         }
  4820.  
  4821.         private void ResetTemporaryData() // keep our datafile cleaned up by removing entries which are temporary
  4822.         {
  4823.             if (duelsData == null)
  4824.                 duelsData = new StoredData();
  4825.  
  4826.             dataDuelists.Clear();
  4827.             dataRequests.Clear();
  4828.             dataImmunity.Clear();
  4829.             dataImmunitySpawns.Clear();
  4830.             duelsData.Restricted.Clear();
  4831.             dataDeath.Clear();
  4832.             duelsData.Queued.Clear();
  4833.             duelsData.Homes.Clear();
  4834.             duelsData.Kits.Clear();
  4835.             SaveData();
  4836.         }
  4837.  
  4838.         public DuelingZone RemoveDuelist(string playerId)
  4839.         {
  4840.             foreach (var zone in duelingZones)
  4841.             {
  4842.                 if (zone.HasPlayer(playerId))
  4843.                 {
  4844.                     zone.RemovePlayer(playerId);
  4845.                     return zone;
  4846.                 }
  4847.             }
  4848.  
  4849.             return null;
  4850.         }
  4851.  
  4852.         public void ResetDuelist(string targetId, bool removeHome = true) // remove a dueler from the datafile
  4853.         {
  4854.             duelsData.Kits.Remove(targetId);
  4855.             duelsData.Restricted.Remove(targetId);
  4856.             dataImmunity.Remove(targetId);
  4857.             dataImmunitySpawns.Remove(targetId);
  4858.             dataDuelists.Remove(targetId);
  4859.             dataRequests.Remove(targetId);
  4860.             dataDeath.Remove(targetId);
  4861.  
  4862.             if (removeHome)
  4863.                 duelsData.Homes.Remove(targetId);
  4864.  
  4865.             if (duelingZones.Count > 0)
  4866.                 RemoveDuelist(targetId);
  4867.  
  4868.             RemoveFromQueue(targetId);
  4869.         }
  4870.  
  4871.         private void RemoveZeroStats() // someone enabled duels but never joined one. remove them to keep the datafile cleaned up
  4872.         {
  4873.             foreach (string targetId in duelsData.Allowed.ToList())
  4874.             {
  4875.                 if (!duelsData.Losses.ContainsKey(targetId) && !duelsData.Victories.ContainsKey(targetId)) // no permanent stats
  4876.                 {
  4877.                     ResetDuelist(targetId);
  4878.                     duelsData.Allowed.Remove(targetId);
  4879.                 }
  4880.             }
  4881.         }
  4882.  
  4883.         public void SetupZoneManager()
  4884.         {
  4885.             var zoneIds = ZoneManager?.Call("GetZoneIDs");
  4886.  
  4887.             if (zoneIds != null && zoneIds is string[])
  4888.             {
  4889.                 foreach (var zoneId in (string[])zoneIds)
  4890.                 {
  4891.                     var zoneLoc = ZoneManager?.Call("GetZoneLocation", zoneId);
  4892.  
  4893.                     if (zoneLoc is Vector3 && (Vector3)zoneLoc != Vector3.zero)
  4894.                     {
  4895.                         var position = (Vector3)zoneLoc;
  4896.                         var radius = ZoneManager?.Call("GetZoneRadius", zoneId);
  4897.                         float distance = 0f;
  4898.  
  4899.                         if (radius is float && (float)radius > 0f)
  4900.                         {
  4901.                             distance = (float)radius;
  4902.                         }
  4903.                         else
  4904.                         {
  4905.                             var zoneSize = ZoneManager?.Call("GetZoneSize", zoneId);
  4906.  
  4907.                             if (zoneSize is Vector3 && (Vector3)zoneSize != Vector3.zero)
  4908.                             {
  4909.                                 var size = (Vector3)zoneSize;
  4910.                                 distance = Mathf.Max(size.x, size.y);
  4911.                             }
  4912.                         }
  4913.  
  4914.                         if (distance > 0f)
  4915.                         {
  4916.                             distance += zoneRadius + 5f;
  4917.                             managedZones[position] = distance;
  4918.                         }
  4919.                     }
  4920.                 }
  4921.             }
  4922.         }
  4923.  
  4924.         public void SetupZones()
  4925.         {
  4926.             if (duelsData.ZoneIds.Count > 0)
  4927.             {
  4928.                 foreach (string id in duelsData.ZoneIds)
  4929.                 {
  4930.                     duelsData.DuelZones[id] = GetZoneName();
  4931.                 }
  4932.  
  4933.                 duelsData.ZoneIds.Clear();
  4934.                 SaveData();
  4935.             }
  4936.  
  4937.             if (duelsData.DuelZones.Count > zoneAmount) // zoneAmount was changed in the config file so remove existing zones until we're at the new cap
  4938.             {
  4939.                 int removed = 0;
  4940.  
  4941.                 do
  4942.                 {
  4943.                     string zoneId = duelsData.DuelZones.FirstOrDefault().Key;
  4944.                     var zonePos = zoneId.ToVector3();
  4945.  
  4946.                     if (spAutoRemove && duelsData.Spawns.Count > 0)
  4947.                         foreach (string spawn in duelsData.Spawns.ToList())
  4948.                             if (Vector3.Distance(spawn.ToVector3(), zonePos) <= zoneRadius)
  4949.                                 duelsData.Spawns.Remove(spawn);
  4950.  
  4951.                     duelsData.AutoGeneratedSpawns.Remove(zoneId);
  4952.                     duelsData.DuelZones.Remove(zoneId);
  4953.                     removed += RemoveZoneWalls(GetOwnerId(zoneId));
  4954.                 } while (duelsData.DuelZones.Count > zoneAmount);
  4955.  
  4956.                 if (removed > 0)
  4957.                     Puts(msg("RemovedXWallsCustom", null, removed));
  4958.             }
  4959.  
  4960.             var entities = autoSetup && (duelsData.DuelZones.Count < zoneAmount || duelsData.DuelZones.Count > 0) ? GetWallEntities() : null; // don't cache if we don't need to
  4961.  
  4962.             foreach (var entry in duelsData.DuelZones) // create all zones that don't already exist
  4963.                 SetupDuelZone(entry.Key.ToVector3(), entities, entry.Value);
  4964.  
  4965.             if (autoSetup && duelsData.DuelZones.Count < zoneAmount) // create each dueling zone that is missing. if this fails then console will be notified
  4966.             {
  4967.                 int attempts = Math.Max(zoneAmount, 5); // 0.1.10 fix - infinite loop fix for when zone radius is too large to fit on the map
  4968.                 int created = 0;
  4969.  
  4970.                 do
  4971.                 {
  4972.                     if (SetupDuelZone(entities, GetZoneName()) != Vector3.zero)
  4973.                         created++;
  4974.                 } while (duelsData.DuelZones.Count < zoneAmount && --attempts > 0);
  4975.  
  4976.                 if (attempts <= 0)
  4977.                 {
  4978.                     if (created > 0)
  4979.                         Puts(msg("SupportCreated", null, created));
  4980.                     else
  4981.                         Puts(msg("SupportInvalidConfig"));
  4982.                 }
  4983.             }
  4984.  
  4985.             if (duelingZones.Count > 0)
  4986.                 Puts(msg("ZonesSetup", null, duelingZones.Count));
  4987.         }
  4988.  
  4989.         public Vector3 SetupDuelZone(List<BaseEntity> entities, string zoneName) // starts the process of creating a new or existing zone and then setting up it's own spawn points around the circumference of the zone
  4990.         {
  4991.             var zonePos = FindDuelingZone(); // a complex process to search the map for a suitable area
  4992.  
  4993.             if (zonePos == Vector3.zero) // unfortunately we weren't able to find a location. this is likely due to an extremely high entity count. just try again.
  4994.                 return Vector3.zero;
  4995.  
  4996.             SetupDuelZone(zonePos, entities, zoneName);
  4997.             return zonePos;
  4998.         }
  4999.  
  5000.         public DuelingZone SetupDuelZone(Vector3 zonePos, List<BaseEntity> entities, string zoneName)
  5001.         {
  5002.             if (!duelsData.DuelZones.ContainsKey(zonePos.ToString()))
  5003.                 duelsData.DuelZones.Add(zonePos.ToString(), zoneName);
  5004.  
  5005.             var newZone = new GameObject().AddComponent<DuelingZone>();
  5006.  
  5007.             newZone.Setup(zonePos);
  5008.             duelingZones.Add(newZone);
  5009.  
  5010.             if (duelingZones.Count == 1)
  5011.             {
  5012.                 if (blockSpawning) Subscribe(nameof(OnPlayerRespawned));
  5013.                 Subscribe(nameof(OnEntityTakeDamage));
  5014.                 Subscribe(nameof(OnEntitySpawned));
  5015.                 Subscribe(nameof(CanBuild));
  5016.             }
  5017.  
  5018.             CreateZoneWalls(newZone.Position, zoneRadius, zoneUseWoodenWalls ? hewwPrefab : heswPrefab, entities);
  5019.             return newZone;
  5020.         }
  5021.  
  5022.         public List<BaseEntity> GetWallEntities()
  5023.         {
  5024.             var entities = new List<BaseEntity>();
  5025.  
  5026.             foreach (var e in BaseNetworkable.serverEntities)
  5027.             {
  5028.                 if (e != null && e.ShortPrefabName.Contains("wall.external.high"))
  5029.                 {
  5030.                     var entity = e as BaseEntity;
  5031.  
  5032.                     if (entity != null)
  5033.                     {
  5034.                         entities.Add(entity);
  5035.                     }
  5036.                 }
  5037.             }
  5038.  
  5039.             return entities;
  5040.         }
  5041.  
  5042.         public int RemoveZoneWalls(ulong ownerId)
  5043.         {
  5044.             int removed = 0;
  5045.  
  5046.             foreach (var entity in GetWallEntities().ToList())
  5047.             {
  5048.                 if (entity.OwnerID == ownerId)
  5049.                 {
  5050.                     entity.Kill();
  5051.                     removed++;
  5052.                 }
  5053.             }
  5054.  
  5055.             return removed;
  5056.         }
  5057.  
  5058.         public bool ZoneWallsExist(ulong ownerId, List<BaseEntity> entities)
  5059.         {
  5060.             if (entities == null || entities.Count < 3)
  5061.                 entities = GetWallEntities();
  5062.  
  5063.             return entities.Exists(entity => entity.OwnerID == ownerId);
  5064.         }
  5065.  
  5066.         public void CreateZoneWalls(Vector3 center, float zoneRadius, string prefab, List<BaseEntity> entities, BasePlayer player = null)
  5067.         {
  5068.             if (!useZoneWalls)
  5069.                 return;
  5070.  
  5071.             var tick = DateTime.Now;
  5072.             ulong ownerId = GetOwnerId(center.ToString());
  5073.  
  5074.             if (ZoneWallsExist(ownerId, entities))
  5075.                 return;
  5076.  
  5077.             float maxHeight = -200f;
  5078.             float minHeight = 200f;
  5079.             int spawned = 0;
  5080.             int raycasts = Mathf.CeilToInt(360 / zoneRadius * 0.1375f);
  5081.  
  5082.             foreach (var position in GetCircumferencePositions(center, zoneRadius, raycasts, 0f)) // get our positions and perform the calculations for the highest and lowest points of terrain
  5083.             {
  5084.                 RaycastHit hit;
  5085.                 if (Physics.Raycast(new Vector3(position.x, position.y + 200f, position.z), Vector3.down, out hit, Mathf.Infinity, wallMask))
  5086.                 {
  5087.                     maxHeight = Mathf.Max(hit.point.y, maxHeight); // calculate the highest point of terrain
  5088.                     minHeight = Mathf.Min(hit.point.y, minHeight); // calculate the lowest point of terrain
  5089.                     center.y = minHeight; // adjust the spawn point of our walls to that of the lowest point of terrain
  5090.                 }
  5091.             }
  5092.  
  5093.             float gap = prefab == heswPrefab ? 0.3f : 0.5f; // the distance used so that each wall fits closer to the other so players cannot throw items between the walls
  5094.             int stacks = Mathf.CeilToInt((maxHeight - minHeight) / 6f) + extraWallStacks; // get the amount of walls to stack onto each other to go above the highest point
  5095.             float next = 360 / zoneRadius - gap; // the distance apart each wall will be from the other
  5096.  
  5097.             for (int i = 0; i < stacks; i++) // create our loop to spawn each stack
  5098.             {
  5099.                 foreach (var position in GetCircumferencePositions(center, zoneRadius, next, center.y)) // get a list positions where each positions difference is the width of a high external stone wall. specify the height since we've already calculated what's required
  5100.                 {
  5101.                     float groundHeight = TerrainMeta.HeightMap.GetHeight(new Vector3(position.x, position.y + 6f, position.z));
  5102.  
  5103.                     if (groundHeight > position.y + 9f) // 0.1.13 improved distance check underground
  5104.                         continue;
  5105.  
  5106.                     if (useLeastAmount && position.y - groundHeight > 6f + extraWallStacks * 6f)
  5107.                         continue;
  5108.  
  5109.                     var entity = GameManager.server.CreateEntity(prefab, position, default(Quaternion), false);
  5110.  
  5111.                     if (entity != null)
  5112.                     {
  5113.                         entity.OwnerID = ownerId; // set a unique identifier so the walls can be easily removed later
  5114.                         entity.transform.LookAt(center, Vector3.up); // have each wall look at the center of the zone
  5115.                         entity.Spawn(); // spawn into the game
  5116.                         entity.gameObject.SetActive(true); // 0.1.16: fix for animals and explosives passing through walls. set it active after it spawns otherwise AntiHack will throw ProjectileHack: Line of sight warnings each time the entity is hit
  5117.                         spawned++; // our counter
  5118.                     }
  5119.                     else
  5120.                         return; // invalid prefab, return or cause massive server lag
  5121.  
  5122.                     if (stacks == i - 1)
  5123.                     {
  5124.                         RaycastHit hit;
  5125.                         if (Physics.Raycast(new Vector3(position.x, position.y + 6f, position.z), Vector3.down, out hit, 12f, worldMask))
  5126.                             stacks++; // 0.1.16 fix where rocks could allow a boost in or out of the top of a zone
  5127.                     }
  5128.                 }
  5129.  
  5130.                 center.y += 6f; // increase the positions height by one high external stone wall's height
  5131.             }
  5132.  
  5133.             if (player == null)
  5134.                 Puts(msg("GeneratedWalls", null, spawned, stacks, FormatPosition(center), (DateTime.Now - tick).TotalSeconds));
  5135.             else
  5136.                 Message(player, msg("GeneratedWalls", player.UserIDString, spawned, stacks, FormatPosition(center), (DateTime.Now - tick).TotalSeconds));
  5137.  
  5138.             Subscribe(nameof(OnEntityTakeDamage));
  5139.         }
  5140.  
  5141.         public void EjectPlayers(DuelingZone zone)
  5142.         {
  5143.             foreach (var player in zone.Players)
  5144.             {
  5145.                 EjectPlayer(player);
  5146.             }
  5147.         }
  5148.  
  5149.         public void EjectPlayer(BasePlayer player)
  5150.         {
  5151.             if (player == null)
  5152.                 return;
  5153.  
  5154.             player.inventory.Strip();
  5155.             ResetDuelist(player.UserIDString, false);
  5156.             SendHome(player);
  5157.         }
  5158.  
  5159.         public void RemoveDuelZone(DuelingZone zone)
  5160.         {
  5161.             string uid = zone.Position.ToString();
  5162.  
  5163.             foreach (string playerId in spectators.ToList())
  5164.             {
  5165.                 var player = BasePlayer.Find(playerId);
  5166.  
  5167.                 if (player == null)
  5168.                 {
  5169.                     spectators.Remove(playerId);
  5170.                     continue;
  5171.                 }
  5172.  
  5173.                 if (zone.Distance(player.transform.position) <= zoneRadius)
  5174.                 {
  5175.                     EndSpectate(player);
  5176.                     SendHome(player);
  5177.                 }
  5178.             }
  5179.  
  5180.             var match = tdmMatches.FirstOrDefault(x => x.Zone != null && x.Zone == zone);
  5181.  
  5182.             if (match != null)
  5183.                 match.End();
  5184.  
  5185.             if (spAutoRemove && duelsData.Spawns.Count > 0)
  5186.                 foreach (var spawn in zone.Spawns)
  5187.                     duelsData.Spawns.Remove(spawn.ToString());
  5188.  
  5189.             duelsData.DuelZones.Remove(uid);
  5190.             duelsData.AutoGeneratedSpawns.Remove(uid);
  5191.             RemoveEntities(zone);
  5192.             RemoveZoneWalls(GetOwnerId(uid));
  5193.             zone.Kill();
  5194.  
  5195.             if (duelingZones.Count == 0 && tdmMatches.Count == 0)
  5196.             {
  5197.                 SubscribeHooks(false);
  5198.                 CheckZoneHooks();
  5199.             }
  5200.         }
  5201.  
  5202.         public void RemoveEntities(ulong playerId)
  5203.         {
  5204.             if (duelEntities.ContainsKey(playerId))
  5205.             {
  5206.                 foreach (var e in duelEntities[playerId].ToList())
  5207.                     e.SafelyKill();
  5208.  
  5209.                 duelEntities.Remove(playerId);
  5210.             }
  5211.         }
  5212.  
  5213.         public void RemoveEntities(DuelingZone zone)
  5214.         {
  5215.             foreach (var entry in duelEntities.ToList())
  5216.             {
  5217.                 foreach (var entity in entry.Value.ToList())
  5218.                 {
  5219.                     if (entity.IsKilled())
  5220.                     {
  5221.                         duelEntities[entry.Key].Remove(entity);
  5222.                         continue;
  5223.                     }
  5224.  
  5225.                     if (zone.Distance(entity.transform.position) <= zoneRadius + 1f)
  5226.                     {
  5227.                         duelEntities[entry.Key].Remove(entity);
  5228.                         entity.Kill();
  5229.                     }
  5230.                 }
  5231.             }
  5232.         }
  5233.  
  5234.         public DuelingZone GetDuelZone(Vector3 startPos, float offset = 1f)
  5235.         {
  5236.             return duelingZones.FirstOrDefault(zone => zone.Distance(startPos) <= zoneRadius + offset);
  5237.         }
  5238.  
  5239.         public void SendHome(BasePlayer player) // send a player home to the saved location that they joined from
  5240.         {
  5241.             if (player != null && duelsData.Homes.ContainsKey(player.UserIDString))
  5242.             {
  5243.                 if (player.IsDead() && !player.IsConnected && !respawnDeadDisconnect)
  5244.                 {
  5245.                     duelsData.Homes.Remove(player.UserIDString);
  5246.                     return;
  5247.                 }
  5248.  
  5249.                 if (player.IsSleeping() || player.HasPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot))
  5250.                 {
  5251.                     timer.Once(2f, () => SendHome(player));
  5252.                     return;
  5253.                 }
  5254.  
  5255.                 RemoveEntities(player.userID);
  5256.                 var homePos = duelsData.Homes[player.UserIDString].ToVector3();
  5257.  
  5258.                 if (DuelTerritory(homePos) && !player.IsAdmin)
  5259.                 {
  5260.                     var bags = SleepingBag.FindForPlayer(player.userID, true).ToList();
  5261.  
  5262.                     if (bags.Count > 0)
  5263.                     {
  5264.                         bags.Sort((x, y) => x.net.ID.CompareTo(y.net.ID));
  5265.                         homePos = bags[0].transform.position;
  5266.                         homePos.y += 0.25f;
  5267.                     }
  5268.                     else
  5269.                     {
  5270.                         homePos = ServerMgr.FindSpawnPoint().pos;
  5271.                     }
  5272.                 }
  5273.  
  5274.                 if (player.IsDead())
  5275.                 {
  5276.                     if (sendDeadHome)
  5277.                         player.RespawnAt(homePos, default(Quaternion));
  5278.                     else player.Respawn();
  5279.                 }
  5280.                 else
  5281.                 {
  5282.                     if (!sendDeadHome)
  5283.                     {
  5284.                         player.LifeStoryEnd();
  5285.                         player.Respawn();
  5286.                     }
  5287.                     else Teleport(player, homePos);
  5288.                 }
  5289.  
  5290.                 if (playerHealth > 0f)
  5291.                     player.health = playerHealth;
  5292.  
  5293.                 GiveRespawnLoot(player);
  5294.                 duelsData.Homes.Remove(player.UserIDString);
  5295.  
  5296.                 if (guiAutoEnable || createUI.Contains(player.UserIDString))
  5297.                     OnPlayerConnected(player);
  5298.  
  5299.                 if (readyUiList.Contains(player.UserIDString))
  5300.                     ToggleReadyUI(player);
  5301.             }
  5302.         }
  5303.  
  5304.         public void GiveRespawnLoot(BasePlayer player)
  5305.         {
  5306.             if (respawnLoot.Count > 0)
  5307.             {
  5308.                 player.inventory.Strip();
  5309.  
  5310.                 foreach (var entry in respawnLoot)
  5311.                 {
  5312.                     Item item = ItemManager.CreateByName(entry.shortname, entry.amount, entry.skin);
  5313.  
  5314.                     if (item == null)
  5315.                         continue;
  5316.  
  5317.                     var container = entry.container == "wear" ? player.inventory.containerWear : entry.container == "belt" ? player.inventory.containerBelt : player.inventory.containerMain;
  5318.  
  5319.                     if (!item.MoveToContainer(container, entry.slot))
  5320.                     {
  5321.                         item.Remove(0f);
  5322.                     }
  5323.                 }
  5324.             }
  5325.             else if (!string.IsNullOrEmpty(autoKitName) && Kits.CanCall() && IsKit(autoKitName))
  5326.             {
  5327.                 player.inventory.Strip();
  5328.                 Kits.Call("GiveKit", player, autoKitName);
  5329.             }
  5330.         }
  5331.  
  5332.         private void UpdateStability()
  5333.         {
  5334.             if (noStability)
  5335.             {
  5336.                 Subscribe(nameof(OnEntitySpawned));
  5337.  
  5338.                 foreach (BuildingBlock block in BaseNetworkable.serverEntities.OfType<BuildingBlock>().Where(e => DuelTerritory(e.transform.position)).ToList())
  5339.                 {
  5340.                     if (block.grounded)
  5341.                         continue;
  5342.  
  5343.                     if (block.OwnerID == 0 || permission.UserHasGroup(block.OwnerID.ToString(), "admin"))
  5344.                         block.grounded = true;
  5345.                 }
  5346.             }
  5347.         }
  5348.  
  5349.         private void CheckZoneHooks(bool message = false)
  5350.         {
  5351.             if (respawnWalls && duelingZones.Count > 0)
  5352.             {
  5353.                 Subscribe(nameof(OnEntityDeath));
  5354.                 Subscribe(nameof(OnEntityKill));
  5355.             }
  5356.         }
  5357.  
  5358.         private void CheckDuelistMortality()
  5359.         {
  5360.             eventTimer = timer.Once(0.5f, CheckDuelistMortality);
  5361.  
  5362.             if (dataImmunity.Count > 0) // each player that spawns into a dueling zone is given immunity for X seconds. here we'll keep track of this and remove their immunities
  5363.             {
  5364.                 var timeStamp = TimeStamp();
  5365.  
  5366.                 foreach (var kvp in dataImmunity.ToList())
  5367.                 {
  5368.                     var player = BasePlayer.Find(kvp.Key);
  5369.                     long time = kvp.Value - timeStamp;
  5370.  
  5371.                     if (time <= 0)
  5372.                     {
  5373.                         dataImmunity.Remove(kvp.Key);
  5374.                         dataImmunitySpawns.Remove(kvp.Key);
  5375.  
  5376.                         if (!player || !player.IsConnected)
  5377.                             continue;
  5378.  
  5379.                         CuiHelper.DestroyUi(player, "DuelistUI_Countdown");
  5380.                         Message(player, msg("ImmunityFaded", player.UserIDString));
  5381.                     }
  5382.                     else if (player != null && player.IsConnected)
  5383.                     {
  5384.                         if (noMovement && dataImmunitySpawns.ContainsKey(player.UserIDString))
  5385.                         {
  5386.                             var dest = dataImmunitySpawns[player.UserIDString];
  5387.                             player.Teleport(dest);
  5388.                         }
  5389.  
  5390.                         CreateCountdownUI(player, time.ToString());
  5391.                     }
  5392.                 }
  5393.             }
  5394.  
  5395.             if (dataDeath.Count > 0) // keep track of how long the match has been going on for, and if it's been too long then kill the player off.
  5396.             {
  5397.                 var timeStamp = TimeStamp();
  5398.  
  5399.                 foreach (var kvp in dataDeath.ToList())
  5400.                 {
  5401.                     if (kvp.Value - timeStamp <= 0)
  5402.                     {
  5403.                         var target = BasePlayer.Find(kvp.Key);
  5404.                         dataDeath.Remove(kvp.Key);
  5405.  
  5406.                         if (!target || !target.IsConnected || (!IsDueling(target) && !InDeathmatch(target)))
  5407.                             continue;
  5408.  
  5409.                         target.inventory.Strip();
  5410.                         OnEntityDeath(target, null);
  5411.                     }
  5412.                 }
  5413.             }
  5414.  
  5415.             UpdateMatchUI();
  5416.         }
  5417.  
  5418.         public void SubscribeHooks(bool flag) // we're using lots of temporary and permanent hooks so we'll turn off the temporary hooks when the plugin is loaded, and unsubscribe to others inside of their hooks when they're no longer in use
  5419.         {
  5420.             if (!flag)
  5421.             {
  5422.                 Unsubscribe(nameof(OnPlayerDisconnected));
  5423.                 //Unsubscribe(nameof(CanNetworkTo));
  5424.                 Unsubscribe(nameof(OnItemDropped));
  5425.                 Unsubscribe(nameof(OnPlayerSleepEnded));
  5426.                 Unsubscribe(nameof(OnCreateWorldProjectile));
  5427.                 Unsubscribe(nameof(OnLootEntity));
  5428.                 Unsubscribe(nameof(OnPlayerRespawned));
  5429.                 Unsubscribe(nameof(OnEntityTakeDamage));
  5430.                 Unsubscribe(nameof(OnEntitySpawned));
  5431.                 Unsubscribe(nameof(CanBuild));
  5432.                 Unsubscribe(nameof(OnPlayerHealthChange));
  5433.                 Unsubscribe(nameof(OnEntityDeath));
  5434.                 Unsubscribe(nameof(OnEntityKill));
  5435.                 //Unsubscribe(nameof(OnPlayerCommand));
  5436.                 Unsubscribe(nameof(OnPlayerConnected));
  5437.                 return;
  5438.             }
  5439.  
  5440.             Subscribe(nameof(OnPlayerDisconnected));
  5441.             Subscribe(nameof(OnItemDropped));
  5442.             Subscribe(nameof(OnPlayerSleepEnded));
  5443.             Subscribe(nameof(OnCreateWorldProjectile));
  5444.             Subscribe(nameof(OnLootEntity));
  5445.             Subscribe(nameof(OnEntitySpawned));
  5446.  
  5447.             if (!allowPlayerDeaths)
  5448.                 Subscribe(nameof(OnPlayerHealthChange));
  5449.  
  5450.             Subscribe(nameof(OnEntityTakeDamage));
  5451.             Subscribe(nameof(OnEntityDeath));
  5452.  
  5453.             if (useBlacklistCommands || useWhitelistCommands)
  5454.             {
  5455.                 Subscribe(nameof(OnPlayerCommand));
  5456.             }
  5457.  
  5458.             if (respawnWalls)
  5459.                 Subscribe(nameof(OnEntityKill));
  5460.         }
  5461.  
  5462.         // Helper methods which are essential for the plugin to function. Do not modify these.
  5463.  
  5464.         [HookMethod("DuelistTerritory")]
  5465.         public bool DuelistTerritory(Vector3 position) // API
  5466.         {
  5467.             return DuelTerritory(position);
  5468.         }
  5469.  
  5470.         public bool DuelTerritory(Vector3 position, float offset = 5f) // 0.1.21: arena can be inside of the zone at any height
  5471.         {
  5472.             return duelingZones.Exists(zone => Vector3Ex.Distance2D(zone.Position, position) <= zoneRadius + offset);
  5473.         }
  5474.  
  5475.         public ulong GetOwnerId(string uid)
  5476.         {
  5477.             return Convert.ToUInt64(Math.Abs(uid.GetHashCode()));
  5478.         }
  5479.  
  5480.         [HookMethod("inEvent")]
  5481.         public bool inEvent(BasePlayer player)
  5482.         {
  5483.             return InEvent(player);
  5484.         }
  5485.  
  5486.         public bool InEvent(BasePlayer player)
  5487.         {
  5488.             return player != null && (dataDuelists.ContainsKey(player.UserIDString) || tdmMatches.Exists(match => match.GetTeam(player) != Team.None));
  5489.         }
  5490.  
  5491.         public bool IsDueling(BasePlayer player)
  5492.         {
  5493.             return player != null && duelsData != null && duelingZones.Count > 0 && player != null && dataDuelists.ContainsKey(player.UserIDString) && DuelTerritory(player.transform.position);
  5494.         }
  5495.  
  5496.         public bool InDeathmatch(BasePlayer player)
  5497.         {
  5498.             return player != null && tdmMatches.Exists(team => team.GetTeam(player) != Team.None) && DuelTerritory(player.transform.position);
  5499.         }
  5500.  
  5501.         public bool IsSpectator(BasePlayer player)
  5502.         {
  5503.             return player != null && spectators.Contains(player.UserIDString) && DuelTerritory(player.transform.position);
  5504.         }
  5505.  
  5506.         public bool IsEventBanned(string targetId)
  5507.         {
  5508.             return duelsData.Bans.ContainsKey(targetId);
  5509.         }
  5510.  
  5511.         public static long TimeStamp() => (DateTime.Now.Ticks - DateTime.Parse("01/01/1970 00:00:00").Ticks) / 10000000;
  5512.  
  5513.         public string GetDisplayName(string targetId)
  5514.         {
  5515.             return covalence.Players.FindPlayerById(targetId)?.Name ?? targetId;
  5516.         }
  5517.  
  5518.         public void Log(string file, string message, bool timestamp = false)
  5519.         {
  5520.             LogToFile(file, $"[{DateTime.Now}] {message}", this, timestamp);
  5521.         }
  5522.  
  5523.         public GoodVersusEvilMatch GetMatch(BasePlayer player)
  5524.         {
  5525.             return tdmMatches.FirstOrDefault(team => team.GetTeam(player) != Team.None);
  5526.         }
  5527.  
  5528.         public bool InMatch(BasePlayer target)
  5529.         {
  5530.             return tdmMatches.Exists(team => team.GetTeam(target) != Team.None);
  5531.         }
  5532.  
  5533.         public bool IsOnConstruction(Vector3 position)
  5534.         {
  5535.             position.y += 1f;
  5536.             RaycastHit hit;
  5537.  
  5538.             return Physics.Raycast(position, Vector3.down, out hit, 1.5f, constructionMask) && hit.GetEntity() != null;
  5539.         }
  5540.  
  5541.         public bool Teleport(BasePlayer player, Vector3 destination)
  5542.         {
  5543.             if (player == null || destination == Vector3.zero) // don't send a player to their death. this should never happen
  5544.                 return false;
  5545.  
  5546.             player.Invoke(player.EndLooting, 0.01f);
  5547.  
  5548.             if (DuelTerritory(destination))
  5549.             {
  5550.                 var rematch = rematches.FirstOrDefault(x => x.HasPlayer(player));
  5551.  
  5552.                 if (rematch != null)
  5553.                     rematches.Remove(rematch);
  5554.  
  5555.                 SetPlayerTime(player, true);
  5556.             }
  5557.  
  5558.             if (player.IsWounded())
  5559.                 player.StopWounded();
  5560.  
  5561.             player.metabolism.bleeding.value = 0;
  5562.  
  5563.             if (playerHealth > 0f && player.health < playerHealth)
  5564.                 player.health = playerHealth;
  5565.  
  5566.             if (player.IsConnected)
  5567.                 player.StartSleeping();
  5568.  
  5569.             player.Teleport(destination);
  5570.  
  5571.             if (player.IsConnected && (Vector3.Distance(player.transform.position, destination) > 50f || !DuelTerritory(destination))) // 1.1.2 reduced from 150 to 100
  5572.             {
  5573.                 player.SetPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot, true);
  5574.                 player.ClientRPCPlayer(null, player, "StartLoading");
  5575.                 player.UpdateNetworkGroup();
  5576.                 player.SendEntityUpdate();
  5577.             }
  5578.            
  5579.             player.SendNetworkUpdateImmediate(false);
  5580.  
  5581.             if (LustyMap.CanCall())
  5582.             {
  5583.                 LustyMap?.Call(DuelTerritory(destination) ? "DisableMaps" : "EnableMaps", player);
  5584.             }
  5585.  
  5586.             RemoveFromQueue(player.UserIDString);
  5587.  
  5588.             return true;
  5589.         }
  5590.  
  5591.         public bool IsThrownWeapon(Item item)
  5592.         {
  5593.             if (item == null)
  5594.                 return false;
  5595.  
  5596.             if (item.info.category == ItemCategory.Weapon || item.info.category == ItemCategory.Tool)
  5597.             {
  5598.                 if (item.info.stackable > 1)
  5599.                     return false;
  5600.  
  5601.                 var weapon = item?.GetHeldEntity() as BaseProjectile;
  5602.  
  5603.                 if (weapon == null)
  5604.                     return true;
  5605.  
  5606.                 if (weapon.primaryMagazine.capacity > 0)
  5607.                     return false;
  5608.             }
  5609.  
  5610.             return false;
  5611.         }
  5612.  
  5613.         public Vector3 RandomDropPosition() // CargoPlane.RandomDropPosition()
  5614.         {
  5615.             Vector3 vector;
  5616.             float num = 100f, x = TerrainMeta.Size.x / 3f;
  5617.             do
  5618.             {
  5619.                 vector = Vector3Ex.Range(-x, x);
  5620.             } while (filter.GetFactor(vector) == 0f && --num > 0f);
  5621.             vector.y = 0f;
  5622.             return vector;
  5623.         }
  5624.  
  5625.         public Vector3 FindDuelingZone()
  5626.         {
  5627.             Vector3 position;
  5628.             DateTime tick = DateTime.Now; // create a timestamp to see how long this process takes
  5629.             int maxRetries = 500; // 0.1.9: increased due to rock collider detection. 0.1.10 rock collider detection removed but amount not changed
  5630.             int retries = maxRetries; // servers with high entity counts will require this
  5631.  
  5632.             if (managedZones.Count == 0 && ZoneManager.CanCall())
  5633.                 SetupZoneManager();
  5634.  
  5635.             do
  5636.             {
  5637.                 position = RandomDropPosition();
  5638.  
  5639.                 foreach (var monument in monuments)
  5640.                 {
  5641.                     if (Vector3.Distance(position, monument) < 150f) // don't put the dueling zone inside of a monument. players will throw a shit fit
  5642.                     {
  5643.                         position = Vector3.zero;
  5644.                         break;
  5645.                     }
  5646.                 }
  5647.  
  5648.                 if (position == Vector3.zero)
  5649.                     continue;
  5650.  
  5651.                 if (managedZones.Count > 0)
  5652.                 {
  5653.                     foreach (var zone in managedZones)
  5654.                     {
  5655.                         if (Vector3.Distance(zone.Key, position) <= zone.Value)
  5656.                         {
  5657.                             position = Vector3.zero; // blocked by zone manager
  5658.                             break;
  5659.                         }
  5660.                     }
  5661.                 }
  5662.  
  5663.                 if (position == Vector3.zero)
  5664.                     continue;
  5665.  
  5666.                 position.y = TerrainMeta.HeightMap.GetHeight(position) + 100f; // setup the hit
  5667.  
  5668.                 RaycastHit hit;
  5669.                 if (Physics.Raycast(position, Vector3.down, out hit, position.y, groundMask))
  5670.                 {
  5671.                     position.y = Mathf.Max(hit.point.y, TerrainMeta.HeightMap.GetHeight(position)); // get the max height
  5672.  
  5673.                     var colliders = Pool.GetList<Collider>();
  5674.                     Vis.Colliders(position, zoneRadius + 15f, colliders, blockedMask, QueryTriggerInteraction.Collide); // get all colliders using the provided layermask
  5675.  
  5676.                     if (colliders.Count > 0) // if any colliders were found from the blockedMask then we don't want this as our dueling zone. retry.
  5677.                         position = Vector3.zero;
  5678.  
  5679.                     Pool.FreeList(ref colliders);
  5680.  
  5681.                     if (position != Vector3.zero) // so far so good, let's measure the highest and lowest points of the terrain, and count the amount of water colliders
  5682.                     {
  5683.                         var positions = GetCircumferencePositions(position, zoneRadius - 15f, 1f, 0f); // gather positions around the purposed zone
  5684.                         float min = 200f;
  5685.                         float max = -200f;
  5686.                         int water = 0;
  5687.  
  5688.                         foreach (var pos in positions)
  5689.                         {
  5690.                             if (Physics.Raycast(new Vector3(pos.x, pos.y + 100f, pos.z), Vector3.down, 100.5f, waterMask)) //look for water
  5691.                                 water++; // count the amount of water colliders
  5692.  
  5693.                             min = Mathf.Min(pos.y, min); // set the lowest and highest points of the terrain
  5694.                             max = Mathf.Max(pos.y, max);
  5695.                         }
  5696.  
  5697.                         if (max - min > maxIncline || position.y - min > maxIncline) // the incline is too steep to be suitable for a dueling zone, retry.
  5698.                             position = Vector3.zero;
  5699.  
  5700.                         if (water > positions.Count / 4) // too many water colliders, retry.
  5701.                             position = Vector3.zero;
  5702.  
  5703.                         positions.Clear();
  5704.                     }
  5705.                 }
  5706.                 else
  5707.                     position = Vector3.zero; // found water instead of land
  5708.  
  5709.                 if (position == Vector3.zero)
  5710.                     continue;
  5711.  
  5712.                 if (DuelTerritory(position, zoneRadius + 15f)) // check if position overlaps an existing zone
  5713.                     position = Vector3.zero; // overlaps, retry.
  5714.             } while (position == Vector3.zero && --retries > 0); // prevent infinite loops
  5715.  
  5716.             if (position != Vector3.zero)
  5717.                 Puts(msg("FoundZone", null, maxRetries - retries, (DateTime.Now - tick).TotalMilliseconds)); // we found a dueling zone! return the position to be assigned, spawn the zone and the spawn points!
  5718.  
  5719.             return position;
  5720.         }
  5721.  
  5722.         public List<Vector3> GetCircumferencePositions(Vector3 center, float radius, float next, float y) // as the name implies
  5723.         {
  5724.             var positions = new List<Vector3>();
  5725.             float degree = 0f;
  5726.  
  5727.             while (degree < 360)
  5728.             {
  5729.                 float angle = (float)(2 * Math.PI / 360) * degree;
  5730.                 float x = center.x + radius * (float)Math.Cos(angle);
  5731.                 float z = center.z + radius * (float)Math.Sin(angle);
  5732.                 var position = new Vector3(x, 0f, z);
  5733.  
  5734.                 position.y = y == 0f ? TerrainMeta.HeightMap.GetHeight(position) : y;
  5735.                 positions.Add(position);
  5736.  
  5737.                 degree += next;
  5738.             }
  5739.  
  5740.             return positions;
  5741.         }
  5742.  
  5743.         public List<Vector3> GetAutoSpawns(DuelingZone zone)
  5744.         {
  5745.             var spawns = new List<Vector3>();
  5746.             string key = zone.Position.ToString();
  5747.  
  5748.             if (duelsData.AutoGeneratedSpawns.ContainsKey(key) && duelsData.AutoGeneratedSpawns[key].Count > 0)
  5749.                 spawns.AddRange(duelsData.AutoGeneratedSpawns[key].Select(spawn => spawn.ToVector3())); // use cached spawn points
  5750.  
  5751.             if (!duelsData.AutoGeneratedSpawns.ContainsKey(key))
  5752.                 duelsData.AutoGeneratedSpawns.Add(key, new List<string>());
  5753.  
  5754.             if (spawns.Count < 2)
  5755.                 spawns = CreateSpawnPoints(zone.Position); // create spawn points on the fly
  5756.  
  5757.             duelsData.AutoGeneratedSpawns[key] = spawns.Select(spawn => spawn.ToString()).ToList();
  5758.             return spawns;
  5759.         }
  5760.  
  5761.         public List<Vector3> CreateSpawnPoints(Vector3 center)
  5762.         {
  5763.             var positions = new List<Vector3>(); // 0.1.1 bugfix: spawn point height (y) wasn't being modified when indexing the below foreach list. instead, create a copy of each position and return a new list (cause: can't modify members of value types without changing the collection and invalidating the enumerator. bug: index the value type and change the value. result: list did not propagate)
  5764.  
  5765.             // create spawn points slightly inside of the dueling zone so they don't spawn inside of walls
  5766.             foreach (var position in GetCircumferencePositions(center, zoneRadius - 15f, 10f, 0f))
  5767.             {
  5768.                 var hits = Physics.RaycastAll(new Vector3(position.x, TerrainMeta.HighestPoint.y + 200f, position.z), Vector3.down, Mathf.Infinity);
  5769.  
  5770.                 if (hits.Length > 0) // low failure rate
  5771.                 {
  5772.                     float y = TerrainMeta.HeightMap.GetHeight(position);
  5773.  
  5774.                     if (avoidWaterSpawns && TerrainMeta.WaterMap.GetHeight(position) - y > 0.8f)
  5775.                         continue; // 0.1.16: better method to check water level
  5776.  
  5777.                     foreach (var hit in hits)
  5778.                     {
  5779.                         switch (LayerMask.LayerToName(hit.collider.gameObject.layer))
  5780.                         {
  5781.                             case "Construction":
  5782.                             case "Deployed":
  5783.                                 if (!hit.GetEntity()) // 0.1.2 bugfix: spawn points floating when finding a collider with no entity
  5784.                                     continue;
  5785.  
  5786.                                 y = Mathf.Max(hit.point.y, y);
  5787.                                 break;
  5788.                             case "World":
  5789.                             case "Terrain":
  5790.                                 y = Mathf.Max(hit.point.y, y);
  5791.                                 break;
  5792.                         }
  5793.                     }
  5794.  
  5795.                     positions.Add(new Vector3(position.x, y, position.z));
  5796.                 }
  5797.             }
  5798.  
  5799.             return positions;
  5800.         }
  5801.  
  5802.         public bool ResetDuelists() // reset all data for the wipe after assigning awards
  5803.         {
  5804.             if (AssignDuelists())
  5805.             {
  5806.                 if (resetSeed)
  5807.                 {
  5808.                     duelsData.VictoriesSeed.Clear();
  5809.                     duelsData.LossesSeed.Clear();
  5810.                     duelsData.MatchKillsSeed.Clear();
  5811.                     duelsData.MatchDeathsSeed.Clear();
  5812.                     duelsData.MatchLossesSeed.Clear();
  5813.                     duelsData.MatchVictoriesSeed.Clear();
  5814.                     duelsData.MatchSizesVictoriesSeed.Clear();
  5815.                     duelsData.MatchSizesVictoriesSeed.Clear();
  5816.                 }
  5817.  
  5818.                 if (wipeDuelZones)
  5819.                 {
  5820.                     duelsData.DuelZones.Clear();
  5821.                     duelsData.Spawns.Clear();
  5822.                     duelsData.AutoGeneratedSpawns.Clear();
  5823.                 }
  5824.  
  5825.                 duelsData.Bets.Clear();
  5826.                 duelsData.ClaimBets.Clear();
  5827.                 ResetTemporaryData();
  5828.             }
  5829.  
  5830.             return true;
  5831.         }
  5832.  
  5833.         public bool AssignDuelists()
  5834.         {
  5835.             if (!recordStats || duelsData.VictoriesSeed.Count == 0)
  5836.                 return true; // nothing to do here, return
  5837.  
  5838.             foreach (var target in covalence.Players.All) // remove player awards from previous wipe
  5839.             {
  5840.                 if (permission.UserHasPermission(target.Id, duelistPerm))
  5841.                     permission.RevokeUserPermission(target.Id, duelistPerm);
  5842.  
  5843.                 if (permission.UserHasGroup(target.Id, duelistGroup))
  5844.                     permission.RemoveUserGroup(target.Id, duelistGroup);
  5845.             }
  5846.  
  5847.             if (permsToGive <= 0) // check now incase the user disabled awards later on
  5848.                 return true;
  5849.  
  5850.             var duelists = duelsData.VictoriesSeed.ToList(); // sort the data
  5851.             duelists.Sort((x, y) => y.Value.CompareTo(x.Value));
  5852.  
  5853.             int added = 0;
  5854.  
  5855.             for (int i = 0; i < duelists.Count; i++) // now assign it
  5856.             {
  5857.                 var target = covalence.Players.FindPlayerById(duelists[i].Key);
  5858.  
  5859.                 if (target == null || target.IsBanned || target.IsAdmin)
  5860.                     continue;
  5861.  
  5862.                 permission.GrantUserPermission(target.Id, duelistPerm.ToLower(), this);
  5863.                 permission.AddUserGroup(target.Id, duelistGroup.ToLower());
  5864.  
  5865.                 Log("awards", msg("Awards", null, target.Name, target.Id, duelists[i].Value), true);
  5866.                 Puts(msg("Granted", null, target.Name, target.Id, duelistPerm, duelistGroup));
  5867.  
  5868.                 if (++added >= permsToGive)
  5869.                     break;
  5870.             }
  5871.  
  5872.             if (added > 0)
  5873.                 Puts(msg("Logged", null, string.Format("{0}{1}{2}_{3}-{4}.txt", Interface.Oxide.LogDirectory, Path.DirectorySeparatorChar, Name.Replace(" ", "").ToLower(), "awards", DateTime.Now.ToString("yyyy-MM-dd"))));
  5874.  
  5875.             return true;
  5876.         }
  5877.  
  5878.         public bool IsNewman(BasePlayer player) // count the players items. exclude rocks and torchs
  5879.         {
  5880.             if (bypassNewmans || saveRestoreEnabled)
  5881.                 return true;
  5882.  
  5883.             int count = player.inventory.AllItems().Length;
  5884.  
  5885.             if (permission.UserHasPermission(player.UserIDString, "permamap.use") && player.inventory.containerBelt.GetSlot(6) != null)
  5886.                 count -= 1;
  5887.  
  5888.             count -= respawnLoot.Sum(entry => GetAmount(player, entry.shortname));
  5889.  
  5890.             return count == 0;
  5891.         }
  5892.  
  5893.         public int GetAmount(BasePlayer player, string shortname)
  5894.         {
  5895.             return player.inventory.AllItems().Where(x => x.info.shortname == shortname.ToLower()).ToList().Sum(item => item.amount);
  5896.         }
  5897.  
  5898.         public bool RemoveFromQueue(string targetId)
  5899.         {
  5900.             foreach (var kvp in duelsData.Queued)
  5901.             {
  5902.                 if (kvp.Value == targetId)
  5903.                 {
  5904.                     duelsData.Queued.Remove(kvp.Key);
  5905.                     return true;
  5906.                 }
  5907.             }
  5908.  
  5909.             return false;
  5910.         }
  5911.  
  5912.         public void CheckQueue()
  5913.         {
  5914.             if (duelsData.Queued.Count < 2 || !duelsData.DuelsEnabled)
  5915.                 return;
  5916.  
  5917.             string playerId = duelsData.Queued.Values.ElementAt(0);
  5918.             string targetId = duelsData.Queued.Values.ElementAt(1);
  5919.             var player = BasePlayer.Find(playerId);
  5920.             var target = BasePlayer.Find(targetId);
  5921.  
  5922.             if (player == null || !player.CanInteract() || InMatch(player))
  5923.             {
  5924.                 if (RemoveFromQueue(playerId))
  5925.                     CheckQueue();
  5926.  
  5927.                 return;
  5928.             }
  5929.  
  5930.             if (target == null || !player.CanInteract() || InMatch(player))
  5931.             {
  5932.                 if (RemoveFromQueue(targetId))
  5933.                     CheckQueue();
  5934.  
  5935.                 return;
  5936.             }
  5937.  
  5938.             if (!IsNewman(player))
  5939.             {
  5940.                 if (RemoveFromQueue(player.UserIDString))
  5941.                     Message(player, msg("MustBeNaked", player.UserIDString));
  5942.                 return;
  5943.             }
  5944.  
  5945.             if (!IsNewman(target))
  5946.             {
  5947.                 if (RemoveFromQueue(target.UserIDString))
  5948.                     Message(target, msg("MustBeNaked", target.UserIDString));
  5949.                 return;
  5950.             }
  5951.  
  5952.             SelectZone(player, target);
  5953.         }
  5954.  
  5955.         public bool SelectZone(BasePlayer player, BasePlayer target)
  5956.         {
  5957.             var lastZone = GetPlayerZone(player, 2) ?? GetPlayerZone(target, 2) ?? GetDuelZone(player.transform.position) ?? GetDuelZone(target.transform.position);
  5958.  
  5959.             if (lastZone != null)
  5960.             {
  5961.                 var success = lastZone.AddWaiting(player, target);
  5962.  
  5963.                 if (success != null && success is bool && (bool)success)
  5964.                 {
  5965.                     Initiate(player, target, false, lastZone);
  5966.                     return true;
  5967.                 }
  5968.             }
  5969.  
  5970.             var zones = duelingZones.Where(zone => !zone.IsFull && !zone.IsLocked && zone.Spawns.Count >= requiredMinSpawns && zone.Spawns.Count <= requiredMaxSpawns).ToList();
  5971.  
  5972.             while (zones.Count > 0)
  5973.             {
  5974.                 var zone = zones.GetRandom();
  5975.                 var success = zone.AddWaiting(player, target);
  5976.  
  5977.                 if (success == null) // user must pay the duel entry fee first
  5978.                     return true;
  5979.  
  5980.                 if (success is bool && (bool)success)
  5981.                 {
  5982.                     Initiate(player, target, false, zone);
  5983.                     return true;
  5984.                 }
  5985.  
  5986.                 zones.Remove(zone);
  5987.             };
  5988.  
  5989.             return false;
  5990.         }
  5991.  
  5992.         public string GetKit(BasePlayer player, BasePlayer target)
  5993.         {
  5994.             string kit = GetRandomKit();
  5995.  
  5996.             if (duelsData.CustomKits.ContainsKey(player.UserIDString) && duelsData.CustomKits.ContainsKey(target.UserIDString))
  5997.             {
  5998.                 string playerKit = duelsData.CustomKits[player.UserIDString];
  5999.                 string targetKit = duelsData.CustomKits[target.UserIDString];
  6000.  
  6001.                 if (playerKit.Equals(targetKit, StringComparison.CurrentCultureIgnoreCase))
  6002.                 {
  6003.                     return GetVerifiedKit(playerKit) ?? kit;
  6004.                 }
  6005.             }
  6006.  
  6007.             return kit;
  6008.         }
  6009.  
  6010.         public void VerifyKits()
  6011.         {
  6012.             if (Kits.CanCall())
  6013.             {
  6014.                 foreach (string kit in lpDuelingKits.ToList())
  6015.                     if (!IsKit(kit))
  6016.                         lpDuelingKits.Remove(kit);
  6017.  
  6018.                 foreach (string kit in hpDuelingKits.ToList())
  6019.                     if (!IsKit(kit))
  6020.                         hpDuelingKits.Remove(kit);
  6021.             }
  6022.         }
  6023.  
  6024.         public string GetRandomKit()
  6025.         {
  6026.             VerifyKits();
  6027.  
  6028.             if (Random.value < lesserKitChance && lpDuelingKits.Count > 0)
  6029.             {
  6030.                 return lpDuelingKits.GetRandom();
  6031.             }
  6032.  
  6033.             if (hpDuelingKits.Count > 0)
  6034.             {
  6035.                 return hpDuelingKits.GetRandom();
  6036.             }
  6037.  
  6038.             if (customKits.Count > 0)
  6039.             {
  6040.                 return customKits.ElementAt(Random.Range(0, customKits.Count)).Key;
  6041.             }
  6042.  
  6043.             return null;
  6044.         }
  6045.  
  6046.         public void Initiate(BasePlayer player, BasePlayer target, bool checkInventory, DuelingZone destZone)
  6047.         {
  6048.             try
  6049.             {
  6050.                 if (player == null || target == null || destZone == null)
  6051.                     return;
  6052.  
  6053.                 dataRequests.Remove(player.UserIDString);
  6054.                 dataRequests.Remove(target.UserIDString);
  6055.  
  6056.                 if (checkInventory)
  6057.                 {
  6058.                     if (!IsNewman(player))
  6059.                     {
  6060.                         Message(player, msg("MustBeNaked", player.UserIDString));
  6061.                         Message(target, msg("DuelMustBeNaked", target.UserIDString, player.displayName));
  6062.                         return;
  6063.                     }
  6064.  
  6065.                     if (!IsNewman(target))
  6066.                     {
  6067.                         Message(target, msg("MustBeNaked", player.UserIDString));
  6068.                         Message(player, msg("DuelMustBeNaked", player.UserIDString, target.displayName));
  6069.                         return;
  6070.                     }
  6071.                 }
  6072.  
  6073.                 if (!DuelTerritory(player.transform.position) || !duelsData.Homes.ContainsKey(player.UserIDString))
  6074.                 {
  6075.                     var ppos = player.transform.position;
  6076.                     if (IsOnConstruction(ppos)) ppos.y += 1; // prevent player from becoming stuck or dying when teleported home
  6077.                     duelsData.Homes[player.UserIDString] = ppos.ToString();
  6078.                 }
  6079.  
  6080.                 if (!DuelTerritory(target.transform.position) || !duelsData.Homes.ContainsKey(target.UserIDString))
  6081.                 {
  6082.                     var tpos = target.transform.position;
  6083.                     if (IsOnConstruction(tpos)) tpos.y += 1;
  6084.                     duelsData.Homes[target.UserIDString] = tpos.ToString();
  6085.                 }
  6086.  
  6087.                 var playerSpawn = destZone.Spawns.GetRandom();
  6088.                 var targetSpawn = playerSpawn;
  6089.                 float dist = -100f;
  6090.  
  6091.                 foreach (var spawn in destZone.Spawns) // get the furthest spawn point away from the player and assign it to target
  6092.                 {
  6093.                     float distance = Vector3.Distance(spawn, playerSpawn);
  6094.  
  6095.                     if (distance > dist)
  6096.                     {
  6097.                         dist = distance;
  6098.                         targetSpawn = spawn;
  6099.                     }
  6100.                 }
  6101.  
  6102.                 string kit = GetKit(player, target);
  6103.                 duelsData.Kits[player.UserIDString] = kit;
  6104.                 duelsData.Kits[target.UserIDString] = kit;
  6105.  
  6106.                 Teleport(player, playerSpawn);
  6107.                 Teleport(target, targetSpawn);
  6108.  
  6109.                 if (debugMode)
  6110.                     Puts($"{player.displayName} and {target.displayName} have entered a duel.");
  6111.  
  6112.                 RemoveFromQueue(player.UserIDString);
  6113.                 RemoveFromQueue(target.UserIDString);
  6114.  
  6115.                 if (immunityTime >= 1)
  6116.                 {
  6117.                     dataImmunity[player.UserIDString] = TimeStamp() + immunityTime;
  6118.                     dataImmunity[target.UserIDString] = TimeStamp() + immunityTime;
  6119.                     dataImmunitySpawns[player.UserIDString] = playerSpawn;
  6120.                     dataImmunitySpawns[target.UserIDString] = targetSpawn;
  6121.                 }
  6122.  
  6123.                 dataDuelists[player.UserIDString] = target.UserIDString;
  6124.                 dataDuelists[target.UserIDString] = player.UserIDString;
  6125.                 SubscribeHooks(true);
  6126.  
  6127.                 Message(player, msg("NowDueling", player.UserIDString, target.displayName));
  6128.                 Message(target, msg("NowDueling", target.UserIDString, player.displayName));
  6129.  
  6130.             }
  6131.             catch (Exception ex)
  6132.             {
  6133.                 SubscribeHooks(false);
  6134.                 duelsData.DuelsEnabled = false;
  6135.                 SaveData();
  6136.  
  6137.                 Puts("---");
  6138.                 Puts("Plugin disabled: {0} --- {1}", ex.Message, ex.StackTrace);
  6139.                 Puts("---");
  6140.  
  6141.                 ResetDuelist(player.UserIDString);
  6142.                 ResetDuelist(target.UserIDString);
  6143.             }
  6144.         }
  6145.  
  6146.         // manually check as players may not be in a clan or on a friends list
  6147.  
  6148.         public bool IsAllied(string playerId, string targetId)
  6149.         {
  6150.             var player = BasePlayer.Find(playerId);
  6151.             var target = BasePlayer.Find(targetId);
  6152.  
  6153.             return player != null && target != null && IsAllied(player, target);
  6154.         }
  6155.  
  6156.         public bool IsAllied(BasePlayer player, BasePlayer target)
  6157.         {
  6158.             if (player.IsAdmin && target.IsAdmin)
  6159.                 return false;
  6160.  
  6161.             return IsOnSameTeam(player, target) || IsInSameClan(player, target) || IsAuthorizing(player, target) || IsBunked(player, target) || IsCodeAuthed(player, target) || IsInSameBase(player, target);
  6162.         }
  6163.  
  6164.         public bool IsOnSameTeam(BasePlayer player, BasePlayer target)
  6165.         {
  6166.             return player.currentTeam != 0 && player.Team.members.Contains(target.userID);
  6167.         }
  6168.  
  6169.         public bool IsInSameClan(BasePlayer player, BasePlayer target) // 1st method.
  6170.         {
  6171.             return Clans.CanCall() && Convert.ToBoolean(Clans?.Call("IsMemberOrAlly", player.UserIDString, target.UserIDString));
  6172.         }
  6173.  
  6174.         private bool IsAuthorizing(BasePlayer player, BasePlayer target) // 2nd method.
  6175.         {
  6176.             var privs = BaseNetworkable.serverEntities.OfType<BuildingPrivlidge>();
  6177.  
  6178.             return privs.Exists(priv => priv.IsAuthed(player) && priv.IsAuthed(target));
  6179.         }
  6180.  
  6181.         private bool IsBunked(BasePlayer player, BasePlayer target) // 3rd method. thanks @i_love_code for helping with this too
  6182.         {
  6183.             var bags = SleepingBag.FindForPlayer(target.userID, true);
  6184.  
  6185.             if (bags.Length > 0)
  6186.             {
  6187.                 foreach (var a in SleepingBag.FindForPlayer(player.userID, true))
  6188.                 {
  6189.                     if (bags.Exists(b => a.buildingID == b.buildingID))
  6190.                     {
  6191.                         return true;
  6192.                     }
  6193.                 }
  6194.             }
  6195.  
  6196.             return false;
  6197.         }
  6198.  
  6199.         private bool IsCodeAuthed(BasePlayer player, BasePlayer target) // 4th method
  6200.         {
  6201.             foreach (CodeLock codelock in BaseNetworkable.serverEntities.OfType<CodeLock>())
  6202.             {
  6203.                 if (codelock.whitelistPlayers.Contains(player.userID) && codelock.whitelistPlayers.Contains(target.userID))
  6204.                 {
  6205.                     return true;
  6206.                 }
  6207.             }
  6208.  
  6209.             return false;
  6210.         }
  6211.  
  6212.         private bool IsInSameBase(BasePlayer player, BasePlayer target) // 5th method
  6213.         {            
  6214.             var privs = BaseNetworkable.serverEntities.OfType<BuildingPrivlidge>();
  6215.  
  6216.             foreach (var priv in privs.Where(p => p.IsAuthed(player)))
  6217.             {
  6218.                 if (priv.GetBuilding()?.decayEntities?.Exists(entity => entity.OwnerID == target.userID) == true)
  6219.                 {
  6220.                     return true;
  6221.                 }
  6222.             }
  6223.  
  6224.             foreach (var priv in privs.Where(p => p.IsAuthed(target)))
  6225.             {
  6226.                 if (priv.GetBuilding()?.decayEntities?.Exists(entity => entity.OwnerID == player.userID) == true)
  6227.                 {
  6228.                     return true;
  6229.                 }
  6230.             }
  6231.  
  6232.             return false;
  6233.         }
  6234.  
  6235.         public void Metabolize(BasePlayer player, bool set) // we don't want the elements to harm players since the zone can spawn anywhere on the map!
  6236.         {
  6237.             if (player == null)
  6238.                 return;
  6239.  
  6240.             if (set)
  6241.             {
  6242.                 player.health = 100f;
  6243.                 player.metabolism.temperature.min = 32; // immune to cold
  6244.                 player.metabolism.temperature.max = 32;
  6245.                 player.metabolism.temperature.value = 32;
  6246.                 player.metabolism.oxygen.min = 1; // immune to drowning
  6247.                 player.metabolism.oxygen.value = 1;
  6248.                 player.metabolism.poison.value = 0; // if they ate raw meat
  6249.                 player.metabolism.calories.value = player.metabolism.calories.max;
  6250.                 player.metabolism.hydration.value = player.metabolism.hydration.max;
  6251.                 player.metabolism.wetness.max = 0;
  6252.                 player.metabolism.wetness.value = 0;
  6253.                 player.metabolism.radiation_level.max = 0;
  6254.                 player.metabolism.radiation_poison.max = 0;
  6255.             }
  6256.             else
  6257.             {
  6258.                 player.metabolism.oxygen.min = 0;
  6259.                 player.metabolism.oxygen.max = 1;
  6260.                 player.metabolism.temperature.min = -100;
  6261.                 player.metabolism.temperature.max = 100;
  6262.                 player.metabolism.wetness.min = 0;
  6263.                 player.metabolism.wetness.max = 1;
  6264.                 player.metabolism.radiation_level.Reset();
  6265.                 player.metabolism.radiation_poison.Reset();
  6266.             }
  6267.  
  6268.             player.metabolism.SendChangesToClient();
  6269.         }
  6270.  
  6271.         public bool IsKit(string kit)
  6272.         {
  6273.             return Convert.ToBoolean(Kits?.Call("isKit", kit));
  6274.         }
  6275.  
  6276.         public void AwardPlayer(ulong playerId, double money, int points)
  6277.         {
  6278.             if (money == 0.0 && points == 0)
  6279.                 return;
  6280.  
  6281.             var player = BasePlayer.FindByID(playerId);
  6282.  
  6283.             if (money > 0.0)
  6284.             {
  6285.                 if (Economics.CanCall())
  6286.                 {
  6287.                     Economics?.Call("Deposit", playerId, money);
  6288.  
  6289.                     if (player != null)
  6290.                         Message(player, msg("EconomicsDeposit", player.UserIDString, money));
  6291.                 }
  6292.             }
  6293.  
  6294.             if (points > 0)
  6295.             {
  6296.                 if (ServerRewards.CanCall())
  6297.                 {
  6298.                     var success = ServerRewards?.Call("AddPoints", playerId, points);
  6299.  
  6300.                     if (player != null && success != null && success is bool && (bool)success)
  6301.                         Message(player, msg("ServerRewardPoints", player.UserIDString, points));
  6302.                 }
  6303.             }
  6304.         }
  6305.  
  6306.         public void GivePlayerKit(BasePlayer player)
  6307.         {
  6308.             if (player == null)
  6309.                 return;
  6310.  
  6311.             string kit = duelsData.Kits.ContainsKey(player.UserIDString) ? duelsData.Kits[player.UserIDString] : string.Empty;
  6312.  
  6313.             duelsData.Kits.Remove(player.UserIDString);
  6314.             player.inventory.Strip();
  6315.  
  6316.             if (!string.IsNullOrEmpty(kit))
  6317.             {
  6318.                 if (Kits.CanCall() && IsKit(kit))
  6319.                 {
  6320.                     object success = Kits.Call("GiveKit", player, kit);
  6321.  
  6322.                     if (success is bool && (bool)success)
  6323.                     {
  6324.                         return;
  6325.                     }
  6326.                 }
  6327.  
  6328.                 if (string.IsNullOrEmpty(kit))
  6329.                 {
  6330.                     kit = duelsData.CustomKits.ContainsKey(player.UserIDString) ? duelsData.CustomKits[player.UserIDString] : null;
  6331.                 }
  6332.  
  6333.                 if (GiveCustomKit(player, kit))
  6334.                 {
  6335.                     return;
  6336.                 }
  6337.             }
  6338.  
  6339.             if (Kits.CanCall())
  6340.             {
  6341.                 kit = GetRandomKit();
  6342.  
  6343.                 if (!string.IsNullOrEmpty(kit))
  6344.                 {
  6345.                     object success = Kits.Call("GiveKit", player, kit);
  6346.  
  6347.                     if (success is bool && (bool)success)
  6348.                     {
  6349.                         return;
  6350.                     }
  6351.                 }
  6352.             }
  6353.  
  6354.             // give a basic kit when no kit is provided, or the provided kit is invalid
  6355.             player.inventory.GiveItem(ItemManager.CreateByItemID(1443579727, 1, 0)); // bow
  6356.             player.inventory.GiveItem(ItemManager.CreateByItemID(-1234735557, 50, 0)); // arrows
  6357.             player.inventory.GiveItem(ItemManager.CreateByItemID(1602646136, 1, 0)); // stone spear
  6358.             player.inventory.GiveItem(ItemManager.CreateByItemID(-2072273936, 5, 0)); // bandage
  6359.             player.inventory.GiveItem(ItemManager.CreateByItemID(254522515, 3, 0)); // medkit
  6360.             player.inventory.GiveItem(ItemManager.CreateByItemID(1079279582, 4, 0)); // syringe
  6361.         }
  6362.  
  6363.         public bool GiveCustomKit(BasePlayer player, string kit)
  6364.         {
  6365.             if (string.IsNullOrEmpty(kit) || customKits.Count == 0 || !customKits.ContainsKey(kit))
  6366.                 return false;
  6367.  
  6368.             bool success = false;
  6369.  
  6370.             foreach (var dki in customKits[kit])
  6371.             {
  6372.                 Item item = ItemManager.CreateByName(dki.shortname, dki.amount, dki.skin);
  6373.  
  6374.                 if (item == null)
  6375.                 {
  6376.                     Puts("Invalid shortname {0}", dki.shortname);
  6377.                     continue;
  6378.                 }
  6379.  
  6380.                 if (item.skin == 0 && useRandomSkins)
  6381.                 {
  6382.                     var skins = GetItemSkins(item.info);
  6383.  
  6384.                     if (skins.Count > 0)
  6385.                         item.skin = skins.GetRandom();
  6386.                 }
  6387.  
  6388.                 if (dki.mods != null)
  6389.                 {
  6390.                     foreach (string shortname in dki.mods)
  6391.                     {
  6392.                         Item mod = ItemManager.CreateByName(shortname, 1);
  6393.  
  6394.                         if (mod != null)
  6395.                             item.contents.AddItem(mod.info, 1);
  6396.                     }
  6397.                 }
  6398.  
  6399.                 var heldEntity = item.GetHeldEntity();
  6400.  
  6401.                 if (heldEntity != null)
  6402.                 {
  6403.                     if (item.skin != 0)
  6404.                         heldEntity.skinID = item.skin;
  6405.  
  6406.                     var weapon = heldEntity as BaseProjectile;
  6407.  
  6408.                     if (weapon != null)
  6409.                     {
  6410.                         if (!string.IsNullOrEmpty(dki.ammo))
  6411.                         {
  6412.                             var def = ItemManager.FindItemDefinition(dki.ammo);
  6413.  
  6414.                             if (def != null)
  6415.                                 weapon.primaryMagazine.ammoType = def;
  6416.                         }
  6417.  
  6418.                         weapon.primaryMagazine.contents = 0; // unload the old ammo
  6419.                         weapon.SendNetworkUpdateImmediate(false); // update
  6420.                         weapon.primaryMagazine.contents = weapon.primaryMagazine.capacity; // load new ammo
  6421.                     }
  6422.                 }
  6423.  
  6424.                 var container = dki.container == "belt" ? player.inventory.containerBelt : dki.container == "wear" ? player.inventory.containerWear : player.inventory.containerMain;
  6425.  
  6426.                 item.MarkDirty();
  6427.                 if (item.MoveToContainer(container, dki.slot < 0 || dki.slot > container.capacity - 1 ? -1 : dki.slot, true))
  6428.                 {
  6429.                     success = true;
  6430.                 }
  6431.                 else
  6432.                 {
  6433.                     item.Remove(0f);
  6434.                 }
  6435.             }
  6436.  
  6437.             return success;
  6438.         }
  6439.  
  6440.         private void DuelAnnouncement(bool bypass)
  6441.         {
  6442.             if (!bypass && (!duelsData.DuelsEnabled || !useAnnouncement))
  6443.                 return;
  6444.  
  6445.             if (BasePlayer.activePlayerList.Count < 3)
  6446.                 return;
  6447.            
  6448.             string console = msg("DuelAnnouncement");
  6449.             string disabled = msg("Disabled");
  6450.  
  6451.             console = console.Replace("{duelChatCommand}", !string.IsNullOrEmpty(szDuelChatCommand) ? szDuelChatCommand : disabled);
  6452.             console = console.Replace("{ladderCommand}", !string.IsNullOrEmpty(szDuelChatCommand) ? string.Format("{0} ladder", szDuelChatCommand) : disabled);
  6453.             console = console.Replace("{queueCommand}", !string.IsNullOrEmpty(szQueueChatCommand) ? szQueueChatCommand : disabled);
  6454.  
  6455.             if (allowBets)
  6456.                 console += msg("DuelAnnouncementBetsSuffix", null, szDuelChatCommand);
  6457.  
  6458.             Puts(RemoveFormatting(console));
  6459.  
  6460.             foreach (var player in BasePlayer.activePlayerList.Where(p => p?.displayName != null))
  6461.             {
  6462.                 string message = msg("DuelAnnouncement", player.UserIDString);
  6463.  
  6464.                 message = message.Replace("{duelChatCommand}", !string.IsNullOrEmpty(szDuelChatCommand) ? szDuelChatCommand : disabled);
  6465.                 message = message.Replace("{ladderCommand}", !string.IsNullOrEmpty(szDuelChatCommand) ? string.Format("{0} ladder", szDuelChatCommand) : disabled);
  6466.                 message = message.Replace("{queueCommand}", !string.IsNullOrEmpty(szQueueChatCommand) ? szQueueChatCommand : disabled);
  6467.  
  6468.                 if (allowBets)
  6469.                     message += msg("DuelAnnouncementBetsSuffix", player.UserIDString, szDuelChatCommand);
  6470.  
  6471.                 Message(player, string.Format("{0} <color=#C0C0C0>{1}</color>", lang.GetMessage("Prefix", this, player.UserIDString), message));
  6472.             }
  6473.         }
  6474.  
  6475.         public bool CreateBet(BasePlayer player, int betAmount, BetInfo betInfo)
  6476.         {
  6477.             if (betAmount > betInfo.max) // adjust the bet to the maximum since they clearly want to do this
  6478.                 betAmount = betInfo.max;
  6479.  
  6480.             int amount = player.inventory.GetAmount(betInfo.itemid);
  6481.  
  6482.             if (amount == 0)
  6483.             {
  6484.                 Message(player, msg("BetZero", player.UserIDString));
  6485.                 return false;
  6486.             }
  6487.  
  6488.             if (amount < betAmount) // obviously they're just trying to see how this works. we won't adjust it here.
  6489.             {
  6490.                 Message(player, msg("BetNotEnough", player.UserIDString));
  6491.                 return false;
  6492.             }
  6493.  
  6494.             var takenItems = new List<Item>();
  6495.             int takenAmount = player.inventory.Take(takenItems, betInfo.itemid, betAmount);
  6496.  
  6497.             if (takenAmount == betAmount)
  6498.             {
  6499.                 var bet = new BetInfo
  6500.                 {
  6501.                     itemid = betInfo.itemid,
  6502.                     amount = betAmount,
  6503.                     trigger = betInfo.trigger
  6504.                 };
  6505.  
  6506.                 duelsData.Bets.Add(player.UserIDString, bet);
  6507.  
  6508.                 string message = msg("BetPlaced", player.UserIDString, betInfo.trigger, betAmount);
  6509.  
  6510.                 if (allowBetRefund)
  6511.                     message += msg("BetRefundSuffix", player.UserIDString, szDuelChatCommand);
  6512.                 else if (allowBetForfeit)
  6513.                     message += msg("BetForfeitSuffix", player.UserIDString, szDuelChatCommand);
  6514.  
  6515.                 Message(player, message);
  6516.                 Puts("{0} bet {1} ({2})", player.displayName, betInfo.trigger, betAmount);
  6517.  
  6518.                 foreach (Item item in takenItems.ToList())
  6519.                     item.Remove(0.1f);
  6520.  
  6521.                 return true;
  6522.             }
  6523.  
  6524.             if (takenItems.Count > 0)
  6525.             {
  6526.                 foreach (Item item in takenItems.ToList())
  6527.                     player.GiveItem(item, BaseEntity.GiveItemReason.Generic);
  6528.  
  6529.                 takenItems.Clear();
  6530.             }
  6531.  
  6532.             return false;
  6533.         }
  6534.  
  6535.         private void GetWorkshopIDs(int code, string response)
  6536.         {
  6537.             if (!string.IsNullOrEmpty(response) && code == 200)
  6538.             {
  6539.                 var items = JsonConvert.DeserializeObject<ItemSchema>(response).items;
  6540.  
  6541.                 foreach (var item in items)
  6542.                 {
  6543.                     if (string.IsNullOrEmpty(item.itemshortname) || string.IsNullOrEmpty(item.workshopdownload))
  6544.                         continue;
  6545.  
  6546.                     if (!workshopskinsCache.ContainsKey(item.itemshortname))
  6547.                         workshopskinsCache.Add(item.itemshortname, new List<ulong>());
  6548.  
  6549.                     workshopskinsCache[item.itemshortname].Add(Convert.ToUInt64(item.workshopdownload));
  6550.                 }
  6551.             }
  6552.         }
  6553.  
  6554.         public List<ulong> GetItemSkins(ItemDefinition def)
  6555.         {
  6556.             if (!skinsCache.ContainsKey(def.shortname))
  6557.             {
  6558.                 var skins = new List<ulong>();
  6559.  
  6560.                 skins.AddRange(def.skins.Select(skin => Convert.ToUInt64(skin.id)));
  6561.  
  6562.                 if (useWorkshopSkins && workshopskinsCache.ContainsKey(def.shortname))
  6563.                 {
  6564.                     skins.AddRange(workshopskinsCache[def.shortname]);
  6565.                     workshopskinsCache.Remove(def.shortname);
  6566.                 }
  6567.  
  6568.                 if (skins.Contains(0uL))
  6569.                     skins.Remove(0uL);
  6570.  
  6571.                 skinsCache.Add(def.shortname, skins);
  6572.             }
  6573.  
  6574.             return skinsCache[def.shortname];
  6575.         }
  6576.  
  6577.         private void RemoveRequests(BasePlayer player)
  6578.         {
  6579.             foreach (var entry in dataRequests.ToList())
  6580.             {
  6581.                 if (entry.Key == player.UserIDString || entry.Value == player.UserIDString)
  6582.                 {
  6583.                     dataRequests.Remove(entry.Key);
  6584.                 }
  6585.             }
  6586.         }
  6587.  
  6588.         private void UpdateMatchSizeStats(string playerId, bool winner, bool loser, int teamSize)
  6589.         {
  6590.             string key = teamSize.ToString();
  6591.  
  6592.             if (winner)
  6593.             {
  6594.                 if (!duelsData.MatchSizesVictoriesSeed.ContainsKey(key)) duelsData.MatchSizesVictoriesSeed.Add(key, new Dictionary<string, int>());
  6595.                 if (!duelsData.MatchSizesVictories.ContainsKey(key)) duelsData.MatchSizesVictories.Add(key, new Dictionary<string, int>());
  6596.                 if (!duelsData.MatchSizesVictoriesSeed[key].ContainsKey(playerId)) duelsData.MatchSizesVictoriesSeed[key].Add(playerId, 1);
  6597.                 else duelsData.MatchSizesVictoriesSeed[key][playerId]++;
  6598.                 if (!duelsData.MatchSizesVictories[key].ContainsKey(playerId)) duelsData.MatchSizesVictories[key].Add(playerId, 1);
  6599.                 else duelsData.MatchSizesVictories[key][playerId]++;
  6600.             }
  6601.             if (loser)
  6602.             {
  6603.                 if (!duelsData.MatchSizesLossesSeed.ContainsKey(key)) duelsData.MatchSizesLossesSeed.Add(key, new Dictionary<string, int>());
  6604.                 if (!duelsData.MatchSizesLosses.ContainsKey(key)) duelsData.MatchSizesLosses.Add(key, new Dictionary<string, int>());
  6605.                 if (!duelsData.MatchSizesLossesSeed[key].ContainsKey(playerId)) duelsData.MatchSizesLossesSeed[key].Add(playerId, 1);
  6606.                 else duelsData.MatchSizesLossesSeed[key][playerId]++;
  6607.                 if (!duelsData.MatchSizesLosses[key].ContainsKey(playerId)) duelsData.MatchSizesLosses[key].Add(playerId, 1);
  6608.                 else duelsData.MatchSizesLosses[key][playerId]++;
  6609.             }
  6610.         }
  6611.  
  6612.         private void UpdateMatchStats(string playerId, bool winner, bool loser, bool death, bool kill)
  6613.         {
  6614.             if (winner)
  6615.             {
  6616.                 if (!duelsData.MatchVictories.ContainsKey(playerId)) duelsData.MatchVictories.Add(playerId, 1);
  6617.                 else duelsData.MatchVictories[playerId]++;
  6618.                 if (!duelsData.MatchVictoriesSeed.ContainsKey(playerId)) duelsData.MatchVictoriesSeed.Add(playerId, 1);
  6619.                 else duelsData.MatchVictoriesSeed[playerId]++;
  6620.             }
  6621.             if (loser)
  6622.             {
  6623.                 if (!duelsData.MatchLosses.ContainsKey(playerId)) duelsData.MatchLosses.Add(playerId, 1);
  6624.                 else duelsData.MatchLosses[playerId]++;
  6625.                 if (!duelsData.MatchLossesSeed.ContainsKey(playerId)) duelsData.MatchLossesSeed.Add(playerId, 1);
  6626.                 else duelsData.MatchLossesSeed[playerId]++;
  6627.             }
  6628.             if (death)
  6629.             {
  6630.                 if (!duelsData.MatchDeaths.ContainsKey(playerId)) duelsData.MatchDeaths.Add(playerId, 1);
  6631.                 else duelsData.MatchDeaths[playerId]++;
  6632.                 if (!duelsData.MatchDeathsSeed.ContainsKey(playerId)) duelsData.MatchDeathsSeed.Add(playerId, 1);
  6633.                 else duelsData.MatchDeathsSeed[playerId]++;
  6634.             }
  6635.             if (kill)
  6636.             {
  6637.                 if (!duelsData.MatchKills.ContainsKey(playerId)) duelsData.MatchKills.Add(playerId, 1);
  6638.                 else duelsData.MatchKills[playerId]++;
  6639.                 if (!duelsData.MatchKillsSeed.ContainsKey(playerId)) duelsData.MatchKillsSeed.Add(playerId, 1);
  6640.                 else duelsData.MatchKillsSeed[playerId]++;
  6641.             }
  6642.         }
  6643.  
  6644.         #region SpawnPoints
  6645.  
  6646.         public void SendSpawnHelp(BasePlayer player)
  6647.         {
  6648.             Message(player, msg("SpawnCount", player.UserIDString, duelsData.Spawns.Count));
  6649.             Message(player, msg("SpawnAdd", player.UserIDString, szDuelChatCommand));
  6650.             Message(player, msg("SpawnHere", player.UserIDString, szDuelChatCommand));
  6651.             Message(player, msg("SpawnRemove", player.UserIDString, szDuelChatCommand, spRemoveOneMaxDistance));
  6652.             Message(player, msg("SpawnRemoveAll", player.UserIDString, szDuelChatCommand, spRemoveAllMaxDistance));
  6653.             Message(player, msg("SpawnWipe", player.UserIDString, szDuelChatCommand));
  6654.         }
  6655.  
  6656.         public void AddSpawnPoint(BasePlayer player, bool useHit)
  6657.         {
  6658.             var spawn = player.transform.position;
  6659.  
  6660.             if (useHit)
  6661.             {
  6662.                 RaycastHit hit;
  6663.                 if (!Physics.Raycast(player.eyes.HeadRay(), out hit, Mathf.Infinity, wallMask))
  6664.                 {
  6665.                     Message(player, msg("FailedRaycast", player.UserIDString));
  6666.                     return;
  6667.                 }
  6668.  
  6669.                 spawn = hit.point;
  6670.             }
  6671.  
  6672.             if (duelsData.Spawns.Contains(spawn.ToString()))
  6673.             {
  6674.                 Message(player, msg("SpawnExists", player.UserIDString));
  6675.                 return;
  6676.             }
  6677.  
  6678.             duelsData.Spawns.Add(spawn.ToString());
  6679.             player.SendConsoleCommand("ddraw.text", spDrawTime, Color.green, spawn, "+S");
  6680.             Message(player, msg("SpawnAdded", player.UserIDString, FormatPosition(spawn)));
  6681.         }
  6682.  
  6683.         public void RemoveSpawnPoint(BasePlayer player)
  6684.         {
  6685.             float radius = spRemoveOneMaxDistance;
  6686.             var spawn = Vector3.zero;
  6687.             float dist = radius;
  6688.  
  6689.             foreach (var entry in duelsData.Spawns.ToList())
  6690.             {
  6691.                 var _spawn = entry.ToVector3();
  6692.                 float distance = Vector3.Distance(player.transform.position, _spawn);
  6693.  
  6694.                 if (distance < dist)
  6695.                 {
  6696.                     dist = distance;
  6697.                     spawn = _spawn;
  6698.                 }
  6699.             }
  6700.  
  6701.             if (spawn != Vector3.zero)
  6702.             {
  6703.                 duelsData.Spawns.Remove(spawn.ToString());
  6704.                 player.SendConsoleCommand("ddraw.text", spDrawTime, Color.red, spawn, "-S");
  6705.                 Message(player, msg("SpawnRemoved", player.UserIDString, 1));
  6706.             }
  6707.             else
  6708.                 Message(player, msg("SpawnNoneFound", player.UserIDString, radius));
  6709.         }
  6710.  
  6711.         public void RemoveSpawnPoints(BasePlayer player)
  6712.         {
  6713.             int count = 0;
  6714.  
  6715.             foreach (var entry in duelsData.Spawns.ToList())
  6716.             {
  6717.                 var spawn = entry.ToVector3();
  6718.  
  6719.                 if (Vector3.Distance(player.transform.position, spawn) <= spRemoveAllMaxDistance)
  6720.                 {
  6721.                     count++;
  6722.                     duelsData.Spawns.Remove(entry);
  6723.                     player.SendConsoleCommand("ddraw.text", spDrawTime, Color.red, spawn, "-S");
  6724.                 }
  6725.             }
  6726.  
  6727.             if (count == 0)
  6728.                 Message(player, msg("SpawnNoneFound", player.UserIDString, spRemoveAllMaxDistance));
  6729.             else
  6730.                 Message(player, msg("SpawnRemoved", player.UserIDString, count));
  6731.         }
  6732.  
  6733.         public void WipeSpawnPoints(BasePlayer player)
  6734.         {
  6735.             if (duelsData.Spawns.Count == 0)
  6736.             {
  6737.                 Message(player, msg("SpawnNoneExist", player.UserIDString));
  6738.                 return;
  6739.             }
  6740.  
  6741.             var spawns = duelsData.Spawns.Select(spawn => spawn.ToVector3()).ToList();
  6742.  
  6743.             foreach (var spawn in spawns)
  6744.                 player.SendConsoleCommand("ddraw.text", 30f, Color.red, spawn, "-S");
  6745.  
  6746.             int amount = duelsData.Spawns.Count;
  6747.             duelsData.Spawns.Clear();
  6748.             spawns.Clear();
  6749.             Message(player, msg("SpawnWiped", player.UserIDString, amount));
  6750.         }
  6751.  
  6752.         public List<Vector3> GetSpawnPoints(DuelingZone zone)
  6753.         {
  6754.             return duelsData.Spawns.Select(entry => entry.ToVector3()).Where(spawn => zone.Distance(spawn) < zoneRadius).ToList();
  6755.         }
  6756.  
  6757.         public string FormatBone(string source)
  6758.         {
  6759.             if (string.IsNullOrEmpty(source))
  6760.                 return "Chest";
  6761.  
  6762.             foreach (var entry in boneTags)
  6763.                 source = source.Replace(entry.Key, entry.Value);
  6764.  
  6765.             return string.Join(" ", source.Split(' ').Select(str => str.SentenceCase()));
  6766.         }
  6767.  
  6768.         public string FormatPosition(Vector3 position)
  6769.         {
  6770.             string x = position.x.ToString("N2");
  6771.             string y = position.y.ToString("N2");
  6772.             string z = position.z.ToString("N2");
  6773.  
  6774.             return $"{x} {y} {z}";
  6775.         }
  6776.  
  6777.         #endregion
  6778.  
  6779.         #region UI Creation
  6780.  
  6781.         private readonly List<string> createUI = new List<string>();
  6782.         private readonly List<string> duelistUI = new List<string>();
  6783.         private readonly List<string> kitsUI = new List<string>();
  6784.         private readonly List<string> matchesUI = new List<string>();
  6785.  
  6786.         [ConsoleCommand("UI_DuelistCommand")]
  6787.         private void ccmdDuelistUI(ConsoleSystem.Arg arg)
  6788.         {
  6789.             var player = arg.Player();
  6790.  
  6791.             if (!player || !arg.HasArgs())
  6792.                 return;
  6793.  
  6794.             switch (arg.Args[0].ToLower())
  6795.             {
  6796.                 case "accept":
  6797.                     {
  6798.                         if (dataRequests.ContainsValue(player.UserIDString))
  6799.                         {
  6800.                             cmdDuel(player, szDuelChatCommand, new[] { "accept" });
  6801.                             break;
  6802.                         }
  6803.                         if (tdmRequests.ContainsValue(player.UserIDString))
  6804.                         {
  6805.                             cmdTDM(player, szMatchChatCommand, new[] { "accept" });
  6806.                             break;
  6807.                         }
  6808.  
  6809.                         Message(player, msg("NoPendingRequests2", player.UserIDString));
  6810.                         break;
  6811.                     }
  6812.                 case "decline":
  6813.                     {
  6814.                         if (dataRequests.ContainsKey(player.UserIDString) || dataRequests.ContainsValue(player.UserIDString))
  6815.                         {
  6816.                             cmdDuel(player, szDuelChatCommand, new[] { "decline" });
  6817.                             break;
  6818.                         }
  6819.  
  6820.                         var deathmatch = tdmMatches.FirstOrDefault(x => x.GetTeam(player) != Team.None);
  6821.  
  6822.                         if (deathmatch != null || tdmRequests.ContainsValue(player.UserIDString) || tdmRequests.ContainsKey(player.UserIDString))
  6823.                         {
  6824.                             cmdTDM(player, szMatchChatCommand, new[] { "decline" });
  6825.                             break;
  6826.                         }
  6827.  
  6828.                         Message(player, msg("NoPendingRequests", player.UserIDString));
  6829.                         break;
  6830.                     }
  6831.                 case "closeui":
  6832.                     {
  6833.                         DestroyUI(player);
  6834.                         return;
  6835.                     }
  6836.                 case "kits":
  6837.                     {
  6838.                         ToggleKitUI(player);
  6839.                         break;
  6840.                     }
  6841.                 case "public":
  6842.                     {
  6843.                         cmdTDM(player, szMatchChatCommand, new[] { "public" });
  6844.                         break;
  6845.                     }
  6846.                 case "requeue":
  6847.                     {
  6848.                         if (IsDueling(player) || InDeathmatch(player))
  6849.                             return;
  6850.  
  6851.                         if (sendHomeRequeue)
  6852.                         {
  6853.                             CuiHelper.DestroyUi(player, "DuelistUI_Defeat");
  6854.                             SendHome(player);
  6855.                         }
  6856.                         else CreateDefeatUI(player);
  6857.                         cmdQueue(player, szQueueChatCommand, new string[0]);
  6858.                         return;
  6859.                     }
  6860.                 case "queue":
  6861.                     {
  6862.                         if (IsDueling(player) || InDeathmatch(player))
  6863.                             break;
  6864.  
  6865.                         cmdQueue(player, szQueueChatCommand, new string[0]);
  6866.                         break;
  6867.                     }
  6868.                 case "respawn":
  6869.                     {
  6870.                         CuiHelper.DestroyUi(player, "DuelistUI_Defeat");
  6871.  
  6872.                         if (!InEvent(player) && DuelTerritory(player.transform.position))
  6873.                             SendHome(player);
  6874.  
  6875.                         return;
  6876.                     }
  6877.                 case "ready":
  6878.                 case "readyon":
  6879.                 case "readyoff":
  6880.                     {
  6881.                         ReadyUp(player);
  6882.  
  6883.                         if (DuelTerritory(player.transform.position))
  6884.                         {
  6885.                             CreateDefeatUI(player);
  6886.                             return;
  6887.                         }
  6888.  
  6889.                         break;
  6890.                     }
  6891.                 case "tdm":
  6892.                     {
  6893.                         ToggleMatchUI(player);
  6894.                         break;
  6895.                     }
  6896.                 case "kit":
  6897.                     {
  6898.                         if (arg.Args.Length != 2)
  6899.                             return;
  6900.  
  6901.                         var match = GetMatch(player);
  6902.  
  6903.                         if (match != null && match.IsHost(player))
  6904.                         {
  6905.                             if (!match.IsStarted)
  6906.                                 match.Kit = GetVerifiedKit(arg.Args[1]);
  6907.  
  6908.                             break;
  6909.                         }
  6910.  
  6911.                         if (duelsData.CustomKits.ContainsKey(player.UserIDString) && duelsData.CustomKits[player.UserIDString] == arg.Args[1])
  6912.                         {
  6913.                             duelsData.CustomKits.Remove(player.UserIDString);
  6914.                             Message(player, msg("ResetKit", player.UserIDString));
  6915.                             break;
  6916.                         }
  6917.  
  6918.                         string kit = GetVerifiedKit(arg.Args[1]);
  6919.  
  6920.                         if (string.IsNullOrEmpty(kit))
  6921.                             break;
  6922.  
  6923.                         duelsData.CustomKits[player.UserIDString] = kit;
  6924.                         Message(player, msg("KitSet", player.UserIDString, kit));
  6925.                         break;
  6926.                     }
  6927.                 case "joinmatch":
  6928.                     {
  6929.                         if (arg.Args.Length != 2)
  6930.                             return;
  6931.  
  6932.                         if (IsDueling(player))
  6933.                             break;
  6934.  
  6935.                         var match = GetMatch(player);
  6936.  
  6937.                         if (match != null)
  6938.                         {
  6939.                             if (match.IsStarted)
  6940.                                 break;
  6941.  
  6942.                             match.RemoveMatchPlayer(player);
  6943.                         }
  6944.  
  6945.                         var newMatch = tdmMatches.FirstOrDefault(x => x.Id == arg.Args[1] && x.IsPublic);
  6946.  
  6947.                         if (newMatch == null || newMatch.IsFull() || newMatch.IsStarted || newMatch.IsOver)
  6948.                         {
  6949.                             Message(player, msg("MatchNoLongerValid", player.UserIDString));
  6950.                             break;
  6951.                         }
  6952.  
  6953.                         if (newMatch.GetTeam(player) != Team.None)
  6954.                             break;
  6955.  
  6956.                         newMatch.AddMatchPlayer(player, !newMatch.IsFull(Team.Good) ? Team.Good : Team.Evil);
  6957.  
  6958.                         if (matchesUI.Contains(player.UserIDString))
  6959.                         {
  6960.                             CuiHelper.DestroyUi(player, "DuelistUI_Matches");
  6961.                             matchesUI.Remove(player.UserIDString);
  6962.                         }
  6963.  
  6964.                         break;
  6965.                     }
  6966.                 case "size":
  6967.                     {
  6968.                         if (arg.Args.Length != 2 || !arg.Args[1].All(char.IsDigit))
  6969.                             break;
  6970.  
  6971.                         cmdTDM(player, szMatchChatCommand, new[] { "size", arg.Args[1] });
  6972.                         break;
  6973.                     }
  6974.                 case "any":
  6975.                     {
  6976.                         cmdTDM(player, szMatchChatCommand, new[] { "any" });
  6977.                         break;
  6978.                     }
  6979.             }
  6980.  
  6981.             RefreshUI(player);
  6982.         }
  6983.  
  6984.         public void DestroyAllUI()
  6985.         {
  6986.             foreach (var player in BasePlayer.activePlayerList)
  6987.             {
  6988.                 DestroyUI(player);
  6989.             }
  6990.         }
  6991.  
  6992.         public bool DestroyUI(BasePlayer player)
  6993.         {
  6994.             CuiHelper.DestroyUi(player, "DuelistUI_Options");
  6995.             CuiHelper.DestroyUi(player, "DuelistUI_Kits");
  6996.             CuiHelper.DestroyUi(player, "DuelistUI_Matches");
  6997.             CuiHelper.DestroyUi(player, "DuelistUI_Announcement");
  6998.             CuiHelper.DestroyUi(player, "DuelistUI_Defeat");
  6999.             CuiHelper.DestroyUi(player, "DuelistUI_Countdown");
  7000.  
  7001.             if (readyUiList.Contains(player.UserIDString))
  7002.             {
  7003.                 CuiHelper.DestroyUi(player, "DuelistUI_Ready");
  7004.                 readyUiList.Remove(player.UserIDString);
  7005.             }
  7006.  
  7007.             if (duelistUI.Contains(player.UserIDString))
  7008.             {
  7009.                 duelistUI.Remove(player.UserIDString);
  7010.                 return true;
  7011.             }
  7012.  
  7013.             return false;
  7014.         }
  7015.  
  7016.         public void ccmdDUI(ConsoleSystem.Arg arg)
  7017.         {
  7018.             var player = arg.Player();
  7019.  
  7020.             if (!player)
  7021.                 return;
  7022.  
  7023.             if (arg.HasArgs(1))
  7024.             {
  7025.                 switch (arg.Args[0].ToLower())
  7026.                 {
  7027.                     case "on":
  7028.                         {
  7029.                             cmdDUI(player, szUIChatCommand, new string[0]);
  7030.                             return;
  7031.                         }
  7032.                     case "off":
  7033.                         {
  7034.                             DestroyUI(player);
  7035.                             return;
  7036.                         }
  7037.                 }
  7038.             }
  7039.  
  7040.             if (duelistUI.Contains(player.UserIDString))
  7041.                 DestroyUI(player);
  7042.             else
  7043.                 cmdDUI(player, szUIChatCommand, new string[0]);
  7044.         }
  7045.  
  7046.         public void cmdDUI(BasePlayer player, string command, string[] args)
  7047.         {
  7048.             DestroyUI(player);
  7049.             var buttons = new List<string>
  7050.             {
  7051.                 "UI_Accept",
  7052.                 "UI_Decline",
  7053.                 "UI_Kits",
  7054.                 "UI_Public",
  7055.                 "UI_Queue",
  7056.                 "UI_TDM",
  7057.                 "UI_Any",
  7058.                 duelsData.AutoReady.Contains(player.UserIDString) ? "UI_ReadyOn" : "UI_ReadyOff",
  7059.             };
  7060.             var element = UI.CreateElementContainer("DuelistUI_Options", "0 0 0 0.5", "0.915 0.148", "0.981 0.441", guiUseCursor);
  7061.  
  7062.             if (guiUseCloseButton)
  7063.                 UI.CreateButton(ref element, "DuelistUI_Options", "0.29 0.49 0.69 0.5", "X", 14, "0.7 0.9", "0.961 0.98", "UI_DuelistCommand closeui");
  7064.  
  7065.             for (int number = 0; number < buttons.Count; number++)
  7066.             {
  7067.                 var pos = UI.CalcButtonPos(number + 1, 2.075f);
  7068.                 string uicommand = buttons[number].Replace("UI_", "").ToLower();
  7069.                 string text = msg(buttons[number], player.UserIDString);
  7070.                 UI.CreateButton(ref element, "DuelistUI_Options", "0.29 0.49 0.69 0.5", text, 14, $"{pos[0]} {pos[1]}", $"{pos[2]} {pos[3]}", $"UI_DuelistCommand {uicommand}");
  7071.             }
  7072.  
  7073.             if (!duelistUI.Contains(player.UserIDString))
  7074.                 duelistUI.Add(player.UserIDString);
  7075.  
  7076.             CuiHelper.AddUi(player, element);
  7077.         }
  7078.  
  7079.         public void RefreshUI(BasePlayer player)
  7080.         {
  7081.             cmdDUI(player, szUIChatCommand, new string[0]);
  7082.  
  7083.             if (kitsUI.Contains(player.UserIDString))
  7084.             {
  7085.                 kitsUI.Remove(player.UserIDString);
  7086.                 ToggleKitUI(player);
  7087.             }
  7088.             if (matchesUI.Contains(player.UserIDString))
  7089.             {
  7090.                 matchesUI.Remove(player.UserIDString);
  7091.                 ToggleMatchUI(player);
  7092.             }
  7093.         }
  7094.  
  7095.         public void ToggleMatchUI(BasePlayer player)
  7096.         {
  7097.             if (matchesUI.Contains(player.UserIDString))
  7098.             {
  7099.                 CuiHelper.DestroyUi(player, "DuelistUI_Matches");
  7100.                 matchesUI.Remove(player.UserIDString);
  7101.                 return;
  7102.             }
  7103.  
  7104.             if (kitsUI.Contains(player.UserIDString))
  7105.             {
  7106.                 CuiHelper.DestroyUi(player, "DuelistUI_Kits");
  7107.                 kitsUI.Remove(player.UserIDString);
  7108.             }
  7109.  
  7110.             var element = UI.CreateElementContainer("DuelistUI_Matches", "0 0 0 0.5", "0.669 0.148", "0.903 0.541");
  7111.             var matches = tdmMatches.Where(x => x.IsPublic && !x.IsStarted && !x.IsFull()).ToList();
  7112.  
  7113.             for (int number = 0; number < matches.Count; number++)
  7114.             {
  7115.                 var pos = UI.CalcButtonPos(number);
  7116.                 UI.CreateButton(ref element, "DuelistUI_Matches", "0.29 0.49 0.69 0.5", matches[number].Versus, 12, $"{pos[0]} {pos[1]}", $"{pos[2]} {pos[3]}", $"UI_DuelistCommand joinmatch {matches[number].Id}");
  7117.             }
  7118.  
  7119.             var match = GetMatch(player);
  7120.             string teamSize = msg("UI_TeamSize", player.UserIDString);
  7121.  
  7122.             for (int size = Math.Max(2, minDeathmatchSize); size < maxDeathmatchSize + 1; size++)
  7123.             {
  7124.                 var pos = UI.CalcButtonPos(size + matches.Count);
  7125.                 string color = match != null && match.TeamSize == size || size == minDeathmatchSize ? "0.69 0.49 0.29 0.5" : "0.29 0.49 0.69 0.5";
  7126.                 UI.CreateButton(ref element, "DuelistUI_Matches", color, teamSize + size, 12, $"{pos[0]} {pos[1]}", $"{pos[2]} {pos[3]}", $"UI_DuelistCommand size {size}");
  7127.             }
  7128.  
  7129.             if (matches.Count == 0)
  7130.                 UI.CreateLabel(ref element, "DuelistUI_Matches", "1 1 1 1", msg("NoMatchesExistYet", player.UserIDString), 14, "0.047 0.73", "1 0.89");
  7131.  
  7132.             CuiHelper.AddUi(player, element);
  7133.             matchesUI.Add(player.UserIDString);
  7134.         }
  7135.  
  7136.         public void ToggleKitUI(BasePlayer player)
  7137.         {
  7138.             if (kitsUI.Contains(player.UserIDString))
  7139.             {
  7140.                 CuiHelper.DestroyUi(player, "DuelistUI_Kits");
  7141.                 kitsUI.Remove(player.UserIDString);
  7142.                 return;
  7143.             }
  7144.  
  7145.             if (matchesUI.Contains(player.UserIDString))
  7146.             {
  7147.                 CuiHelper.DestroyUi(player, "DuelistUI_Matches");
  7148.                 matchesUI.Remove(player.UserIDString);
  7149.             }
  7150.  
  7151.             var element = UI.CreateElementContainer("DuelistUI_Kits", "0 0 0 0.5", "0.669 0.148", "0.903 0.541");
  7152.             var kits = VerifiedKits;
  7153.             string kit = duelsData.CustomKits.ContainsKey(player.UserIDString) ? duelsData.CustomKits[player.UserIDString] : null;
  7154.  
  7155.             for (int number = 0; number < kits.Count; number++)
  7156.             {
  7157.                 var pos = UI.CalcButtonPos(number);
  7158.                 UI.CreateButton(ref element, "DuelistUI_Kits", kits[number] == kit ? "0.69 0.49 0.29 0.5" : "0.29 0.49 0.69 0.5", kits[number], 12, $"{pos[0]} {pos[1]}", $"{pos[2]} {pos[3]}", $"UI_DuelistCommand kit {kits[number]}");
  7159.             }
  7160.  
  7161.             CuiHelper.AddUi(player, element);
  7162.             kitsUI.Add(player.UserIDString);
  7163.         }
  7164.  
  7165.         public void CreateAnnouncementUI(BasePlayer player, string text)
  7166.         {
  7167.             if (!player || !player.IsConnected || guiAnnounceUITime <= 0f)
  7168.                 return;
  7169.  
  7170.             var element = UI.CreateElementContainer("DuelistUI_Announcement", "0 0 0 0.5", "-0.027 0.92", "1.026 0.9643", false, "Hud");
  7171.  
  7172.             UI.CreateLabel(ref element, "DuelistUI_Announcement", "", text, 18, "0 0", "1 1");
  7173.             CuiHelper.DestroyUi(player, "DuelistUI_Announcement");
  7174.             CuiHelper.AddUi(player, element);
  7175.  
  7176.             timer.Once(guiAnnounceUITime, () => CuiHelper.DestroyUi(player, "DuelistUI_Announcement"));
  7177.         }
  7178.  
  7179.         public void CreateCountdownUI(BasePlayer player, string text)
  7180.         {
  7181.             var element = UI.CreateElementContainer("DuelistUI_Countdown", "0 0 0 0.5", "0.484 0.92", "0.527 0.9643", false, "Hud");
  7182.  
  7183.             UI.CreateLabel(ref element, "DuelistUI_Countdown", "1 0.1 0.1 1", text, 20, "0 0", "1 1");
  7184.             CuiHelper.DestroyUi(player, "DuelistUI_Countdown");
  7185.             CuiHelper.AddUi(player, element);
  7186.         }
  7187.  
  7188.         public void ToggleReadyUI(BasePlayer player)
  7189.         {
  7190.             if (!player || !player.IsConnected)
  7191.                 return;
  7192.  
  7193.             if (readyUiList.Contains(player.UserIDString))
  7194.             {
  7195.                 CuiHelper.DestroyUi(player, "DuelistUI_Ready");
  7196.                 readyUiList.Remove(player.UserIDString);
  7197.                 return;
  7198.             }
  7199.  
  7200.             var element = UI.CreateElementContainer("DuelistUI_Ready", "0 0 0 0.5", "0.475 0.158", "0.573 0.21");
  7201.             UI.CreateButton(ref element, "DuelistUI_Ready", "0.29 0.49 0.69 0.5", msg(duelsData.AutoReady.Contains(player.UserIDString) ? "UI_ReadyOn" : "UI_ReadyOff", player.UserIDString), 18, "0.016 0.081", "0.984 0.919", "UI_DuelistCommand ready");
  7202.             CuiHelper.AddUi(player, element);
  7203.             readyUiList.Add(player.UserIDString);
  7204.         }
  7205.  
  7206.         public void CreateDefeatUI(BasePlayer player)
  7207.         {
  7208.             if (!player || !player.IsConnected)
  7209.                 return;
  7210.  
  7211.             var element = UI.CreateElementContainer("DuelistUI_Defeat", "0 0 0 0.5", "0.436 0.133", "0.534 0.307", guiUseCursor);
  7212.  
  7213.             UI.CreateButton(ref element, "DuelistUI_Defeat", "0.29 0.49 0.69 0.5", msg("UI_Respawn", player.UserIDString), 18, "0.016 0.679", "0.984 0.976", "UI_DuelistCommand respawn");
  7214.             UI.CreateButton(ref element, "DuelistUI_Defeat", "0.29 0.49 0.69 0.5", msg("UI_Requeue", player.UserIDString), 18, "0.016 0.357", "0.984 0.655", "UI_DuelistCommand requeue");
  7215.             UI.CreateButton(ref element, "DuelistUI_Defeat", "0.29 0.49 0.69 0.5", msg(duelsData.AutoReady.Contains(player.UserIDString) ? "UI_ReadyOn" : "UI_ReadyOff", player.UserIDString), 18, "0.016 0.024", "0.984 0.333", "UI_DuelistCommand ready");
  7216.             CuiHelper.DestroyUi(player, "DuelistUI_Defeat");
  7217.             CuiHelper.AddUi(player, element);
  7218.  
  7219.             if (readyUiList.Contains(player.UserIDString))
  7220.             {
  7221.                 CuiHelper.DestroyUi(player, "DuelistUI_Ready");
  7222.                 readyUiList.Remove(player.UserIDString);
  7223.             }
  7224.         }
  7225.  
  7226.         private void UpdateMatchUI()
  7227.         {
  7228.             if (!matchUpdateRequired)
  7229.                 return;
  7230.  
  7231.             matchUpdateRequired = false;
  7232.  
  7233.             foreach (string userId in matchesUI.ToList())
  7234.             {
  7235.                 matchesUI.Remove(userId);
  7236.                 var player = BasePlayer.Find(userId);
  7237.  
  7238.                 if (player != null && player.IsConnected)
  7239.                 {
  7240.                     CuiHelper.DestroyUi(player, "DuelistUI_Matches");
  7241.                     ToggleMatchUI(player);
  7242.                 }
  7243.             }
  7244.         }
  7245.  
  7246.         public class UI // Credit: Absolut
  7247.         {
  7248.             public static CuiElementContainer CreateElementContainer(string panelName, string color, string aMin, string aMax, bool cursor = false, string parent = "Overlay")
  7249.             {
  7250.                 var NewElement = new CuiElementContainer
  7251.                 {
  7252.                     {
  7253.                         new CuiPanel
  7254.                         {
  7255.                             Image =
  7256.                             {
  7257.                                 Color = color
  7258.                             },
  7259.                             RectTransform =
  7260.                             {
  7261.                                 AnchorMin = aMin,
  7262.                                 AnchorMax = aMax
  7263.                             },
  7264.                             CursorEnabled = cursor
  7265.                         },
  7266.                         new CuiElement().Parent = parent,
  7267.                         panelName
  7268.                     }
  7269.                 };
  7270.                 return NewElement;
  7271.             }
  7272.  
  7273.             public static void CreateLabel(ref CuiElementContainer container, string panel, string color, string text, int size, string aMin, string aMax, TextAnchor align = TextAnchor.MiddleCenter)
  7274.             {
  7275.                 container.Add(new CuiLabel
  7276.                 {
  7277.                     Text =
  7278.                     {
  7279.                         Color = color,
  7280.                         FontSize = size,
  7281.                         Align = align,
  7282.                         FadeIn = 1.0f,
  7283.                         Text = text
  7284.                     },
  7285.                     RectTransform =
  7286.                     {
  7287.                         AnchorMin = aMin,
  7288.                         AnchorMax = aMax
  7289.                     }
  7290.                 },
  7291.                 panel);
  7292.             }
  7293.  
  7294.             public static void CreateButton(ref CuiElementContainer container, string panel, string color, string text, int size, string aMin, string aMax, string command, TextAnchor align = TextAnchor.MiddleCenter, string labelColor = "")
  7295.             {
  7296.                 container.Add(new CuiButton
  7297.                 {
  7298.                     Button =
  7299.                         {
  7300.                             Color = color,
  7301.                             Command = command,
  7302.                             FadeIn = 1.0f
  7303.                         },
  7304.                     RectTransform =
  7305.                         {
  7306.                             AnchorMin = aMin,
  7307.                             AnchorMax = aMax
  7308.                         },
  7309.                     Text =
  7310.                         {
  7311.                             Text = text,
  7312.                             FontSize = size,
  7313.                             Align = align,
  7314.                             Color = labelColor
  7315.                         }
  7316.                 },
  7317.                     panel);
  7318.             }
  7319.  
  7320.             public static float[] CalcButtonPos(int number, float dMinOffset = 1f)
  7321.             {
  7322.                 Vector2 position = new Vector2(0.03f, 0.889f);
  7323.                 Vector2 dimensions = new Vector2(0.45f * dMinOffset, 0.1f);
  7324.                 float offsetY = 0;
  7325.                 float offsetX = 0;
  7326.                 if (number >= 0 && number < 9)
  7327.                 {
  7328.                     offsetY = (-0.01f - dimensions.y) * number;
  7329.                 }
  7330.                 else if (number > 8 && number < 19)
  7331.                 {
  7332.                     offsetY = (-0.01f - dimensions.y) * (number - 9);
  7333.                     offsetX = (0.04f + dimensions.x) * 1;
  7334.                 }
  7335.                 else if (number > 18 && number < 29)
  7336.                 {
  7337.                     offsetY = (-0.01f - dimensions.y) * (number - 19);
  7338.                     offsetX = (0.08f + dimensions.x) * 1;
  7339.                 }
  7340.                 Vector2 offset = new Vector2(offsetX, offsetY);
  7341.                 Vector2 posMin = position + offset;
  7342.                 Vector2 posMax = posMin + dimensions;
  7343.                 return new[] { posMin.x, posMin.y, posMax.x, posMax.y };
  7344.             }
  7345.         }
  7346.  
  7347.         #endregion
  7348.  
  7349.         #region Config
  7350.  
  7351.         private bool Changed;
  7352.         private string szMatchChatCommand;
  7353.         private string szDuelChatCommand;
  7354.         private string szQueueChatCommand;
  7355.         private const string duelistPerm = "duelist.dd";
  7356.         private const string duelistGroup = "duelist";
  7357.         private float zoneRadius;
  7358.         private int deathTime;
  7359.         private int immunityTime;
  7360.         private int zoneCounter;
  7361.         private List<string> _hpDuelingKits = new List<string>();
  7362.         private List<string> _lpDuelingKits = new List<string>();
  7363.         private List<string> hpDuelingKits = new List<string>();
  7364.         private List<string> lpDuelingKits = new List<string>();
  7365.         private List<BetInfo> duelingBets = new List<BetInfo>();
  7366.         private bool recordStats = true;
  7367.         private int permsToGive = 3;
  7368.         private float maxIncline;
  7369.         private bool allowBetForfeit;
  7370.         private bool allowBetRefund;
  7371.         private bool allowBets;
  7372.         private bool putToSleep;
  7373.         private bool blockSpawning;
  7374.         private bool killNpc;
  7375.         private float announceTime;
  7376.         private bool removePlayers;
  7377.         private bool useAnnouncement;
  7378.         private bool autoSetup;
  7379.         private bool broadcastDefeat;
  7380.         private double economicsMoney;
  7381.         private double requiredDuelMoney;
  7382.         private int serverRewardsPoints;
  7383.         private float damageScaleAmount;
  7384.         private int zoneAmount;
  7385.         private int playersPerZone;
  7386.         private bool visibleToAdmins;
  7387.         private float spDrawTime;
  7388.         private float spRemoveOneMaxDistance;
  7389.         private float spRemoveAllMaxDistance;
  7390.         private bool spAutoRemove;
  7391.         private bool avoidWaterSpawns;
  7392.         private int extraWallStacks;
  7393.         private bool useZoneWalls;
  7394.         private bool zoneUseWoodenWalls;
  7395.         private float buildingBlockExtensionRadius;
  7396.         private bool autoAllowAll;
  7397.         private bool useRandomSkins;
  7398.         private float playerHealth;
  7399.         private bool dmFF;
  7400.         private int minDeathmatchSize;
  7401.         private int maxDeathmatchSize;
  7402.         private bool autoEnable;
  7403.         private ulong teamGoodShirt;
  7404.         private ulong teamEvilShirt;
  7405.         private string teamShirt;
  7406.         private double teamEconomicsMoney;
  7407.         private int teamServerRewardsPoints;
  7408.         private float lesserKitChance;
  7409.         private bool tdmEnabled;
  7410.         private bool useLeastAmount;
  7411.         private bool tdmServerDeaths;
  7412.         private bool tdmMatchDeaths;
  7413.         private List<string> whitelistCommands = new List<string>();
  7414.         private bool useWhitelistCommands;
  7415.         private List<string> blacklistCommands = new List<string>();
  7416.         private bool useBlacklistCommands;
  7417.         private bool bypassNewmans;
  7418.         private bool saveRestoreEnabled;
  7419.         private List<DuelKitItem> respawnLoot = new List<DuelKitItem>();
  7420.         private bool respawnDeadDisconnect;
  7421.         private bool sendDeadHome;
  7422.         private bool resetSeed;
  7423.         private bool noStability;
  7424.         private bool noMovement;
  7425.         private bool requireTeamSize;
  7426.         private int requiredMinSpawns;
  7427.         private int requiredMaxSpawns;
  7428.         private bool guiAutoEnable;
  7429.         private bool guiUseCursor;
  7430.         private string szUIChatCommand;
  7431.         private bool useWorkshopSkins;
  7432.         private bool respawnWalls;
  7433.         private bool allowPlayerDeaths;
  7434.         private bool morphBarricadesStoneWalls;
  7435.         private bool morphBarricadesWoodenWalls;
  7436.         private bool guiUseCloseButton;
  7437.         private string autoKitName;
  7438.         private float guiAnnounceUITime;
  7439.         private bool sendDefeatedHome;
  7440.         private bool sendHomeRequeue;
  7441.         private bool sendHomeSpectatorWhenRematchTimesOut;
  7442.         private bool autoFlames;
  7443.         private bool autoOvens;
  7444.         private bool autoTurrets;
  7445.         private int sphereAmount;
  7446.         private bool wipeDuelZones;
  7447.         private bool setPlayerTime;
  7448.         private ulong chatSteamID;
  7449.  
  7450.         private List<object> RespawnLoot
  7451.         {
  7452.             get
  7453.             {
  7454.                 return new List<object>
  7455.                 {
  7456.                     new DuelKitItem
  7457.                     {
  7458.                         shortname = "rock",
  7459.                         amount = 1,
  7460.                         skin = 0,
  7461.                         container = "belt",
  7462.                         slot = -1
  7463.                     },
  7464.                     new DuelKitItem
  7465.                     {
  7466.                         shortname = "torch",
  7467.                         amount = 1,
  7468.                         skin = 0,
  7469.                         container = "belt",
  7470.                         slot = -1
  7471.                     }
  7472.                 };
  7473.             }
  7474.         }
  7475.  
  7476.         private List<object> BlacklistedCommands
  7477.         {
  7478.             get
  7479.             {
  7480.                 return new List<object>
  7481.                 {
  7482.                     "/tp",
  7483.                     "/remove",
  7484.                     "/bank",
  7485.                     "/shop",
  7486.                     "/event",
  7487.                     "/rw",
  7488.                     "/home",
  7489.                     "/trade"
  7490.                 };
  7491.             }
  7492.         }
  7493.  
  7494.         private List<object> WhitelistedCommands
  7495.         {
  7496.             get
  7497.             {
  7498.                 return new List<object>
  7499.                 {
  7500.                     "/report",
  7501.                     "/pm",
  7502.                     "/r",
  7503.                     "/help"
  7504.                 };
  7505.             }
  7506.         }
  7507.  
  7508.         private List<object> DefaultBets
  7509.         {
  7510.             get
  7511.             {
  7512.                 return new List<object>
  7513.                 {
  7514.                     new Dictionary<string, object>
  7515.                     {
  7516.                         ["trigger"] = "stone",
  7517.                         ["max"] = 50000,
  7518.                         ["itemid"] = -2099697608
  7519.                     },
  7520.                     new Dictionary<string, object>
  7521.                     {
  7522.                         ["trigger"] = "sulfur",
  7523.                         ["max"] = 50000,
  7524.                         ["itemid"] = -1581843485
  7525.                     },
  7526.                     new Dictionary<string, object>
  7527.                     {
  7528.                         ["trigger"] = "fragment",
  7529.                         ["max"] = 50000,
  7530.                         ["itemid"] = 69511070
  7531.                     },
  7532.                     new Dictionary<string, object>
  7533.                     {
  7534.                         ["trigger"] = "charcoal",
  7535.                         ["max"] = 50000,
  7536.                         ["itemid"] = -1938052175
  7537.                     },
  7538.                     new Dictionary<string, object>
  7539.                     {
  7540.                         ["trigger"] = "gp",
  7541.                         ["max"] = 25000,
  7542.                         ["itemid"] = -265876753
  7543.                     },
  7544.                     new Dictionary<string, object>
  7545.                     {
  7546.                         ["trigger"] = "hqm",
  7547.                         ["max"] = 1000,
  7548.                         ["itemid"] = 317398316
  7549.                     },
  7550.                     new Dictionary<string, object>
  7551.                     {
  7552.                         ["trigger"] = "c4",
  7553.                         ["max"] = 10,
  7554.                         ["itemid"] = 1248356124
  7555.                     },
  7556.                     new Dictionary<string, object>
  7557.                     {
  7558.                         ["trigger"] = "rocket",
  7559.                         ["max"] = 6,
  7560.                         ["itemid"] = -742865266
  7561.                     }
  7562.                 };
  7563.             }
  7564.         }
  7565.  
  7566.         private List<object> DefaultLesserKits
  7567.         {
  7568.             get
  7569.             {
  7570.                 return new List<object>
  7571.                 {
  7572.                     "kit_4",
  7573.                     "kit_5",
  7574.                     "kit_6"
  7575.                 };
  7576.             }
  7577.         }
  7578.  
  7579.         private List<object> DefaultKits
  7580.         {
  7581.             get
  7582.             {
  7583.                 return new List<object>
  7584.                 {
  7585.                     "kit_1",
  7586.                     "kit_2",
  7587.                     "kit_3"
  7588.                 };
  7589.             }
  7590.         }
  7591.  
  7592.         private static Dictionary<string, List<DuelKitItem>> customKits = new Dictionary<string, List<DuelKitItem>>();
  7593.  
  7594.         private Dictionary<string, object> DefaultCustomKits
  7595.         {
  7596.             get
  7597.             {
  7598.                 return new Dictionary<string, object>
  7599.                 {
  7600.                     ["Hunting Bow"] = new List<object>
  7601.                     {
  7602.                         new DuelKitItem
  7603.                         {
  7604.                             shortname = "bow.hunting",
  7605.                             amount = 1,
  7606.                             skin = 0,
  7607.                             container = "belt",
  7608.                             slot = -1
  7609.                         },
  7610.                         new DuelKitItem
  7611.                         {
  7612.                             shortname = "arrow.wooden",
  7613.                             amount = 50,
  7614.                             skin = 0,
  7615.                             container = "belt",
  7616.                             slot = -1
  7617.                         },
  7618.                         new DuelKitItem
  7619.                         {
  7620.                             shortname = "spear.stone",
  7621.                             amount = 1,
  7622.                             skin = 0,
  7623.                             container = "belt",
  7624.                             slot = -1
  7625.                         },
  7626.                         new DuelKitItem
  7627.                         {
  7628.                             shortname = "bandage",
  7629.                             amount = 5,
  7630.                             skin = 0,
  7631.                             container = "belt",
  7632.                             slot = -1
  7633.                         },
  7634.                         new DuelKitItem
  7635.                         {
  7636.                             shortname = "syringe.medical",
  7637.                             amount = 5,
  7638.                             skin = 0,
  7639.                             container = "belt",
  7640.                             slot = -1
  7641.                         },
  7642.                         new DuelKitItem
  7643.                         {
  7644.                             shortname = "largemedkit",
  7645.                             amount = 5,
  7646.                             skin = 0,
  7647.                             container = "belt",
  7648.                             slot = -1
  7649.                         },
  7650.                         new DuelKitItem
  7651.                         {
  7652.                             shortname = "burlap.gloves",
  7653.                             amount = 1,
  7654.                             skin = 0,
  7655.                             container = "wear",
  7656.                             slot = -1
  7657.                         },
  7658.                         new DuelKitItem
  7659.                         {
  7660.                             shortname = "burlap.headwrap",
  7661.                             amount = 1,
  7662.                             skin = 0,
  7663.                             container = "wear",
  7664.                             slot = -1
  7665.                         },
  7666.                         new DuelKitItem
  7667.                         {
  7668.                             shortname = "burlap.shirt",
  7669.                             amount = 1,
  7670.                             skin = 0,
  7671.                             container = "wear",
  7672.                             slot = -1
  7673.                         },
  7674.                         new DuelKitItem
  7675.                         {
  7676.                             shortname = "burlap.shoes",
  7677.                             amount = 1,
  7678.                             skin = 0,
  7679.                             container = "wear",
  7680.                             slot = -1
  7681.                         },
  7682.                         new DuelKitItem
  7683.                         {
  7684.                             shortname = "burlap.trousers",
  7685.                             amount = 1,
  7686.                             skin = 0,
  7687.                             container = "wear",
  7688.                             slot = -1
  7689.                         }
  7690.                     },
  7691.                     ["Assault Rifle and Bolt Action Rifle"] = new List<object>
  7692.                     {
  7693.                         new DuelKitItem
  7694.                         {
  7695.                             shortname = "rifle.ak",
  7696.                             amount = 1,
  7697.                             skin = 0,
  7698.                             container = "belt",
  7699.                             slot = -1,
  7700.                             ammo = "ammo.rifle",
  7701.                             mods = new List<string>
  7702.                             {
  7703.                                 "weapon.mod.lasersight"
  7704.                             }
  7705.                         },
  7706.                         new DuelKitItem
  7707.                         {
  7708.                             shortname = "rifle.bolt",
  7709.                             amount = 1,
  7710.                             skin = 0,
  7711.                             container = "belt",
  7712.                             slot = -1,
  7713.                             ammo = "ammo.rifle",
  7714.                             mods = new List<string>
  7715.                             {
  7716.                                 "weapon.mod.lasersight",
  7717.                                 "weapon.mod.small.scope"
  7718.                             }
  7719.                         },
  7720.                         new DuelKitItem
  7721.                         {
  7722.                             shortname = "largemedkit",
  7723.                             amount = 5,
  7724.                             skin = 0,
  7725.                             container = "belt",
  7726.                             slot = -1
  7727.                         },
  7728.                         new DuelKitItem
  7729.                         {
  7730.                             shortname = "bandage",
  7731.                             amount = 5,
  7732.                             skin = 0,
  7733.                             container = "belt",
  7734.                             slot = -1
  7735.                         },
  7736.                         new DuelKitItem
  7737.                         {
  7738.                             shortname = "syringe.medical",
  7739.                             amount = 5,
  7740.                             skin = 0,
  7741.                             container = "belt",
  7742.                             slot = -1
  7743.                         },
  7744.                         new DuelKitItem
  7745.                         {
  7746.                             shortname = "bearmeat.cooked",
  7747.                             amount = 10,
  7748.                             skin = 0,
  7749.                             container = "belt",
  7750.                             slot = -1
  7751.                         },
  7752.                         new DuelKitItem
  7753.                         {
  7754.                             shortname = "hoodie",
  7755.                             amount = 1,
  7756.                             skin = 0,
  7757.                             container = "wear",
  7758.                             slot = -1
  7759.                         },
  7760.                         new DuelKitItem
  7761.                         {
  7762.                             shortname = "metal.facemask",
  7763.                             amount = 1,
  7764.                             skin = 0,
  7765.                             container = "wear",
  7766.                             slot = -1
  7767.                         },
  7768.                         new DuelKitItem
  7769.                         {
  7770.                             shortname = "metal.plate.torso",
  7771.                             amount = 1,
  7772.                             skin = 0,
  7773.                             container = "wear",
  7774.                             slot = -1
  7775.                         },
  7776.                         new DuelKitItem
  7777.                         {
  7778.                             shortname = "pants",
  7779.                             amount = 1,
  7780.                             skin = 0,
  7781.                             container = "wear",
  7782.                             slot = -1
  7783.                         },
  7784.                         new DuelKitItem
  7785.                         {
  7786.                             shortname = "burlap.gloves",
  7787.                             amount = 1,
  7788.                             skin = 0,
  7789.                             container = "wear",
  7790.                             slot = -1
  7791.                         },
  7792.                         new DuelKitItem
  7793.                         {
  7794.                             shortname = "shoes.boots",
  7795.                             amount = 1,
  7796.                             skin = 0,
  7797.                             container = "wear",
  7798.                             slot = -1
  7799.                         },
  7800.                         new DuelKitItem
  7801.                         {
  7802.                             shortname = "ammo.rifle",
  7803.                             amount = 200,
  7804.                             skin = 0,
  7805.                             container = "main",
  7806.                             slot = -1
  7807.                         },
  7808.                         new DuelKitItem
  7809.                         {
  7810.                             shortname = "weapon.mod.flashlight",
  7811.                             amount = 1,
  7812.                             skin = 0,
  7813.                             container = "main",
  7814.                             slot = -1
  7815.                         },
  7816.                         new DuelKitItem
  7817.                         {
  7818.                             shortname = "weapon.mod.small.scope",
  7819.                             amount = 1,
  7820.                             skin = 0,
  7821.                             container = "main",
  7822.                             slot = -1
  7823.                         }
  7824.                     },
  7825.                     ["Semi-Automatic Pistol"] = new List<object>
  7826.                     {
  7827.                         new DuelKitItem
  7828.                         {
  7829.                             shortname = "pistol.semiauto",
  7830.                             amount = 1,
  7831.                             skin = 0,
  7832.                             container = "belt",
  7833.                             slot = -1,
  7834.                             ammo = "ammo.pistol",
  7835.                             mods = new List<string>
  7836.                             {
  7837.                                 "weapon.mod.lasersight"
  7838.                             }
  7839.                         },
  7840.                         new DuelKitItem
  7841.                         {
  7842.                             shortname = "largemedkit",
  7843.                             amount = 5,
  7844.                             skin = 0,
  7845.                             container = "belt",
  7846.                             slot = -1
  7847.                         },
  7848.                         new DuelKitItem
  7849.                         {
  7850.                             shortname = "bandage",
  7851.                             amount = 5,
  7852.                             skin = 0,
  7853.                             container = "belt",
  7854.                             slot = -1
  7855.                         },
  7856.                         new DuelKitItem
  7857.                         {
  7858.                             shortname = "syringe.medical",
  7859.                             amount = 5,
  7860.                             skin = 0,
  7861.                             container = "belt",
  7862.                             slot = -1
  7863.                         },
  7864.                         new DuelKitItem
  7865.                         {
  7866.                             shortname = "bearmeat.cooked",
  7867.                             amount = 10,
  7868.                             skin = 0,
  7869.                             container = "belt",
  7870.                             slot = -1
  7871.                         },
  7872.                         new DuelKitItem
  7873.                         {
  7874.                             shortname = "hoodie",
  7875.                             amount = 1,
  7876.                             skin = 0,
  7877.                             container = "wear",
  7878.                             slot = -1
  7879.                         },
  7880.                         new DuelKitItem
  7881.                         {
  7882.                             shortname = "metal.facemask",
  7883.                             amount = 1,
  7884.                             skin = 0,
  7885.                             container = "wear",
  7886.                             slot = -1
  7887.                         },
  7888.                         new DuelKitItem
  7889.                         {
  7890.                             shortname = "metal.plate.torso",
  7891.                             amount = 1,
  7892.                             skin = 0,
  7893.                             container = "wear",
  7894.                             slot = -1
  7895.                         },
  7896.                         new DuelKitItem
  7897.                         {
  7898.                             shortname = "pants",
  7899.                             amount = 1,
  7900.                             skin = 0,
  7901.                             container = "wear",
  7902.                             slot = -1
  7903.                         },
  7904.                         new DuelKitItem
  7905.                         {
  7906.                             shortname = "burlap.gloves",
  7907.                             amount = 1,
  7908.                             skin = 0,
  7909.                             container = "wear",
  7910.                             slot = -1
  7911.                         },
  7912.                         new DuelKitItem
  7913.                         {
  7914.                             shortname = "shoes.boots",
  7915.                             amount = 1,
  7916.                             skin = 0,
  7917.                             container = "wear",
  7918.                             slot = -1
  7919.                         },
  7920.                         new DuelKitItem
  7921.                         {
  7922.                             shortname = "ammo.pistol",
  7923.                             amount = 200,
  7924.                             skin = 0,
  7925.                             container = "main",
  7926.                             slot = -1
  7927.                         },
  7928.                         new DuelKitItem
  7929.                         {
  7930.                             shortname = "weapon.mod.flashlight",
  7931.                             amount = 1,
  7932.                             skin = 0,
  7933.                             container = "main",
  7934.                             slot = -1
  7935.                         }
  7936.                     },
  7937.                     ["Pump Shotgun"] = new List<object>
  7938.                     {
  7939.                         new DuelKitItem
  7940.                         {
  7941.                             shortname = "shotgun.pump",
  7942.                             amount = 1,
  7943.                             skin = 0,
  7944.                             container = "belt",
  7945.                             slot = -1,
  7946.                             ammo = "ammo.shotgun.slug",
  7947.                             mods = new List<string>
  7948.                             {
  7949.                                 "weapon.mod.lasersight"
  7950.                             }
  7951.                         },
  7952.                         new DuelKitItem
  7953.                         {
  7954.                             shortname = "largemedkit",
  7955.                             amount = 5,
  7956.                             skin = 0,
  7957.                             container = "belt",
  7958.                             slot = -1
  7959.                         },
  7960.                         new DuelKitItem
  7961.                         {
  7962.                             shortname = "bandage",
  7963.                             amount = 5,
  7964.                             skin = 0,
  7965.                             container = "belt",
  7966.                             slot = -1
  7967.                         },
  7968.                         new DuelKitItem
  7969.                         {
  7970.                             shortname = "syringe.medical",
  7971.                             amount = 5,
  7972.                             skin = 0,
  7973.                             container = "belt",
  7974.                             slot = -1
  7975.                         },
  7976.                         new DuelKitItem
  7977.                         {
  7978.                             shortname = "bearmeat.cooked",
  7979.                             amount = 10,
  7980.                             skin = 0,
  7981.                             container = "belt",
  7982.                             slot = -1
  7983.                         },
  7984.                         new DuelKitItem
  7985.                         {
  7986.                             shortname = "hoodie",
  7987.                             amount = 1,
  7988.                             skin = 0,
  7989.                             container = "wear",
  7990.                             slot = -1
  7991.                         },
  7992.                         new DuelKitItem
  7993.                         {
  7994.                             shortname = "metal.facemask",
  7995.                             amount = 1,
  7996.                             skin = 0,
  7997.                             container = "wear",
  7998.                             slot = -1
  7999.                         },
  8000.                         new DuelKitItem
  8001.                         {
  8002.                             shortname = "metal.plate.torso",
  8003.                             amount = 1,
  8004.                             skin = 0,
  8005.                             container = "wear",
  8006.                             slot = -1
  8007.                         },
  8008.                         new DuelKitItem
  8009.                         {
  8010.                             shortname = "pants",
  8011.                             amount = 1,
  8012.                             skin = 0,
  8013.                             container = "wear",
  8014.                             slot = -1
  8015.                         },
  8016.                         new DuelKitItem
  8017.                         {
  8018.                             shortname = "burlap.gloves",
  8019.                             amount = 1,
  8020.                             skin = 0,
  8021.                             container = "wear",
  8022.                             slot = -1
  8023.                         },
  8024.                         new DuelKitItem
  8025.                         {
  8026.                             shortname = "shoes.boots",
  8027.                             amount = 1,
  8028.                             skin = 0,
  8029.                             container = "wear",
  8030.                             slot = -1
  8031.                         },
  8032.                         new DuelKitItem
  8033.                         {
  8034.                             shortname = "ammo.shotgun.slug",
  8035.                             amount = 200,
  8036.                             skin = 0,
  8037.                             container = "main",
  8038.                             slot = -1
  8039.                         },
  8040.                         new DuelKitItem
  8041.                         {
  8042.                             shortname = "weapon.mod.flashlight",
  8043.                             amount = 1,
  8044.                             skin = 0,
  8045.                             container = "main",
  8046.                             slot = -1
  8047.                         }
  8048.                     }
  8049.                 };
  8050.             }
  8051.         }
  8052.  
  8053.         protected override void LoadDefaultMessages() // holy shit this took forever.
  8054.         {
  8055.             lang.RegisterMessages(new Dictionary<string, string>
  8056.             {
  8057.                 ["Awards"] = "{0} ({1}) duels won {2}",
  8058.                 ["Granted"] = "Granted {0} ({1}) permission {2} for group {3}",
  8059.                 ["Logged"] = "Duelists have been logged to: {0}",
  8060.                 ["Indestructible"] = "This object belongs to the server and is indestructible!",
  8061.                 ["Building is blocked!"] = "<color=red>Building is blocked inside of dueling zones!</color>",
  8062.                 ["TopAll"] = "[ <color=#ffff00>Top Duelists Of All Time ({0})</color> ]:",
  8063.                 ["Top"] = "[ <color=#ffff00>Top Duelists ({0})</color> ]:",
  8064.                 ["NoLongerQueued"] = "You are no longer in queue for a duel.",
  8065.                 ["InQueueSuccess"] = "You are now in queue for a duel. You will teleport instantly when a match is available.",
  8066.                 ["MustBeNaked"] = "<color=red>You must be naked before you can duel.</color>",
  8067.                 ["AlreadyInADuel"] = "You cannot queue for a duel while already in a duel!",
  8068.                 ["MustAllowDuels"] = "You must allow duels first! Type: <color=orange>/{0} allow</color>",
  8069.                 ["DuelsDisabled"] = "Duels are disabled.",
  8070.                 ["NoZoneExists"] = "No dueling zone exists.",
  8071.                 ["Banned"] = "You are banned from duels.",
  8072.                 ["FoundZone"] = "Took {0} tries ({1}ms) to get a dueling zone.",
  8073.                 ["ImmunityFaded"] = "Your immunity has faded.",
  8074.                 ["NotifyBetWon"] = "You have won your bet! To claim type <color=orange>/{0} claim</color>.",
  8075.                 ["ConsoleBetWon"] = "{0} ({1}) won his bet against {2} ({3})!",
  8076.                 ["DuelDeathMessage"] = "<color=silver><color=lime>{0}</color> (<color=lime>W</color>: <color=orange>{1}</color> / <color=red>L</color>: <color=orange>{2}</color>) has defeated <color=lime>{3}</color> (<color=lime>W</color>: <color=orange>{4}</color> / <color=red>L</color>: <color=orange>{5}</color>) in a duel with <color=green>{6}</color> health left.{7}</color>",
  8077.                 ["BetWon"] = " Bet won: <color=lime>{0}</color> (<color=lime>{1}</color>)",
  8078.                 ["ExecutionTime"] = "You have <color=red>{0} minutes</color> to win the duel before you are executed.",
  8079.                 ["FailedZone"] = "Failed to create a dueling zone, please try again.",
  8080.                 ["FailedSetup"] = "Failed to setup the zone, please try again.",
  8081.                 ["FailedRaycast"] = "Look towards the ground, and try again.",
  8082.                 ["BetPlaced"] = "Your bet {0} ({1}) has been placed.",
  8083.                 ["BetForfeitSuffix"] = " Type <color=orange>/{0} bet forfeit</color> to forfeit your bet.",
  8084.                 ["BetRefundSuffix"] = " Type <color=orange>/{0} bet refund</color> to refund your bet.",
  8085.                 ["BetNotEnough"] = "Bet cancelled. You do not have enough to bet this amount!",
  8086.                 ["BetZero"] = "Bet cancelled. You do not have this item in your inventory.",
  8087.                 ["DuelAnnouncement"] = "Type <color=orange>/{duelChatCommand}</color> for information on the dueling system. See your standing on the leaderboard by using <color=orange>/{ladderCommand}</color>. Type <color=orange>/{queueCommand}</color> to enter the dueling queue now!",
  8088.                 ["DuelAnnouncementBetsSuffix"] = " Feeling lucky? Use <color=orange>/{0} bet</color> to create a bet!",
  8089.                 ["ZoneCreated"] = "Dueling zone created successfully.",
  8090.                 ["RemovedZone"] = "Removed dueling zone.",
  8091.                 ["RemovedBan"] = "Unbanned {0}",
  8092.                 ["AddedBan"] = "Banned {0}",
  8093.                 ["PlayerNotFound"] = "{0} not found. Try being more specific or use a steam id.",
  8094.                 ["RequestTimedOut"] = "Request timed out to duel <color=lime>{0}</color>",
  8095.                 ["RemovedFromQueueRequest"] = "You have been removed from the dueling queue since you have requested to duel another player.",
  8096.                 ["RemovedFromDuel"] = "You have been removed from your duel.",
  8097.                 ["BetsDoNotMatch"] = "Your bet {0} ({1}) does not match {2} ({3})",
  8098.                 ["InvalidBet"] = "Invalid bet '{0}'",
  8099.                 ["BetSyntax"] = "Syntax: /{0} bet <item> <amount> - resources must be refined",
  8100.                 ["AvailableBets"] = "Available Bets:",
  8101.                 ["MustHaveSameBet"] = "{0} is betting: {1} ({2}). You must have the same bet to duel this player.",
  8102.                 ["NoBetsToRefund"] = "There are no bets to refund.",
  8103.                 ["Disabled"] = "Disabled",
  8104.                 ["HelpDuelBet"] = "<color=silver><color=orange>/{0} bet</color> - place a bet towards your next duel.</color>",
  8105.                 ["HelpDuelAdmin"] = "<color=orange>Admin: /{0} on|off</color> - enable/disable duels",
  8106.                 ["HelpDuelAdminRefundAll"] = "<color=orange>Admin: /{0} bet refundall</color> - refund all bets for all players",
  8107.                 ["DuelsDisabledAlready"] = "Duels are already disabled!",
  8108.                 ["DuelsNowDisabled"] = "Duels disabled. Sending duelers home.",
  8109.                 ["DuelsEnabledAlready"] = "Duels are already enabled!",
  8110.                 ["DuelsNowEnabled"] = "Duels enabled",
  8111.                 ["NoBetsToClaim"] = "You have no bets to claim.",
  8112.                 ["PlayerClaimedBet"] = "Claimed bet {0} ({1})",
  8113.                 ["AllBetsClaimed"] = "You have claimed all of your bets.",
  8114.                 ["DuelChatOff"] = "You will no longer see duel death messages.",
  8115.                 ["DuelChatOn"] = "You will now see duel death messages.",
  8116.                 ["PlayerRequestsOn"] = "Players may now request to duel you. You will be removed from this list if you do not duel.",
  8117.                 ["PlayerRequestsOff"] = "Players may no longer request to duel you.",
  8118.                 ["BlockedRequestsFrom"] = "Blocked duel requests from: <color=lime>{0}</color>",
  8119.                 ["UnblockedRequestsFrom"] = "Removed block on duel requests from: <color=lime>{0}</color>",
  8120.                 ["AlreadyBlocked"] = "You have already blocked players from requesting duels.",
  8121.                 ["NoBetsConfigured"] = "No bets are configured.",
  8122.                 ["RefundAllPlayerNotice"] = "Server administrator has refunded your bet: {0} ({1})",
  8123.                 ["RefundAllAdminNotice"] = "Refunded {0} ({1}): {2} ({3})",
  8124.                 ["BetsRemaining"] = "Bet items remaining in database: {0}",
  8125.                 ["AllBetsRefunded"] = "All dueling bets refunded",
  8126.                 ["CannotForfeit"] = "You cannot forfeit bets on this server.",
  8127.                 ["CannotForfeitRequestDuel"] = "You cannot forfeit a bet while requesting a duel!",
  8128.                 ["CannotForfeitInDuel"] = "You cannot forfeit a bet while dueling!",
  8129.                 ["CannotRefundRequestDuel"] = "You cannot refund a bet while requesting a duel!",
  8130.                 ["CannotRefundInDuel"] = "You cannot refund a bet while dueling!",
  8131.                 ["BetForfeit"] = "You forfeit your bet!",
  8132.                 ["NoBetToForfeit"] = "You do not have an active bet to forfeit.",
  8133.                 ["NoBetToRefund"] = "You do not have an active bet to refund.",
  8134.                 ["CannotRefund"] = "You cannot refund bets on this server.",
  8135.                 ["BetRefunded"] = "You have refunded your bet.",
  8136.                 ["AlreadyBetting"] = "You are already betting! Your bet: {0} ({1})",
  8137.                 ["ToRefundUse"] = "To refund your bet, type: <color=orange>/{0} bet refund</color>",
  8138.                 ["ToForfeitUse"] = "To forfeit your bet, type: <color=orange>/{0} bet forfeit</color>. Refunds are not allowed.",
  8139.                 ["InvalidNumber"] = "Invalid number: {0}",
  8140.                 ["MultiplesOnly"] = "Number must be a multiple of 500. ie: 500, 1000, 2000, 5000, 10000, 15000",
  8141.                 ["NoRequestsReceived"] = "No players have requested a duel with you.",
  8142.                 ["DuelCancelledFor"] = "<color=lime>{0}</color> has cancelled the duel!",
  8143.                 ["NoPendingRequests"] = "You have no pending request to cancel.",
  8144.                 ["DuelCancelledWith"] = "<color=lime>{0}</color> has cancelled the duel request.",
  8145.                 ["DuelCancelComplete"] = "Duel request cancelled.",
  8146.                 ["MustWaitToRequestAgain"] = "You must wait <color=red>{0} minute(s)</color> from the last time you requested a duel to request another.",
  8147.                 ["AlreadyDueling"] = "You are already dueling another player!",
  8148.                 ["CannotRequestThisPlayer"] = "You are not allowed to request duels with this player.",
  8149.                 ["TargetAlreadyDueling"] = "<color=lime>{0}</color> is already dueling another player!",
  8150.                 ["NotAllowedYet"] = "<color=lime>{0}</color> has not enabled duel requests yet. They must type <color=orange>/{1} allow</color>",
  8151.                 ["MustWaitForAccept"] = "You have requested a duel with <color=lime>{0}</color> already. You must wait for this player to accept the duel.",
  8152.                 ["PendingRequestAlready"] = "This player has a duel request pending already.",
  8153.                 ["TargetHasNoBet"] = "You have an active bet going. <color=lime>{0}</color> must have the same bet to duel you.",
  8154.                 ["YourBet"] = "Your bet: {0} ({1})",
  8155.                 ["WoundedQueue"] = "You cannot duel while either player is wounded.",
  8156.                 ["DuelMustBeNaked"] = "Duel cancelled: <color=lime>{0}</color> inventory is not empty.",
  8157.                 ["LadderLife"] = "<color=#5A625B>Use <color=yellow>/{0} ladder life</color> to see all time stats</color>",
  8158.                 ["EconomicsDeposit"] = "You have received <color=yellow>${0}</color>!",
  8159.                 ["ServerRewardPoints"] = "You have received <color=yellow>{0} RP</color>!",
  8160.                 ["DuelsMustBeEnabled"] = "Use '/{0} on' to enable dueling on the server.",
  8161.                 ["DataSaved"] = "Data has been saved.",
  8162.                 ["DuelsNowDisabledEmpty"] = "Duels disabled.",
  8163.                 ["CannotTeleport"] = "You are not allowed to teleport from a dueling zone.",
  8164.                 ["AllZonesFull"] = "All zones are currently full. Zones: {0}. Limit Per Zone: {1}",
  8165.                 ["NoZoneFound"] = "No zone found. You must stand inside of the zone to remove it.",
  8166.                 ["RemovedZoneAt"] = "Removed zone at {0}",
  8167.                 ["CannotDuel"] = "You are not allowed to duel at the moment.",
  8168.                 ["LeftZone"] = "<color=red>You were found outside of the dueling zone while dueling. Your items have been removed.</color>",
  8169.                 ["SpawnAdd"] = "<color=orange>/{0} spawns add</color> - add a spawn point at the position you are looking at.",
  8170.                 ["SpawnHere"] = "<color=orange>/{0} spawns here</color> - add a spawn point at your position.",
  8171.                 ["SpawnRemove"] = "<color=orange>/{0} spawns remove</color> - removes the nearest spawn point within <color=orange>{1}m</color>.",
  8172.                 ["SpawnRemoveAll"] = "<color=orange>/{0} spawns removeall</color> - remove all spawn points within <color=orange>{1}m</color>.",
  8173.                 ["SpawnWipe"] = "<color=orange>/{0} spawns wipe</color> - wipe all spawn points.",
  8174.                 ["SpawnWiped"] = "<color=red>{0}</color> spawns points wiped.",
  8175.                 ["SpawnCount"] = "<color=green>{0}</color> spawn points in database.",
  8176.                 ["SpawnNoneFound"] = "No custom spawn points found within <color=orange>{0}m</color>.",
  8177.                 ["SpawnAdded"] = "Spawn point added at {0}",
  8178.                 ["SpawnRemoved"] = "Removed <color=red>{0}</color> spawn(s)",
  8179.                 ["SpawnExists"] = "This spawn point exists already.",
  8180.                 ["SpawnNoneExist"] = "No spawn points exist.",
  8181.                 ["ZoneExists"] = "A dueling zone already exists here.",
  8182.                 ["ZoneLimit"] = "Zone limit reached ({0}). You must manually remove an existing zone before creating a new one.",
  8183.                 ["CannotEventJoin"] = "You are not allowed to join this event while dueling.",
  8184.                 ["KitDoesntExist"] = "This kit doesn't exist: {0}",
  8185.                 ["KitSet"] = "Custom kit set to {0}. This kit will be used when both players have the same custom kit.",
  8186.                 ["KitsNotConfigured"] = "No kits have been configured for dueling.",
  8187.                 ["RemovedXWalls"] = "Removed {0} walls.",
  8188.                 ["SupportCreated"] = "{0} new dueling zones were created, however the total amount was not met. Please lower the radius, increase Maximum Incline On Hills, or reload the plugin to try again.",
  8189.                 ["SupportInvalidConfig"] = "Invalid zone radius detected in the configuration file for this map size. Please lower the radius, increase Maximum Incline On Hills, or reload the plugin to try again.",
  8190.                 ["WallSyntax"] = "Use <color=orange>/{0} walls [radius] <wood|stone></color>, or stand inside of an existing area with walls and use <color=orange>/{0} walls</color> to remove them.",
  8191.                 ["GeneratedWalls"] = "Generated {0} arena walls {1} high at {2} in {3}ms",
  8192.                 ["ResetKit"] = "You are no longer using a custom kit.",
  8193.                 ["HelpDuels"] = "<color=#183a0e><size=18>DUELIST ({0})</size></color><color=#5A625B>\nDuel other players.</color>",
  8194.                 ["HelpAllow"] = "<color=#5A397A>/{0} allow</color><color=#5A625B> • Toggle requests for duels</color>",
  8195.                 ["HelpBlock"] = "<color=#5A397A>/{0} block <name></color><color=#5A625B> • Toggle block requests for a player</color>",
  8196.                 ["HelpChallenge"] = "<color=#5A397A>/{0} <name></color><color=#5A625B> • Challenge another player</color>",
  8197.                 ["HelpAccept"] = "<color=#5A397A>/{0} accept</color><color=#5A625B> • Accept a challenge</color>",
  8198.                 ["HelpCancel"] = "<color=#5A397A>/{0} cancel</color><color=#5A625B> • Cancel your duel request</color>",
  8199.                 ["HelpQueue"] = "<color=#5A397A>/{0}</color><color=#5A625B> • Join duel queue</color>",
  8200.                 ["HelpChat"] = "<color=#5A397A>/{0} chat</color><color=#5A625B> • Toggle duel death messages</color>",
  8201.                 ["HelpLadder"] = "<color=#5A397A>/{0} ladder</color><color=#5A625B> • Show top 10 duelists</color>",
  8202.                 ["HelpBet"] = "<color=#5A397A>/{0} bet</color><color=#5A625B> • Place a bet towards a duel</color>",
  8203.                 ["TopFormat"] = "<color=#666666><color=#5A625B>{0}.</color> <color=#00FF00>{1}</color> (<color=#008000>W:{2}</color> • <color=#ff0000>L:{3} </color> • <color=#4c0000>WLR:{4}</color>)</color>",
  8204.                 ["NowDueling"] = "<color=#ff0000>You are now dueling <color=#00FF00>{0}</color>!</color>",
  8205.                 ["MoneyRequired"] = "Both players must be able to pay an entry fee of <color=#008000>${0}</color> to duel.",
  8206.                 ["CannotShop"] = "You are not allowed to shop while dueling.",
  8207.                 ["DuelRequestSent"] = "Sent request to duel <color=lime>{0}</color>. Request expires in 1 minute. Use <color=orange>/{1} cancel</color> to cancel this request.",
  8208.                 ["DuelRequestReceived"] = "<color=lime>{0}</color> has requested a duel. You have 1 minute to type <color=orange>/{1} accept</color> to accept the duel, or use <color=orange>/{1} decline</color> to decline immediately.",
  8209.                 ["MatchQueued"] = "You have entered the deathmatch queue. The match will start when a dueling zone becomes available.",
  8210.                 ["MatchTeamed"] = "You are not allowed to do this while on a deathmatch team.",
  8211.                 ["MatchNoMatchesExist"] = "No matches exist. Challenge a player by using <color=orange>/{0} name</color>",
  8212.                 ["MatchStarted"] = "Your match is starting versus: <color=yellow>{0}</color>",
  8213.                 ["MatchStartedAlready"] = "Your match has already started. You must wait for it to end.",
  8214.                 ["MatchPlayerLeft"] = "You have removed yourself from your deathmatch team.",
  8215.                 ["MatchCannotChallenge"] = "{0} is already in a match.",
  8216.                 ["MatchCannotChallengeAgain"] = "You can only challenge one player at a time.",
  8217.                 ["MatchRequested"] = "<color=lime>{0}</color> has requested a deathmatch. Use <color=orange>/{1} accept</color> to accept this challenge.",
  8218.                 ["MatchRequestSent"] = "Match request sent to <color=lime>{0}</color>.",
  8219.                 ["MatchNoneRequested"] = "No one has challenged you to a deathmatch yet.",
  8220.                 ["MatchPlayerOffline"] = "The player challenging you is no longer online.",
  8221.                 ["MatchSizeChanged"] = "Deathmatch changed to <color=yellow>{0}v{0}</color>.",
  8222.                 ["MatchOpened"] = "Your deathmatch is now open for private invitation. Friends may use <color=orange>/{0} any</color>, and players may use <color=orange>/{0} {1}</color> to join your team. Use <color=orange>/{0} public</color> to toggle invitations as public or private.",
  8223.                 ["MatchCancelled"] = "{0} has cancelled the deathmatch.",
  8224.                 ["MatchNotAHost"] = "You must be a host of a deathmatch to use this command.",
  8225.                 ["MatchDoesntExist"] = "You are not in a deathmatch. Challenge a player by using <color=orange>/{0} name</color>.",
  8226.                 ["MatchSizeSyntax"] = "Invalid syntax, use /{0} size #",
  8227.                 ["MatchTeamFull"] = "Team is full ({0} players)",
  8228.                 ["MatchJoinedTeam"] = "{0} joined {1} ({2}/{3}). {4} ({5}/{3})",
  8229.                 ["MatchNoPlayersLeft"] = "No players are left on the opposing team. Match cancelled.",
  8230.                 ["MatchChallenge2"] = "<color=#5A397A>/{0} any</color><color=#5A625B> • Join any match where a friend is the host</color>",
  8231.                 ["MatchChallenge3"] = "<color=#5A397A>/{0} <code></color><color=#5A625B> • Join a match with the provided code</color>",
  8232.                 ["MatchAccept"] = "<color=#5A397A>/{0} accept</color><color=#5A625B> • Accept a challenge</color>",
  8233.                 ["MatchCancel"] = "<color=#5A397A>/{0} cancel</color><color=#5A625B> • Cancel your match request</color>",
  8234.                 ["MatchLeave"] = "<color=#5A397A>/{0} cancel</color><color=#5A625B> • Leave your match</color>",
  8235.                 ["MatchSize"] = "<color=#5A397A>/{0} size #</color><color=#5A625B> • Set your match size ({1}v{1}) [Hosts Only]</color>",
  8236.                 ["MatchKickBan"] = "<color=#5A397A>/{0} kickban id/name</color><color=#5A625B> • Kickban a player from the match [Host Only]</color>",
  8237.                 ["MatchSetCode"] = "<color=#5A397A>/{0} setcode [code]</color><color=#5A625B> • Change or see your code [Host Only]</color>",
  8238.                 ["MatchTogglePublic"] = "<color=#5A397A>/{0} public</color><color=#5A625B> • Toggle match as public or private invitation [Host Only]</color>",
  8239.                 ["MatchDefeat"] = "<color=silver><color=lime>{0}</color> has defeated <color=lime>{1}</color> in a <color=yellow>{2}v{2}</color> deathmatch!</color>",
  8240.                 ["MatchIsNotNaked"] = "Match cannot start because <color=lime>{0}</color> is not naked. Next queue check in 30 seconds.",
  8241.                 ["MatchCannotBan"] = "You cannot ban this player, or this player is already banned.",
  8242.                 ["MatchBannedUser"] = "You have banned <color=lime>{0}</color> from your team.",
  8243.                 ["MatchPlayerNotFound"] = "<color=lime>{0}</color> is not on your team.",
  8244.                 ["MatchCodeIs"] = "Your code is: {0}",
  8245.                 ["InQueueList"] = "Players in the queue:",
  8246.                 ["HelpTDM"] = "<color=#5A397A>/{0}</color><color=#5A625B> • Create a team deathmatch</color>",
  8247.                 ["InMatchListGood"] = "Good Team: {0}",
  8248.                 ["InMatchListEvil"] = "Evil Team: {0}",
  8249.                 ["MatchNoTeamFoundCode"] = "No team could be found for you with the provided code: {0}",
  8250.                 ["MatchNoTeamFoundAny"] = "No team could be found with a friend as the host. Use a code instead.",
  8251.                 ["MatchPublic"] = "Your match is now open to the public.",
  8252.                 ["MatchPrivate"] = "Your match is now private and requires a code, or to be a friend to join.",
  8253.                 ["CannotBank"] = "You are not allowed to bank while dueling.",
  8254.                 ["TargetMustBeNaked"] = "<color=red>The person you are challenging must be naked before you can challenge them.</color>",
  8255.                 ["MatchKit"] = "<color=#5A397A>/{0} kit <name></color><color=#5A625B> • Changes the kit used [Host Only]</color>",
  8256.                 ["MatchKitSet"] = "Kit set to: <color=yellow>{0}</color>",
  8257.                 ["MatchChallenge0"] = "<color=#5A397A>/{0} <name> [kitname]</color><color=#5A625B> • Challenge another player and set the kit if specified</color>",
  8258.                 ["MatchPlayerDefeated"] = "<color=silver><color=lime>{0}</color> was killed by <color=lime>{1}</color> using <color=red>{2}</color> (<color=red>{3}: {4}m</color>)</color>",
  8259.                 ["CommandNotAllowed"] = "You are not allowed to use this command right now.",
  8260.                 ["CommandNotAllowedQueued"] = "You are not allowed to use this command while queued.",
  8261.                 ["HelpKit"] = "<color=#5A397A>/{0} kit</color><color=#5A625B> • Pick a kit</color>",
  8262.                 ["RemovedXWallsCustom"] = "Removed {0} walls due to the deletion of zones which exceed the Max Zone cap.",
  8263.                 ["ZonesSetup"] = "Initialized {0} existing dueling zones.",
  8264.                 ["ArenasSetup"] = "{0} existing arenas are now protected.",
  8265.                 ["NoPendingRequests2"] = "You have no pending request to accept.",
  8266.                 ["MatchNoLongerValid"] = "You cannot join this match anymore.",
  8267.                 ["NoMatchesExistYet"] = "No matches exist yet.",
  8268.                 ["UI_Accept"] = "Accept",
  8269.                 ["UI_Decline"] = "Decline",
  8270.                 ["UI_Kits"] = "Kits",
  8271.                 ["UI_Public"] = "Public",
  8272.                 ["UI_Queue"] = "Queue",
  8273.                 ["UI_TDM"] = "TDM",
  8274.                 ["UI_TeamSize"] = "Set Team Size: ",
  8275.                 ["UI_Any"] = "Exists",
  8276.                 ["UI_Help"] = "<color=#5A397A>/{0}</color><color=#5A625B> • Show Duelist User Interface</color>",
  8277.                 ["ResetSeed"] = "Stats for this seed have been reset.",
  8278.                 ["RematchNone"] = "No rematches are available for you.",
  8279.                 ["RematchNotify"] = "A rematch is available for {0} seconds. Click Ready to join, or type /{1} ready",
  8280.                 ["UI_Ready"] = "Ready",
  8281.                 ["RematchAccepted"] = "You have accepted the rematch.",
  8282.                 ["RematchAcceptedAlready"] = "You have accepted the rematch already!",
  8283.                 ["RematchTimedOut"] = "Your rematch timed out.",
  8284.                 ["RematchFailed"] = "The rematch failed to start. Not all players were ready.",
  8285.                 ["RematchFailed2"] = "The rematch failed to open. Not all players are available.",
  8286.                 ["RematchAutoOn"] = "You will now automatically ready up for rematches.",
  8287.                 ["RematchAutoOff"] = "You will no longer automatically ready up for rematches.",
  8288.                 ["UI_Respawn"] = "Respawn",
  8289.                 ["UI_Requeue"] = "Requeue",
  8290.                 ["BeginSpectating"] = "You are now spectating.",
  8291.                 ["EndSpectating"] = "You are no longer a spectator.",
  8292.                 ["UI_ReadyOn"] = "<color=red>Ready On</color>",
  8293.                 ["UI_ReadyOff"] = "Ready Off",
  8294.                 ["SuicideBlock"] = "<color=red>You have suicided or disconnected in a duel and must wait up to 60 seconds to duel again.</color>",
  8295.                 ["ZoneRenamed"] = "Zone renamed to {0}",
  8296.                 ["ZoneNames"] = "<color=#183a0e>Zone Names ({0}):</color> {1}",
  8297.                 ["ZoneRename"] = "/{0} rename <name>",
  8298.                 ["ZoneSet"] = "Zone set to: {0}",
  8299.                 ["Prefix"] = "[ <color=#406B35>Duelist</color> ]: ",
  8300.             }, this);
  8301.         }
  8302.  
  8303.         public List<string> VerifiedKits
  8304.         {
  8305.             get
  8306.             {
  8307.                 VerifyKits();
  8308.  
  8309.                 var list = new List<string>();
  8310.  
  8311.                 if (hpDuelingKits.Count > 0)
  8312.                     list.AddRange(hpDuelingKits);
  8313.  
  8314.                 if (lpDuelingKits.Count > 0)
  8315.                     list.AddRange(lpDuelingKits);
  8316.  
  8317.                 if (list.Count == 0 && customKits.Count > 0)
  8318.                 {
  8319.                     list.AddRange(customKits.ToList().Select(kvp => kvp.Key));
  8320.                 }
  8321.  
  8322.                 list.Sort();
  8323.                 return list;
  8324.             }
  8325.         }
  8326.  
  8327.         public string GetVerifiedKit(string kit)
  8328.         {
  8329.             string kits = string.Join(", ", VerifiedKits.ToArray());
  8330.  
  8331.             if (!string.IsNullOrEmpty(kits))
  8332.             {
  8333.                 if (customKits.Exists(entry => entry.Key.Equals(kit, StringComparison.CurrentCultureIgnoreCase)))
  8334.                 {
  8335.                     return customKits.FirstOrDefault(entry => entry.Key.Equals(kit, StringComparison.CurrentCultureIgnoreCase)).Key;
  8336.                 }
  8337.                 if (hpDuelingKits.Exists(entry => entry.Equals(kit, StringComparison.CurrentCultureIgnoreCase)))
  8338.                 {
  8339.                     return hpDuelingKits.FirstOrDefault(entry => entry.Equals(kit, StringComparison.CurrentCultureIgnoreCase));
  8340.                 }
  8341.                 if (lpDuelingKits.Exists(entry => entry.Equals(kit, StringComparison.CurrentCultureIgnoreCase)))
  8342.                 {
  8343.                     return lpDuelingKits.FirstOrDefault(entry => entry.Equals(kit, StringComparison.CurrentCultureIgnoreCase));
  8344.                 }
  8345.             }
  8346.  
  8347.             return null;
  8348.         }
  8349.  
  8350.         private void LoadVariables()
  8351.         {
  8352.             LoadAnimalSettings();
  8353.             LoadNormalSettings();            
  8354.             LoadDeviceSettings();
  8355.             LoadBetSettings();
  8356.             LoadZoneSettings();
  8357.             LoadDeployableSettings();
  8358.             LoadRankedSettings();
  8359.             LoadKitSettings();
  8360.             LoadRespawnSettings();
  8361.             LoadRewardSettings();
  8362.             LoadSpawnSettings();
  8363.             LoadDeathmatchSettings();
  8364.             LoadAdvancedSettings();
  8365.             LoadUserInterfaceSettings();
  8366.             LoadSpectatorSettings();                        
  8367.             RegisterCommands();
  8368.             EnsureLimits();
  8369.  
  8370.             if (Changed)
  8371.             {
  8372.                 SaveConfig();
  8373.                 Changed = false;
  8374.             }
  8375.         }
  8376.  
  8377.         private void LoadAnimalSettings()
  8378.         {
  8379.             putToSleep = Convert.ToBoolean(GetConfig("Animals", "Put To Sleep", true));
  8380.             killNpc = Convert.ToBoolean(GetConfig("Animals", "Die Instantly", false));
  8381.         }
  8382.  
  8383.         private void LoadNormalSettings()
  8384.         {
  8385.             chatSteamID = Convert.ToUInt64(GetConfig("Settings", "Chat SteamID", 0uL));
  8386.             setPlayerTime = Convert.ToBoolean(GetConfig("Settings", "Set Preferred Environment Plugin Time", false));
  8387.             removePlayers = Convert.ToBoolean(GetConfig("Settings", "Prevent Players Not Dueling From Entering The Zone", false));
  8388.             announceTime = Convert.ToSingle(GetConfig("Settings", "Announce Duel Information Every X Seconds", 1800f));
  8389.             wipeDuelZones = Convert.ToBoolean(GetConfig("Settings", "Auto Wipe Dueling Zones On Map Wipe", true));
  8390.             autoSetup = Convert.ToBoolean(GetConfig("Settings", "Auto Create Dueling Zone If Zone Does Not Exist", false));
  8391.             immunityTime = Convert.ToInt32(GetConfig("Settings", "Immunity Time", 10));
  8392.             deathTime = Convert.ToInt32(GetConfig("Settings", "Time To Duel In Minutes Before Death", 10));
  8393.             szDuelChatCommand = Convert.ToString(GetConfig("Settings", "Duel Command Name", "duel"));
  8394.             szQueueChatCommand = Convert.ToString(GetConfig("Settings", "Queue Command Name", "queue"));
  8395.             useAnnouncement = Convert.ToBoolean(GetConfig("Settings", "Allow Announcement", true));
  8396.             broadcastDefeat = Convert.ToBoolean(GetConfig("Settings", "Broadcast Defeat To All Players", true));
  8397.             damageScaleAmount = Convert.ToSingle(GetConfig("Settings", "Scale Damage Percent", 1f));
  8398.             buildingBlockExtensionRadius = Convert.ToSingle(GetConfig("Settings", "Building Block Extension Radius", 30f));
  8399.             autoAllowAll = Convert.ToBoolean(GetConfig("Settings", "Disable Requirement To Allow Duels", false));
  8400.             useRandomSkins = Convert.ToBoolean(GetConfig("Settings", "Use Random Skins", true));
  8401.             playerHealth = Convert.ToSingle(GetConfig("Settings", "Player Health After Duel [0 = disabled]", 100f));
  8402.             autoEnable = Convert.ToBoolean(GetConfig("Settings", "Auto Enable Dueling If Zone(s) Exist", false));
  8403.             bypassNewmans = Convert.ToBoolean(GetConfig("Settings", "Bypass Naked Check And Strip Items Anyway", false));
  8404.             respawnDeadDisconnect = Convert.ToBoolean(GetConfig("Settings", "Respawn Dead Players On Disconnect", true));
  8405.             resetSeed = Convert.ToBoolean(GetConfig("Settings", "Reset Temporary Ladder Each Wipe", true));
  8406.             noStability = Convert.ToBoolean(GetConfig("Settings", "No Stability On Structures", true));
  8407.             noMovement = Convert.ToBoolean(GetConfig("Settings", "No Movement During Immunity", false));
  8408.             respawnWalls = Convert.ToBoolean(GetConfig("Settings", "Respawn Zone Walls On Death", false));
  8409.             sphereAmount = Convert.ToInt32(GetConfig("Settings", "Create Dome Around Event Using Spheres (0 = disabled, recommended = 5)", 0));
  8410.             blockSpawning = Convert.ToBoolean(GetConfig("Settings", "Prevent Players From Spawning In Zone", true));
  8411.             useBlacklistCommands = Convert.ToBoolean(GetConfig("Settings", "Blacklist Commands", false));
  8412.             blacklistCommands = (GetConfig("Settings", "Blacklisted Chat Commands", BlacklistedCommands) as List<object>).Where(o => o != null && o.ToString().Length > 0).ToList().Select(o => o.ToString().ToLower()).ToList();
  8413.             useWhitelistCommands = Convert.ToBoolean(GetConfig("Settings", "Whitelist Commands", false));
  8414.             whitelistCommands = (GetConfig("Settings", "Whitelisted Chat Commands", WhitelistedCommands) as List<object>).Where(o => o != null && o.ToString().Length > 0).ToList().Select(o => o.ToString().ToLower()).ToList();
  8415.         }
  8416.  
  8417.         private void LoadDeviceSettings()
  8418.         {
  8419.             autoOvens = Convert.ToBoolean(GetConfig("Automatically Power On Devices", "Ovens", false));
  8420.             autoFlames = Convert.ToBoolean(GetConfig("Automatically Power On Devices", "FlameTurrets", false));
  8421.             autoTurrets = Convert.ToBoolean(GetConfig("Automatically Power On Devices", "AutoTurrets", false));
  8422.         }
  8423.  
  8424.         private void LoadBetSettings()
  8425.         {
  8426.             allowBetForfeit = Convert.ToBoolean(GetConfig("Betting", "Allow Bets To Be Forfeit", true));
  8427.             allowBetRefund = Convert.ToBoolean(GetConfig("Betting", "Allow Bets To Be Refunded", false));
  8428.             allowBets = Convert.ToBoolean(GetConfig("Betting", "Enabled", false));
  8429.  
  8430.             SetupBets();
  8431.         }
  8432.  
  8433.         private void LoadZoneSettings()
  8434.         {
  8435.             zoneRadius = Convert.ToSingle(GetConfig("Zone", "Zone Radius (Min: 50, Max: 300)", 50f));
  8436.             zoneCounter = Convert.ToInt32(GetConfig("Zone", "Create New Zone Every X Duels [0 = disabled]", 10));
  8437.             maxIncline = Convert.ToSingle(GetConfig("Zone", "Maximum Incline On Hills", 40f));
  8438.             zoneAmount = Convert.ToInt32(GetConfig("Zone", "Max Zones [Min 1]", 1));
  8439.             playersPerZone = Convert.ToInt32(GetConfig("Zone", "Players Per Zone [Multiple Of 2]", 10));
  8440.             visibleToAdmins = Convert.ToBoolean(GetConfig("Zone", "Players Visible To Admins", true));
  8441.             avoidWaterSpawns = Convert.ToBoolean(GetConfig("Zone", "Avoid Creating Automatic Spawn Points In Water", true));
  8442.             extraWallStacks = Convert.ToInt32(GetConfig("Zone", "Extra High External Wall Stacks", 2));
  8443.             useZoneWalls = Convert.ToBoolean(GetConfig("Zone", "Use Arena Wall Generation", true));
  8444.             zoneUseWoodenWalls = Convert.ToBoolean(GetConfig("Zone", "Use Wooden Walls", false));
  8445.             useLeastAmount = Convert.ToBoolean(GetConfig("Zone", "Create Least Amount Of Walls", false));
  8446.         }
  8447.  
  8448.         private void LoadDeployableSettings()
  8449.         {
  8450.             morphBarricadesStoneWalls = Convert.ToBoolean(GetConfig("Deployables", "Morph Barricades Into High External Stone Walls", false));
  8451.             morphBarricadesWoodenWalls = Convert.ToBoolean(GetConfig("Deployables", "Morph Barricades Into High External Wooden Walls", false));
  8452.         }
  8453.  
  8454.         private void LoadRankedSettings()
  8455.         {
  8456.             recordStats = Convert.ToBoolean(GetConfig("Ranked Ladder", "Enabled", true));
  8457.             permsToGive = Convert.ToInt32(GetConfig("Ranked Ladder", "Award Top X Players On Wipe", 3));
  8458.         }
  8459.  
  8460.         private void LoadRespawnSettings()
  8461.         {
  8462.             autoKitName = Convert.ToString(GetConfig("Respawn", "Give Kit If Respawn Items Are Empty", "autokit"));
  8463.             var defaultRespawn = GetConfig("Respawn", "Items", RespawnLoot) as List<object>;
  8464.  
  8465.             SetupRespawnItems(defaultRespawn, ref respawnLoot);
  8466.         }
  8467.  
  8468.         private void LoadRewardSettings()
  8469.         {
  8470.             economicsMoney = Convert.ToDouble(GetConfig("Rewards", "Economics Money [0 = disabled]", 0.0));
  8471.             serverRewardsPoints = Convert.ToInt32(GetConfig("Rewards", "ServerRewards Points [0 = disabled]", 0));
  8472.             requiredDuelMoney = Convert.ToDouble(GetConfig("Rewards", "Required Money To Duel", 0.0));
  8473.         }
  8474.  
  8475.         private void LoadSpawnSettings()
  8476.         {
  8477.             spDrawTime = Convert.ToSingle(GetConfig("Spawns", "Draw Time", 30f));
  8478.             spRemoveOneMaxDistance = Convert.ToSingle(GetConfig("Spawns", "Remove Distance", 10f));
  8479.             spRemoveAllMaxDistance = Convert.ToSingle(GetConfig("Spawns", "Remove All Distance", zoneRadius));
  8480.             //spRemoveInRange = Convert.ToBoolean(GetConfig("Spawns", "Remove In Duel Zone Only", false));
  8481.             spAutoRemove = Convert.ToBoolean(GetConfig("Spawns", "Auto Remove On Zone Removal", false));
  8482.         }
  8483.  
  8484.         private void LoadDeathmatchSettings()
  8485.         {
  8486.             dmFF = Convert.ToBoolean(GetConfig("Deathmatch", "Friendly Fire", true));
  8487.             minDeathmatchSize = Convert.ToInt32(GetConfig("Deathmatch", "Min Team Size", 2));
  8488.             maxDeathmatchSize = Convert.ToInt32(GetConfig("Deathmatch", "Max Team Size", 5));
  8489.             teamEvilShirt = Convert.ToUInt64(GetConfig("Deathmatch", "Evil Shirt Skin", 14177));
  8490.             teamGoodShirt = Convert.ToUInt64(GetConfig("Deathmatch", "Good Shirt Skin", 101));
  8491.             teamShirt = Convert.ToString(GetConfig("Deathmatch", "Shirt Shortname", "tshirt"));
  8492.             teamEconomicsMoney = Convert.ToDouble(GetConfig("Deathmatch", "Economics Money [0 = disabled]", 0.0));
  8493.             teamServerRewardsPoints = Convert.ToInt32(GetConfig("Deathmatch", "ServerRewards Points [0 = disabled]", 0));
  8494.             tdmEnabled = Convert.ToBoolean(GetConfig("Deathmatch", "Enabled", true));
  8495.             szMatchChatCommand = Convert.ToString(GetConfig("Deathmatch", "Chat Command", "tdm"));
  8496.             tdmServerDeaths = Convert.ToBoolean(GetConfig("Deathmatch", "Announce Deaths To Server", false));
  8497.             tdmMatchDeaths = Convert.ToBoolean(GetConfig("Deathmatch", "Announce Deaths To Match", true));
  8498.         }
  8499.  
  8500.         private void LoadAdvancedSettings()
  8501.         {
  8502.             requireTeamSize = Convert.ToBoolean(GetConfig("Advanced Options", "Require TDM Minimum Spawn Points To Be Equal Or Greater To The Number Of Players Joining", false));
  8503.             requiredMinSpawns = Convert.ToInt32(GetConfig("Advanced Options", "Require 1v1 Minimum Spawn Points To Be Equal Or Greater Than X", 2));
  8504.             requiredMaxSpawns = Convert.ToInt32(GetConfig("Advanced Options", "Require 1v1 Maximum Spawn Points To Be Less Than Or Equal To X", 200));
  8505.             allowPlayerDeaths = Convert.ToBoolean(GetConfig("Advanced Options", "Let Players Die Normally", false));
  8506.             sendDeadHome = Convert.ToBoolean(GetConfig("Advanced Options", "Send Dead Players Back Home", true));
  8507.             sendDefeatedHome = Convert.ToBoolean(GetConfig("Advanced Options", "Send Defeated Players Back Home", false));
  8508.         }
  8509.  
  8510.         private void LoadUserInterfaceSettings()
  8511.         {
  8512.             guiAutoEnable = Convert.ToBoolean(GetConfig("User Interface", "Auto Enable GUI For Players", false));
  8513.             szUIChatCommand = Convert.ToString(GetConfig("User Interface", "Chat Command", "dui"));
  8514.             guiUseCursor = Convert.ToBoolean(GetConfig("User Interface", "Use Cursor", false));
  8515.             guiUseCloseButton = Convert.ToBoolean(GetConfig("User Interface", "Show Close Button (X)", true));
  8516.             guiAnnounceUITime = Convert.ToSingle(GetConfig("User Interface", "Show Defeat Message UI For X Seconds", 7.5f));
  8517.             sendHomeRequeue = Convert.ToBoolean(GetConfig("User Interface", "Send Spectators Home FirstOrDefault When Clicking Requeue", false));
  8518.         }
  8519.  
  8520.         private void LoadSpectatorSettings()
  8521.         {
  8522.             sendHomeSpectatorWhenRematchTimesOut = Convert.ToBoolean(GetConfig("Spectators", "Send Home If Rematch Times Out", false));
  8523.         }
  8524.  
  8525.         private void RegisterCommands()
  8526.         {
  8527.             if (!permission.PermissionExists(duelistPerm)) // prevent warning
  8528.                 permission.RegisterPermission(duelistPerm, this);
  8529.  
  8530.             permission.CreateGroup(duelistGroup, duelistGroup, 0);
  8531.             permission.GrantGroupPermission(duelistGroup, duelistPerm, this);
  8532.  
  8533.             AddCovalenceCommand("duelist", nameof(CommandDuelist));
  8534.  
  8535.             if (!string.IsNullOrEmpty(szDuelChatCommand))
  8536.             {
  8537.                 cmd.AddChatCommand(szDuelChatCommand, this, cmdDuel);
  8538.                 cmd.AddConsoleCommand(szDuelChatCommand, this, nameof(ccmdDuel));
  8539.                 whitelistCommands.Add(szDuelChatCommand.ToLower());
  8540.             }
  8541.  
  8542.             if (!string.IsNullOrEmpty(szQueueChatCommand))
  8543.                 cmd.AddChatCommand(szQueueChatCommand, this, cmdQueue);
  8544.  
  8545.             if (tdmEnabled && !string.IsNullOrEmpty(szMatchChatCommand))
  8546.             {
  8547.                 cmd.AddChatCommand(szMatchChatCommand, this, cmdTDM);
  8548.                 whitelistCommands.Add(szMatchChatCommand.ToLower());
  8549.             }
  8550.  
  8551.             if (!string.IsNullOrEmpty(szUIChatCommand))
  8552.             {
  8553.                 cmd.AddChatCommand(szUIChatCommand, this, cmdDUI);
  8554.                 cmd.AddConsoleCommand(szUIChatCommand, this, nameof(ccmdDUI));
  8555.             }
  8556.         }
  8557.  
  8558.         private void SetupBets()
  8559.         {
  8560.             var bets = GetConfig("Betting", "Bets", DefaultBets) as List<object>;
  8561.  
  8562.             foreach (var bet in bets)
  8563.             {
  8564.                 duelingBets.Add(JsonConvert.DeserializeObject<BetInfo>(JsonConvert.SerializeObject(bet)));
  8565.             }
  8566.         }
  8567.  
  8568.         private void LoadKitSettings()
  8569.         {
  8570.             var kits = GetConfig("Settings", "Kits", DefaultKits) as List<object>;
  8571.  
  8572.             if (kits != null && kits.Count > 0)
  8573.             {
  8574.                 foreach (object obj in kits)
  8575.                 {
  8576.                     var kit = Convert.ToString(obj);
  8577.  
  8578.                     if (!string.IsNullOrEmpty(kit) && !hpDuelingKits.Contains(kit))
  8579.                     {
  8580.                         hpDuelingKits.Add(kit); // 0.1.14 fix
  8581.                         _hpDuelingKits.Add(kit); // 0.1.17 clone for Least Used Chance compatibility
  8582.                     }
  8583.                 }
  8584.             }
  8585.  
  8586.             lesserKitChance = Convert.ToSingle(GetConfig("Settings", "Kits Least Used Chance", 0.25f));
  8587.            
  8588.             var lesserKits = GetConfig("Settings", "Kits Least Used", DefaultLesserKits) as List<object>;
  8589.  
  8590.             foreach (object obj in lesserKits)
  8591.             {
  8592.                 var kit = Convert.ToString(obj);
  8593.  
  8594.                 if (!string.IsNullOrEmpty(kit) && !lpDuelingKits.Contains(kit))
  8595.                 {
  8596.                     lpDuelingKits.Add(kit); // 0.1.16
  8597.                     _lpDuelingKits.Add(kit); // 0.1.17 clone for Least Used Chance compatibility
  8598.                 }
  8599.             }
  8600.  
  8601.             useWorkshopSkins = Convert.ToBoolean(GetConfig("Custom Kits", "Use Workshop Skins", true));
  8602.  
  8603.             var defaultKits = GetConfig("Custom Kits", "Kits", DefaultCustomKits) as Dictionary<string, object>;
  8604.  
  8605.             SetupCustomKits(defaultKits, ref customKits);
  8606.         }
  8607.  
  8608.         private void EnsureLimits()
  8609.         {
  8610.             if (buildingBlockExtensionRadius < 20f)
  8611.                 buildingBlockExtensionRadius = 20f;
  8612.  
  8613.             if (zoneAmount < 1)
  8614.                 zoneAmount = 1;
  8615.  
  8616.             if (playersPerZone < 2)
  8617.                 playersPerZone = 2;
  8618.             else if (playersPerZone % 2 != 0)
  8619.                 playersPerZone++;
  8620.  
  8621.             if (immunityTime < 0)
  8622.                 immunityTime = 0;
  8623.  
  8624.             if (zoneRadius < 50f)
  8625.                 zoneRadius = 50f;
  8626.             else if (zoneRadius > 300f)
  8627.                 zoneRadius = 300f;
  8628.  
  8629.             if (requiredMinSpawns < 2)
  8630.                 requiredMinSpawns = 2;
  8631.  
  8632.             if (requiredMaxSpawns < 2)
  8633.                 requiredMaxSpawns = 2;
  8634.  
  8635.             if (guiAnnounceUITime < 1f)
  8636.                 guiAnnounceUITime = 1f;
  8637.         }
  8638.  
  8639.         private void SetupDefinitions()
  8640.         {
  8641.             foreach (var itemDef in ItemManager.GetItemDefinitions())
  8642.             {
  8643.                 var mod = itemDef.GetComponent<ItemModDeployable>();
  8644.  
  8645.                 if (mod == null)
  8646.                 {
  8647.                     continue;
  8648.                 }
  8649.  
  8650.                 bool externalWall = mod.entityPrefab.resourcePath.Contains("external") && mod.entityPrefab.resourcePath.Contains("wall");
  8651.                 bool barricade = mod.entityPrefab.resourcePath.Contains("barricade");
  8652.  
  8653.                 if (externalWall || barricade)
  8654.                 {
  8655.                     bool value = Convert.ToBoolean(GetConfig("Deployables", string.Format("Allow {0}", itemDef.displayName.translated), false));
  8656.  
  8657.                     if (!value)
  8658.                         continue;
  8659.  
  8660.                     deployables[itemDef.displayName.translated] = value;
  8661.                     prefabs[mod.entityPrefab.resourcePath] = itemDef.displayName.translated;
  8662.                 }
  8663.             }
  8664.         }
  8665.  
  8666.         private void SetupRespawnItems(List<object> list, ref List<DuelKitItem> source)
  8667.         {
  8668.             foreach (var entry in list)
  8669.             {
  8670.                 source.Add(JsonConvert.DeserializeObject<DuelKitItem>(JsonConvert.SerializeObject(entry)));
  8671.             }
  8672.         }
  8673.  
  8674.         private void SetupCustomKits(Dictionary<string, object> dict, ref Dictionary<string, List<DuelKitItem>> source)
  8675.         {
  8676.             foreach (var entry in dict)
  8677.             {
  8678.                 source[entry.Key] = JsonConvert.DeserializeObject<List<DuelKitItem>>(JsonConvert.SerializeObject(entry.Value));
  8679.             }
  8680.         }
  8681.  
  8682.         protected override void LoadDefaultConfig()
  8683.         {
  8684.             PrintWarning("Creating a new configuration file");
  8685.             Config.Clear();
  8686.             LoadVariables();
  8687.         }
  8688.  
  8689.         private object GetConfig(string menu, string datavalue, object defaultValue)
  8690.         {
  8691.             var data = Config[menu] as Dictionary<string, object>;
  8692.             if (data == null)
  8693.             {
  8694.                 data = new Dictionary<string, object>();
  8695.                 Config[menu] = data;
  8696.                 Changed = true;
  8697.             }
  8698.             object value;
  8699.             if (!data.TryGetValue(datavalue, out value))
  8700.             {
  8701.                 value = defaultValue;
  8702.                 data[datavalue] = value;
  8703.                 Changed = true;
  8704.             }
  8705.             return value;
  8706.         }
  8707.  
  8708.         private Dictionary<string, string> hexColors = new Dictionary<string, string>
  8709.         {
  8710.             ["<color=blue>"] = "<color=#0000FF>",
  8711.             ["<color=red>"] = "<color=#FF0000>",
  8712.             ["<color=yellow>"] = "<color=#FFFF00>",
  8713.             ["<color=lightblue>"] = "<color=#ADD8E6>",
  8714.             ["<color=orange>"] = "<color=#FFA500>",
  8715.             ["<color=silver>"] = "<color=#C0C0C0>",
  8716.             ["<color=magenta>"] = "<color=#FF00FF>",
  8717.             ["<color=green>"] = "<color=#008000>",
  8718.             ["<color=lime>"] = "<color=#00FF00>",
  8719.         };
  8720.  
  8721.         private string msg(string key, string id = null, params object[] args)
  8722.         {
  8723.             var sb = new System.Text.StringBuilder(id == null ? RemoveFormatting(lang.GetMessage(key, this, id)) : lang.GetMessage(key, this, id));
  8724.             var str = sb.ToString();
  8725.  
  8726.             foreach (var entry in hexColors)
  8727.             {
  8728.                 if (str.Contains(entry.Key))
  8729.                 {
  8730.                     sb.Replace(entry.Key, entry.Value);
  8731.                 }
  8732.             }
  8733.  
  8734.             return args.Length > 0 ? string.Format(sb.ToString(), args) : sb.ToString();
  8735.         }
  8736.  
  8737.         public string RemoveFormatting(string source)
  8738.         {
  8739.             return source.Contains(">") ? Regex.Replace(source, "<.*?>", string.Empty) : source;
  8740.         }
  8741.  
  8742.         public static void Message(BasePlayer player, string message)
  8743.         {
  8744.             Instance.Player.Message(player, message, Instance.chatSteamID);
  8745.         }
  8746.  
  8747.         public void Message(HashSet<BasePlayer> players, string key, params object[] args)
  8748.         {
  8749.             foreach (var player in players)
  8750.             {
  8751.                 if (!player || !player.IsConnected) continue;
  8752.                 Message(player, msg(key, player.UserIDString, args ?? new string[0]));
  8753.             }
  8754.         }
  8755.  
  8756.         #endregion
  8757.     }
  8758. }
  8759.  
  8760. namespace Oxide.Plugins.DuelistExtensionMethods
  8761. {
  8762.     public static class ExtensionMethods
  8763.     {
  8764.         public static bool All<T>(this IEnumerable<T> a, Func<T, bool> b) { using (var c = a.GetEnumerator()) { while (c.MoveNext()) { if (!b(c.Current)) { return false; } } } return true; }
  8765.         public static T ElementAt<T>(this IEnumerable<T> a, int b) { using (var c = a.GetEnumerator()) { while (c.MoveNext()) { if (b == 0) { return c.Current; } b--; } } return default(T); }
  8766.         public static bool Exists<T>(this IEnumerable<T> a, Func<T, bool> b = null) { using (var c = a.GetEnumerator()) { while (c.MoveNext()) { if (b == null || b(c.Current)) { return true; } } } return false; }
  8767.         public static T FirstOrDefault<T>(this IEnumerable<T> a, Func<T, bool> b = null) { using (var c = a.GetEnumerator()) { while (c.MoveNext()) { if (b == null || b(c.Current)) { return c.Current; } } } return default(T); }
  8768.         public static IEnumerable<T> Select<Y, T>(this IList<Y> a, Func<Y, T> b) { var c = new List<T>(); for (int i = 0; i < a.Count; i++) { c.Add(b(a[i])); } return c; }
  8769.         public static string[] Skip(this string[] a, int b) { if (a.Length == 0) { return Array.Empty<string>(); } string[] c = new string[a.Length - b]; int n = 0; for (int i = 0; i < a.Length; i++) { if (i < b) continue; c[n] = a[i]; n++; } return c; }
  8770.         public static List<T> Take<T>(this IList<T> a, int b) { var c = new List<T>(); for (int i = 0; i < a.Count; i++) { if (c.Count == b) { break; } c.Add(a[i]); } return c; }
  8771.         public static List<T> ToList<T>(this IEnumerable<T> a) { var b = new List<T>(); using (var c = a.GetEnumerator()) { while (c.MoveNext()) { b.Add(c.Current); } } return b; }
  8772.         public static IEnumerable<T> Where<T>(this IEnumerable<T> a, Func<T, bool> b) { var c = new List<T>(); using (var d = a.GetEnumerator()) { while (d.MoveNext()) { if (b(d.Current)) { c.Add(d.Current); } } } return c; }
  8773.         public static bool IsHuman(this BasePlayer a) { if (a.IsNpc || !a.userID.IsSteamId()) { return false; } return true; }
  8774.         public static List<T> OfType<T>(this IEnumerable<BaseNetworkable> a) where T : BaseNetworkable { var b = new List<T>(); using (var c = a.GetEnumerator()) { while (c.MoveNext()) { if (c.Current is T) { b.Add(c.Current as T); } } } return b; }
  8775.         public static int Sum<T>(this IList<T> a, Func<T, int> b) { int c = 0; for (int i = 0; i < a.Count; i++) { var d = b(a[i]); if (float.IsNaN(d)) { continue; } c += d; } return c; }
  8776.         public static bool IsKilled(this BaseNetworkable a) { return a == null || a.IsDestroyed || a.transform == null; }
  8777.         public static void SafelyKill(this BaseNetworkable a) { if (a == null || a.IsDestroyed) { return; } a.Kill(BaseNetworkable.DestroyMode.None); }
  8778.         public static bool CanCall(this Plugin a) { return a != null && a.IsLoaded; }
  8779.     }
  8780. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement