Advertisement
MissFox

Untitled

Mar 8th, 2019
163
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 108.78 KB | None | 0 0
  1. using Rust;
  2. using System.Collections.Generic;
  3. using System;
  4. using System.Text;
  5. using UnityEngine;
  6. using Oxide.Core;
  7. using Oxide.Core.Plugins;
  8. using Oxide.Core.Libraries;
  9. using Oxide.Core.Configuration;
  10. using System.Linq;
  11. using Oxide.Game.Rust.Cui;
  12. using System.Reflection;
  13.  
  14. //Repair bug fix.
  15. //Non-firing hooks fix
  16. namespace Oxide.Plugins
  17. {
  18.     [Info("PlayerRanks", "Steenamaroo", "1.4.0", ResourceId = 2359)]
  19.     class PlayerRanks : RustPlugin
  20.     {                                                              
  21.         [PluginReference]
  22.         Plugin Clans, Friends, EventManager, PlaytimeTracker, Economics;
  23.  
  24.         #region RustIO
  25.         private Library lib;
  26.         private MethodInfo isInstalled;
  27.         private MethodInfo hasFriend;
  28.          
  29.         bool IsInstalled()
  30.         {
  31.             return (lib != null) && (bool)isInstalled.Invoke(lib, new object[] { });
  32.         }
  33.  
  34.         bool HasFriend(string playerId, string friendId)
  35.         {
  36.             return (lib != null) && (bool)hasFriend.Invoke(lib, new object[] { playerId, friendId });
  37.         }
  38.         #endregion
  39.         Dictionary<uint, Dictionary<ulong, float>> BradleyAttackers = new Dictionary<uint, Dictionary<ulong, float>>();
  40.         Dictionary<uint, Dictionary<ulong, int>> HeliAttackers = new Dictionary<uint, Dictionary<ulong, int>>();
  41.         Dictionary<ulong, WoundedData> woundedData = new Dictionary<ulong, WoundedData>();
  42.         bool HasPermission(string id, string perm) => permission.UserHasPermission(id, perm);
  43.         private Dictionary<string, bool> allowedCats = new Dictionary<string, bool>();
  44.         List<ulong> airdrops = new List<ulong>();
  45.         const string permAllowed = "playerranks.allowed";
  46.         List<string> Broadcast = new List<string>();
  47.         List<ulong> MenuOpen = new List<ulong>();
  48.         bool catsOnOff;
  49.         Timer broadcastTimer;
  50.  
  51.         public class DataStorage
  52.         {
  53.             public Dictionary<ulong, PRDATA> PlayerRankData = new Dictionary<ulong, PRDATA>();
  54.             public Dictionary<DateTime, Dictionary<string, LeaderBoardData>> leaderBoards = new Dictionary<DateTime, Dictionary<string, LeaderBoardData>>();
  55.         }
  56.        
  57.         public class LeaderBoardData
  58.         {
  59.             public ulong UserID;
  60.             public string UserName;
  61.             public double Score;
  62.         }
  63.        
  64.         public class PRDATA
  65.         {
  66.             public object GetValue(String category)
  67.             {
  68.                      if (category == "PVPKills") return PVPKills;
  69.                 if (category == "PVPDistance") return PVPDistance;
  70.                 if (category == "PVEKills") return PVEKills;
  71.                 if (category == "PVEDistance") return PVEDistance;
  72.                 if (category == "NPCKills") return NPCKills;
  73.                 if (category == "NPCDistance") return NPCDistance;
  74.                 if (category == "SleepersKilled") return SleepersKilled;
  75.                 if (category == "HeadShots") return HeadShots;
  76.                 if (category == "Deaths") return Deaths;
  77.                 if (category == "Suicides") return Suicides;
  78.                 if (category == "KDR") return KDR;
  79.                 if (category == "SDR") return SDR;
  80.                 if (category == "SkullsCrushed") return SkullsCrushed;
  81.                 if (category == "TimesWounded") return TimesWounded;
  82.                 if (category == "TimesHealed") return TimesHealed;
  83.                 if (category == "HeliHits") return HeliHits;
  84.                 if (category == "HeliKills") return HeliKills;
  85.                 if (category == "APCHits") return APCHits;
  86.                 if (category == "APCKills") return APCKills;
  87.                 if (category == "BarrelsDestroyed") return BarrelsDestroyed;
  88.                 if (category == "ExplosivesThrown") return ExplosivesThrown;
  89.                 if (category == "ArrowsFired") return ArrowsFired;
  90.                 if (category == "BulletsFired") return BulletsFired;
  91.                 if (category == "RocketsLaunched") return RocketsLaunched;
  92.                 if (category == "WeaponTrapsDestroyed") return WeaponTrapsDestroyed;
  93.                 if (category == "DropsLooted") return DropsLooted;
  94.                 if (category == "StructuresBuilt") return StructuresBuilt;
  95.                 if (category == "StructuresDemolished") return StructuresDemolished;
  96.                 if (category == "ItemsDeployed") return ItemsDeployed;
  97.                 if (category == "ItemsCrafted") return ItemsCrafted;
  98.                 if (category == "EntitiesRepaired") return EntitiesRepaired;
  99.                 if (category == "ResourcesGathered") return ResourcesGathered;
  100.                 if (category == "StructuresUpgraded") return StructuresUpgraded;
  101.                 return "Not Found";
  102.             }
  103.  
  104.             public bool Admin;
  105.             public ulong UserID;
  106.             public string Name;
  107.             public string TimePlayed = "0";
  108.             public string Status = "offline";
  109.             public int Economics;
  110.             public DateTime ActiveDate = DateTime.UtcNow;
  111.             public int PVPKills;
  112.             public double PVPDistance;
  113.             public int PVEKills;
  114.             public double PVEDistance;
  115.             public int NPCKills;
  116.             public double NPCDistance;
  117.             public int SleepersKilled;  
  118.             public int HeadShots;
  119.             public int Deaths;
  120.             public int Suicides;
  121.             public double KDR;
  122.             public double SDR;
  123.             public int SkullsCrushed;
  124.             public int TimesWounded;
  125.             public int TimesHealed;
  126.             public int HeliHits;
  127.             public int HeliKills;
  128.             public int APCHits;
  129.             public int APCKills;
  130.             public int BarrelsDestroyed;
  131.             public int ExplosivesThrown;
  132.             public int ArrowsFired;
  133.             public int BulletsFired;
  134.             public int RocketsLaunched;
  135.             public int WeaponTrapsDestroyed;        
  136.             public int DropsLooted;
  137.  
  138.             //intense options
  139.             public int StructuresBuilt;
  140.             public int StructuresDemolished;
  141.             public int ItemsDeployed;
  142.             public int ItemsCrafted;
  143.             public int EntitiesRepaired;
  144.             public int ResourcesGathered;
  145.             public int StructuresUpgraded;
  146.         }
  147.  
  148.         class WoundedData
  149.         {
  150.             public float distance;
  151.             public ulong attackerId;
  152.         }
  153.        
  154.         DataStorage data;
  155.         private DynamicConfigFile PRData;
  156.  
  157.         void Loaded()
  158.         {
  159.             SetUp();
  160.             lang.RegisterMessages(messages, this);
  161.             permission.RegisterPermission(permAllowed, this);
  162.             cmd.AddChatCommand($"{conf.Options.chatCommandAlias}", this, "CmdTarget");
  163.             CheckDependencies();
  164.             Sql_conn = Sql.OpenDb(conf.MySQL.sql_host, conf.MySQL.sql_port, conf.MySQL.sql_db, conf.MySQL.sql_user, conf.MySQL.sql_pass + ";CharSet=utf8mb4", this);
  165.         }
  166.  
  167.         void Unload()
  168.         {
  169.             Sql_conn.Con.Dispose();
  170.             foreach (BasePlayer current in BasePlayer.activePlayerList)
  171.                 if (MenuOpen.Contains(current.userID))
  172.                 {
  173.                     CuiHelper.DestroyUi(current, "ranksgui");
  174.                     MenuOpen.Remove(current.userID);
  175.                 }
  176.             SaveData(false);
  177.         }
  178.  
  179.         void OnPlayerDisconnected(BasePlayer player)
  180.         {
  181.             data.PlayerRankData[player.userID].Status = "offline";
  182.             if (MenuOpen.Contains(player.userID))
  183.             {
  184.                 CuiHelper.DestroyUi(player, "ranksgui");
  185.                 MenuOpen.Remove(player.userID);
  186.             }
  187.         }
  188.  
  189.         void SetUp()
  190.         {
  191.             PRData = Interface.Oxide.DataFileSystem.GetFile("PlayerRanks");
  192.             LoadData();
  193.             LoadConfigVariables();
  194.             foreach (var entry in data.PlayerRankData)
  195.                 entry.Value.Status = "offline";
  196.             foreach (BasePlayer player in BasePlayer.activePlayerList)
  197.                 OnPlayerInit(player);
  198.  
  199.             timer.Every(conf.Options.saveTimer * 60, () =>
  200.             {
  201.                 SaveData(true);
  202.                 Puts("Player Ranks Local Database Was Saved.");
  203.             }
  204.         );
  205.             UpdateCategories();
  206.             SetUpBroadcast();
  207.         }
  208.  
  209.         void SetUpBroadcast()    
  210.         {
  211.             if (conf.Options.useTimedTopList)  
  212.             {
  213.                 if (broadcastTimer != null)
  214.                     broadcastTimer.Destroy();
  215.                 Broadcast.Clear();
  216.                 foreach (var cat in allowedCats)
  217.                 {
  218.                     if (allowedCats[cat.Key] == true)
  219.                     Broadcast.Add(cat.Key);
  220.                 }
  221.                 if (Broadcast.Count != 0)
  222.                     BroadcastLooper(0);
  223.             }
  224.         }
  225.        
  226.         void BroadcastLooper(int counter)
  227.         {
  228.             var time = 10f;
  229.             if (BroadcastMethod(Broadcast[counter]))
  230.                 time = conf.Options.TimedTopListTimer * 60;
  231.  
  232.             counter++;
  233.             if (counter == Broadcast.Count)
  234.                 counter = 0;
  235.             broadcastTimer = timer.Once(time, () => BroadcastLooper(counter));
  236.         }
  237.        
  238.         void UpdateCategories()
  239.         {
  240.             allowedCats.Clear();
  241.             allowedCats.Add("PVPKills", conf.Categories.usepvpkills);
  242.             allowedCats.Add("PVPDistance", conf.Categories.usepvpdistance);
  243.             allowedCats.Add("PVEKills", conf.Categories.usepvekills);
  244.             allowedCats.Add("PVEDistance", conf.Categories.usepvedistance);
  245.             allowedCats.Add("NPCKills", conf.Categories.usenpckills);
  246.             allowedCats.Add("NPCDistance", conf.Categories.usenpcdistance);
  247.             allowedCats.Add("SleepersKilled", conf.Categories.usesleeperskilled);
  248.             allowedCats.Add("HeadShots", conf.Categories.useheadshots);
  249.             allowedCats.Add("Deaths", conf.Categories.usedeaths);
  250.             allowedCats.Add("Suicides", conf.Categories.usesuicides);
  251.             allowedCats.Add("KDR", conf.Categories.usekdr);
  252.             allowedCats.Add("SDR", conf.Categories.usesdr);
  253.             allowedCats.Add("SkullsCrushed", conf.Categories.useskullscrushed);
  254.             allowedCats.Add("TimesWounded", conf.Categories.usetimeswounded);
  255.             allowedCats.Add("TimesHealed", conf.Categories.usetimeshealed);
  256.             allowedCats.Add("HeliHits", conf.Categories.usehelihits);
  257.             allowedCats.Add("HeliKills", conf.Categories.usehelikills);
  258.             allowedCats.Add("APCHits", conf.Categories.useapchits);
  259.             allowedCats.Add("APCKills", conf.Categories.useapckills);
  260.             allowedCats.Add("BarrelsDestroyed", conf.Categories.usebarrelsdestroyed);
  261.             allowedCats.Add("ExplosivesThrown", conf.Categories.useexplosivesthrown);
  262.             allowedCats.Add("ArrowsFired", conf.Categories.usearrowsfired);
  263.             allowedCats.Add("BulletsFired", conf.Categories.usebulletsfired);
  264.             allowedCats.Add("RocketsLaunched", conf.Categories.userocketslaunched);
  265.             allowedCats.Add("WeaponTrapsDestroyed", conf.Categories.useweapontrapsdestroyed);
  266.             allowedCats.Add("DropsLooted", conf.Categories.usedropslooted);
  267.        
  268.             if (conf.Options.useIntenseOptions)
  269.             {
  270.                 allowedCats.Add("StructuresBuilt", conf.Categories.usestructuresbuilt);
  271.                 allowedCats.Add("StructuresDemolished", conf.Categories.usestructuresdemolished);
  272.                 allowedCats.Add("ItemsDeployed", conf.Categories.useitemsdeployed);
  273.                 allowedCats.Add("ItemsCrafted", conf.Categories.useitemscrafted);
  274.                 allowedCats.Add("EntitiesRepaired", conf.Categories.useentitiesrepaired);
  275.                 allowedCats.Add("ResourcesGathered", conf.Categories.useresourcesgathered);
  276.                 allowedCats.Add("StructuresUpgraded", conf.Categories.usestructuresupgraded);
  277.             }
  278.         }
  279.                
  280.         void CheckDependencies()
  281.         {
  282.             //warn if enabled and missing
  283.             if (Friends == null)
  284.             if (conf.Options.useFriendsAPI && conf.Options.useFriendsAPI)
  285.                 PrintWarning("{0}: {1}", Title, "FriendsAPI is not installed and will not be used.");
  286.             if (Clans == null && conf.Options.useClans)
  287.                 PrintWarning("{0}: {1}", Title, "Clans is not installed and will not be used.");
  288.             lib = Interface.GetMod().GetLibrary<Library>("RustIO");
  289.             if (lib == null || (isInstalled = lib.GetFunction("IsInstalled")) == null || (hasFriend = lib.GetFunction("HasFriend")) == null)
  290.             {
  291.                 lib = null;
  292.                 if (conf.Options.useRustIO)
  293.                 PrintWarning("{0}: {1}", Title, "Rust:IO is not installed and will not be used.");
  294.             }
  295.             //just warn if missing
  296.             if (PlaytimeTracker == null)
  297.                 PrintWarning("{0}: {1}", Title, "PlayTime Tracker is not installed and will not be used.");
  298.             if (Economics == null)
  299.                 PrintWarning("{0}: {1}", Title, "Economics is not installed and will not be used.");  
  300.         }
  301.  
  302.         void OnPlayerInit(BasePlayer player)
  303.         {
  304.             if (ServerUsers.Is(player.userID, ServerUsers.UserGroup.Banned))
  305.                 if (data.PlayerRankData.ContainsKey(player.userID))
  306.                 {
  307.                     data.PlayerRankData.Remove(player.userID);
  308.                     SaveData(true);
  309.                     return;
  310.                 }
  311.                            
  312.             if (MenuOpen.Contains(player.userID))
  313.             {
  314.                 MenuOpen.Remove(player.userID);
  315.                 CuiHelper.DestroyUi(player, "ranksgui");
  316.             }
  317.  
  318.             if (!data.PlayerRankData.ContainsKey(player.userID))
  319.             {
  320.                 data.PlayerRankData.Add(player.userID, new PRDATA()
  321.                 {
  322.                     Admin = IsAuth(player),
  323.                     UserID = player.userID,
  324.                     Name = player.displayName,
  325.                     TimePlayed = "0",
  326.                     Status = "online",
  327.                     ActiveDate = DateTime.UtcNow,
  328.                     Economics = 0,
  329.                     PVPKills = 0,
  330.                     PVPDistance = 0.0,
  331.                     PVEKills = 0,
  332.                     PVEDistance = 0.0,
  333.                     NPCKills = 0,
  334.                     NPCDistance = 0.0,
  335.                     SleepersKilled = 0,
  336.                     HeadShots = 0,
  337.                     Deaths = 0,
  338.                     Suicides = 0,
  339.                     KDR = 0,
  340.                     SDR = 0,
  341.                     SkullsCrushed = 0,
  342.                     TimesHealed = 0,
  343.                     TimesWounded = 0,
  344.                     HeliHits = 0,
  345.                     HeliKills = 0,
  346.                     APCHits = 0,
  347.                     APCKills = 0,
  348.                     BarrelsDestroyed = 0,
  349.                     ExplosivesThrown = 0,
  350.                     ArrowsFired = 0,
  351.                     BulletsFired = 0,
  352.                     RocketsLaunched = 0,
  353.                     WeaponTrapsDestroyed = 0,
  354.                     DropsLooted = 0,
  355.  
  356.                     //intense options
  357.                     StructuresBuilt = 0,
  358.                     StructuresDemolished = 0,
  359.                     ItemsDeployed = 0,
  360.                     ItemsCrafted = 0,
  361.                     EntitiesRepaired = 0,
  362.                     ResourcesGathered = 0,
  363.                     StructuresUpgraded = 0,
  364.                 });
  365.             }
  366.             else
  367.                 UpdatePlayer(player);
  368.         }
  369.  
  370.         void UpdatePlayer(BasePlayer player)
  371.         {
  372.             var path = data.PlayerRankData[player.userID];
  373.             var time = PlaytimeTracker?.Call("GetPlayTime", player.UserIDString);
  374.             var playTime = (time != null) ? GetPlaytimeClock((double)time) : null;
  375.                    
  376.             path.Admin = IsAuth(player);
  377.             path.Name = player.displayName;
  378.             path.Status = "online";
  379.             path.ActiveDate = DateTime.UtcNow;
  380.             if (Economics)
  381.                 path.Economics = Convert.ToInt32(Economics?.Call("Balance", player.userID));
  382.             path.TimePlayed = (!string.IsNullOrEmpty(playTime)) ? playTime : string.Empty;
  383.         }
  384.  
  385.         void OnPlayerBanned(string name, ulong id, string address, string reason)=>data.PlayerRankData.Remove(id);
  386.        
  387.         private string GetPlaytimeClock(double time)
  388.         {
  389.             TimeSpan dateDifference = TimeSpan.FromSeconds((float)time);
  390.             var days = dateDifference.Days;
  391.             var hours = dateDifference.Hours;
  392.             hours += (days * 24);
  393.             var mins = dateDifference.Minutes;
  394.             var secs = dateDifference.Seconds;
  395.             return string.Format("{0:00}:{1:00}:{2:00}", hours, mins, secs);
  396.         }
  397.  
  398.         void OnEntityTakeDamage(BaseEntity entity, HitInfo hitinfo)
  399.         {
  400.             if (hitinfo.Initiator == null || !(hitinfo.Initiator is BasePlayer)) return;
  401.             var player = hitinfo.Initiator as BasePlayer;
  402.             DamageType type = hitinfo.damageTypes.GetMajorityDamageType();
  403.             float amount = hitinfo.damageTypes.Total();
  404.  
  405.             if (conf.Options.blockEvents)
  406.             if (CheckEvents(player))return;
  407.            
  408.             if (entity is BaseHelicopter)
  409.             {
  410.                 if (!HeliAttackers.ContainsKey(entity.net.ID))
  411.                     HeliAttackers.Add(entity.net.ID, new Dictionary<ulong, int>());
  412.                 if (!HeliAttackers[entity.net.ID].ContainsKey(player.userID))
  413.                     HeliAttackers[entity.net.ID].Add(player.userID, 1);
  414.                 else
  415.                 {
  416.                     HeliAttackers[entity.net.ID][player.userID]++;
  417.                     ProcessHeliHits(player);
  418.                 }
  419.             }
  420.             if (entity is BradleyAPC)
  421.             {
  422.                 if (type.ToString() == "Bullet")
  423.                 {
  424.                     ProcessAPCHits(player);//plain bullets count as hits, but not towards majority damage
  425.                     return;
  426.                 }
  427.                 if (!BradleyAttackers.ContainsKey(entity.net.ID)) //explosive ammo does get this far, because two damage types are processed.
  428.                     BradleyAttackers.Add(entity.net.ID, new Dictionary<ulong, float>());
  429.                 if (!BradleyAttackers[entity.net.ID].ContainsKey(player.userID))
  430.                     BradleyAttackers[entity.net.ID].Add(player.userID, amount);
  431.                 else
  432.                 {
  433.                     BradleyAttackers[entity.net.ID][player.userID] = BradleyAttackers[entity.net.ID][player.userID] + amount;
  434.                     ProcessAPCHits(player);
  435.                 }
  436.             }
  437.             if (entity is BasePlayer)
  438.                 if (hitinfo.isHeadshot && !FriendCheck(player, entity as BasePlayer))
  439.                     ProcessHeadShot(player);
  440.         }  
  441.    
  442.         ulong GetMajorityAttacker(uint id)
  443.         {
  444.             ulong majorityPlayer = 0U;
  445.             if (HeliAttackers.ContainsKey(id))
  446.             {
  447.                 Dictionary<ulong, int> majority = HeliAttackers[id].OrderByDescending(pair => pair.Value).Take(1).ToDictionary(pair => pair.Key, pair => pair.Value);
  448.                 foreach (var name in majority)
  449.                 {
  450.                     majorityPlayer = name.Key;
  451.                 }
  452.             }
  453.             if (BradleyAttackers.ContainsKey(id))
  454.             {
  455.                 Dictionary<ulong, float> majority = BradleyAttackers[id].OrderByDescending(pair => pair.Value).Take(1).ToDictionary(pair => pair.Key, pair => pair.Value);
  456.                 foreach (var name in majority)
  457.                 {
  458.                     majorityPlayer = name.Key;
  459.                 }
  460.             }
  461.             return majorityPlayer;
  462.         }
  463.        
  464.         void OnEntityDeath(BaseEntity entity, HitInfo hitinfo)
  465.         {
  466.             if (entity.name.Contains("corpse"))
  467.                 return;
  468.            
  469.             var victim = entity as BasePlayer;
  470.  
  471.             if (hitinfo?.Initiator == null && entity is BasePlayer)
  472.             {
  473.                 if (woundedData.ContainsKey(victim.userID))
  474.                 {
  475.                     BasePlayer attacker = BasePlayer.FindByID(woundedData[victim.userID].attackerId);
  476.                     if (conf.Options.blockEvents)
  477.                     if (CheckEvents(attacker))return;
  478.                     var distance = woundedData[victim.userID].distance;
  479.                     if (!victim.userID.IsSteamId() || victim is NPCPlayer || victim is HTNPlayer)
  480.                     {
  481.                         if (attacker != null)
  482.                         {
  483.                             if (data.PlayerRankData.ContainsKey(attacker.userID))
  484.                                 data.PlayerRankData[attacker.userID].NPCKills++;
  485.                             if (distance > data.PlayerRankData[attacker.userID].NPCDistance)
  486.                                 data.PlayerRankData[attacker.userID].NPCDistance = Math.Round(distance, 2); //process method not called, because distance is from record
  487.                         }
  488.                         return;
  489.                     }
  490.        
  491.                     if (victim.userID.IsSteamId())
  492.                     {
  493.                         ProcessDeath(victim);
  494.                         if (attacker != null)
  495.                         {
  496.                             if (data.PlayerRankData.ContainsKey(attacker.userID))
  497.                                 data.PlayerRankData[attacker.userID].PVPKills++;
  498.                             if (distance > data.PlayerRankData[attacker.userID].PVPDistance)
  499.                                 data.PlayerRankData[attacker.userID].PVPDistance = Math.Round(distance, 2);
  500.                         }
  501.                         return;
  502.                     }
  503.                     woundedData.Remove(victim.userID);
  504.                 }
  505.                 String [] stringArray = {"Cold", "Drowned", "Heat", "Suicide", "Generic", "Posion", "Radiation", "Thirst", "Hunger", "Fall"};
  506.                 if (stringArray.Any(victim.lastDamage.ToString().Contains))
  507.                     ProcessSuicide(victim);
  508.                 ProcessDeath(victim);
  509.                 return;
  510.             }
  511.                                      
  512.             if (entity is BaseHelicopter)  
  513.             {
  514.                 BasePlayer player = null;
  515.                 player = BasePlayer.FindByID(GetMajorityAttacker(entity.net.ID));
  516.                 if (player != null)
  517.                 {                          
  518.                     ProcessHeliKills(player);
  519.                     HeliAttackers.Remove(entity.net.ID);
  520.                 }
  521.                 return;
  522.             }
  523.  
  524.             if (entity is BradleyAPC)  
  525.             {
  526.                 BasePlayer player;
  527.                 var BradleyID = entity.net.ID;
  528.                 player = BasePlayer.FindByID(GetMajorityAttacker(BradleyID));
  529.                 if (player != null)
  530.                 {
  531.                     ProcessAPCKills(player);
  532.                     BradleyAttackers.Remove(BradleyID);
  533.                 }
  534.                 return;
  535.             }
  536.  
  537.             if (hitinfo?.Initiator is BasePlayer)
  538.             {
  539.                 var player = hitinfo.Initiator as BasePlayer;
  540.                 if (player.userID.IsSteamId() && !(player is NPCPlayer) && !(player is HTNPlayer))
  541.                 {
  542.                     if (entity.name.Contains("agents/"))
  543.                         ProcessPVEKill(player, entity);
  544.                     else if (entity.name.Contains("barrel"))       
  545.                         ProcessBarrelsDestroyed(player);
  546.                     else if (!(entity is BasePlayer) && entity.name.Contains("turret"))                                                                                  
  547.                         ProcessWeaponTrapsDestroyed(player);
  548.                     else if (entity.name.Contains("guntrap"))                                                                                  
  549.                         ProcessWeaponTrapsDestroyed(player);
  550.                     else if (victim is BasePlayer && !victim.userID.IsSteamId())
  551.                         ProcessNPCKills(player, victim);
  552.                     else if (victim is BasePlayer && (victim is NPCPlayer || victim is HTNPlayer))
  553.                         ProcessNPCKills(player, victim);
  554.                     else if (victim is BasePlayer && victim.userID.IsSteamId())
  555.                     {
  556.                         ProcessDeath(victim);
  557.                         if (hitinfo.Initiator != entity)
  558.                             ProcessPVPKill(player, victim);
  559.                         if (victim.IsSleeping())
  560.                             ProcessSleepersKilled(player, victim);
  561.                         if (hitinfo.Initiator == entity)
  562.                             ProcessSuicide(player);
  563.                     }
  564.                     return;
  565.                 }
  566.             }
  567.             if (victim == null) return;
  568.  
  569.             if (victim is BasePlayer && !(victim is NPCPlayer) && !(victim is HTNPlayer))
  570.                 ProcessDeath(victim);
  571.                
  572.             if (woundedData.ContainsKey(victim.userID))
  573.             woundedData.Remove(victim.userID);
  574.             return;
  575.         }
  576.        
  577.         void OnExplosiveThrown(BasePlayer player, BaseEntity entity, Item item)
  578.         {
  579.             if (!(player.GetActiveItem().info.displayName.english == "Supply Signal"))
  580.                 ProcessExplosivesThrown(player);
  581.         }
  582.  
  583.         void OnWeaponFired(BaseProjectile projectile, BasePlayer player, ItemModProjectile mod)
  584.         {
  585.             if (mod.ToString().Contains("arrow"))
  586.                 ProcessArrowsFired(player);
  587.  
  588.             if (mod.ToString().Contains("ammo"))
  589.                 ProcessBulletsFired(player);
  590.         }
  591.        
  592.         void OnRocketLaunched(BasePlayer player)=>ProcessRocketsLaunched(player);
  593.        
  594.         void OnEntityBuilt(Planner plan, GameObject objectBlock)
  595.         {
  596.             if (conf.Options.useIntenseOptions)
  597.             {
  598.                 BasePlayer player = plan.GetOwnerPlayer();
  599.                 if (player.GetActiveItem().info.displayName.english == "Building Plan")
  600.                     ProcessStructuresBuilt(player);
  601.                 else
  602.                     ProcessItemsDeployed(player);
  603.             }
  604.         }
  605.              
  606.         void OnStructureDemolish(BaseCombatEntity entity, BasePlayer player)=>ProcessStructuresDemolished(player);
  607.        
  608.         void OnItemCraft(ItemCraftTask item)
  609.         {
  610.             if (conf.Options.useIntenseOptions)
  611.             {
  612.                 BasePlayer crafter = item.owner;
  613.                 if (crafter != null)
  614.                     ProcessItemsCrafted(crafter);
  615.             }
  616.         }
  617.  
  618.         void OnStructureRepair(BaseCombatEntity entity, BasePlayer player)
  619.         {
  620.             float before = 0,after = 0;
  621.             if (conf.Options.useIntenseOptions)
  622.             {
  623.                 if (entity.health != null)
  624.                 {
  625.                     before = entity.health;
  626.                     timer.Once(0.1f,()=>
  627.                     {
  628.                         after = entity.health;
  629.                         if (!Mathf.Approximately(before,after))
  630.                             ProcessEntitiesRepaired(player);
  631.                     });
  632.                 }
  633.             }
  634.         }
  635.        
  636.         void OnHealingItemUse(HeldEntity item, BasePlayer target)=>ProcessTimesHealed(target);
  637.  
  638.         void OnItemUse(Item item)
  639.         {
  640.             BasePlayer player = item?.GetOwnerPlayer();
  641.             if (item.GetOwnerPlayer() == null) return;
  642.            
  643.             if (player != null && item.info.displayName.english == "Large Medkit")
  644.                 ProcessTimesHealed(player);
  645.        
  646.             if (item.info.shortname != "skull.human") return;//credit redBDGR
  647.             string skullName = null;
  648.             if (item.name != null)
  649.                 skullName = item.name.Substring(10, item.name.Length - 11);
  650.             else return;
  651.            
  652.             if (!player.displayName.Contains($"{skullName}")) //.contains is for [God] - UserID would be better here
  653.                 ProcessSkullsCrushed(player);
  654.         }
  655.  
  656.         void CanBeWounded(BasePlayer player, HitInfo hitInfo)
  657.         {
  658.             if (player == null || hitInfo == null) return;
  659.             if (!(player.userID.IsSteamId()) || player is NPCPlayer || player is HTNPlayer) return;
  660.             var attacker = hitInfo.InitiatorPlayer;
  661.             if (attacker != null)
  662.             {
  663.                 if (attacker == player || IsFriend(attacker.userID, player.userID) || IsClanmate(attacker.userID, player.userID)) return;
  664.                 woundedData[player.userID] = new WoundedData {distance = Vector3.Distance(player.transform.position, attacker.transform.position), attackerId = attacker.userID };
  665.                 {
  666.                     NextTick(() =>
  667.                     {      
  668.                         if (player.IsWounded())
  669.                             ProcessTimesWounded(player);
  670.                     });
  671.                 }
  672.             }
  673.         }
  674.  
  675.         void OnPlayerRecover(BasePlayer player)
  676.         {
  677.             if (woundedData.ContainsKey(player.userID))
  678.                 woundedData.Remove(player.userID);
  679.         }
  680.  
  681.         void OnStructureUpgrade(BaseCombatEntity entity, BasePlayer player, BuildingGrade.Enum grade)=>ProcessStructuresUpgraded(player);
  682.        
  683.         void OnCollectiblePickup(Item item, BasePlayer player)
  684.         {
  685.             if (conf.Options.useIntenseOptions)
  686.                 ProcessResourcesGathered(player, item.amount);
  687.         }
  688.        
  689.         void OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item)
  690.         {
  691.             if (conf.Options.useIntenseOptions)
  692.             {
  693.                 BasePlayer player = entity?.ToPlayer();
  694.                 ProcessResourcesGathered(player, item.amount);
  695.             }
  696.         }
  697.        
  698.         void OnEntitySpawned(BaseEntity entity)
  699.         {
  700.             if (entity.name.Contains("supply_drop"))
  701.                 airdrops.Add(entity.net.ID);    
  702.         }
  703.  
  704.         void OnLootEntity(BasePlayer player, BaseEntity entity)
  705.         {
  706.             if (airdrops.Contains(entity.net.ID))
  707.             {
  708.                 airdrops.Remove(entity.net.ID);
  709.                 ProcessDropsLooted(player);
  710.             }
  711.         }
  712.        
  713.         #region processes    
  714.         bool ProcessChecks(BasePlayer player)
  715.         {
  716.             if(!conf.Options.statCollection) return false;
  717.             if (conf.Options.blockEvents)
  718.                 if (CheckEvents(player))return false;
  719.  
  720.             return data.PlayerRankData.ContainsKey(player.userID);
  721.         }
  722.  
  723.         bool FriendCheck(BasePlayer player, BasePlayer victim)
  724.         {
  725.             if (victim != null)
  726.             {
  727.                 if (conf.Options.useClans && IsClanmate(player.userID, victim.userID))
  728.                     return true;
  729.                 if (conf.Options.useFriendsAPI && IsFriend(player.userID, victim.userID))
  730.                     return true;
  731.                 if (conf.Options.useRustIO && HasFriend(player.userID.ToString(), victim.userID.ToString()))
  732.                     return true;
  733.             }
  734.             return false;
  735.         }
  736.         void ProcessPVPKill(BasePlayer player, BasePlayer victim)
  737.         {
  738.             if (FriendCheck(player, victim))
  739.                 return;
  740.            
  741.             if (ProcessChecks(player))
  742.             {
  743.                 data.PlayerRankData[player.userID].PVPKills++;
  744.                 if (victim.Distance(player.transform.position) > data.PlayerRankData[player.userID].PVPDistance)
  745.                     data.PlayerRankData[player.userID].PVPDistance = Math.Round(victim.Distance(player.transform.position), 2);
  746.  
  747.                 if ((data.PlayerRankData[player.userID].Deaths) > 0)
  748.                 {
  749.                     var KDR = Convert.ToDouble(data.PlayerRankData[player.userID].PVPKills) / (data.PlayerRankData[player.userID].Deaths);
  750.                     data.PlayerRankData[player.userID].KDR = Math.Round(KDR, 2);
  751.                 }
  752.                 else
  753.                     data.PlayerRankData[player.userID].KDR = (data.PlayerRankData[player.userID].PVPKills);
  754.             }
  755.         }
  756.        
  757.         void ProcessPVEKill(BasePlayer player, BaseEntity victim)
  758.         {
  759.             if (ProcessChecks(player))
  760.             {
  761.                 data.PlayerRankData[player.userID].PVEKills++;
  762.                 if (victim.Distance(player.transform.position) > data.PlayerRankData[player.userID].PVEDistance)
  763.                     data.PlayerRankData[player.userID].PVEDistance = Math.Round(victim.Distance(player.transform.position), 2);
  764.             }
  765.         }
  766.        
  767.         void ProcessNPCKills(BasePlayer player, BaseEntity victim)
  768.         {
  769.             if (ProcessChecks(player))
  770.             {
  771.                 data.PlayerRankData[player.userID].NPCKills++;
  772.                 if (victim.Distance(player.transform.position) > data.PlayerRankData[player.userID].NPCDistance)
  773.                     data.PlayerRankData[player.userID].NPCDistance = Math.Round(victim.Distance(player.transform.position), 2);
  774.             }
  775.         }
  776.        
  777.         void ProcessSleepersKilled(BasePlayer player, BaseEntity victim)
  778.         {
  779.             if (ProcessChecks(player))
  780.             {
  781.                 data.PlayerRankData[player.userID].SleepersKilled++;
  782.                 if (victim.Distance(player.transform.position) > data.PlayerRankData[player.userID].PVPDistance)
  783.                     data.PlayerRankData[player.userID].PVPDistance = Math.Round(victim.Distance(player.transform.position), 2);
  784.             }
  785.         }
  786.        
  787.         void ProcessHeadShot(BasePlayer player)
  788.         {
  789.             if (ProcessChecks(player))
  790.                 data.PlayerRankData[player.userID].HeadShots++;    
  791.         }
  792.        
  793.         void ProcessDeath(BasePlayer player)
  794.         {
  795.             if (ProcessChecks(player))
  796.             {
  797.                 data.PlayerRankData[player.userID].Deaths++;
  798.  
  799.                 var SDR = Convert.ToDouble(data.PlayerRankData[player.userID].Suicides) / (data.PlayerRankData[player.userID].Deaths);
  800.                 data.PlayerRankData[player.userID].SDR = Math.Round(SDR, 2);
  801.  
  802.                 var KDR = Convert.ToDouble(data.PlayerRankData[player.userID].PVPKills) / (data.PlayerRankData[player.userID].Deaths);
  803.                 data.PlayerRankData[player.userID].KDR = Math.Round(KDR, 2);
  804.             }
  805.         }
  806.        
  807.         void ProcessSuicide(BasePlayer player)
  808.         {
  809.             if (ProcessChecks(player))
  810.             {
  811.                 data.PlayerRankData[player.userID].Suicides++;
  812.  
  813.                 if ((data.PlayerRankData[player.userID].Deaths) > 0)
  814.                 {
  815.                     var SDR = Convert.ToDouble(data.PlayerRankData[player.userID].Suicides) / (data.PlayerRankData[player.userID].Deaths);
  816.                     data.PlayerRankData[player.userID].SDR = Math.Round(SDR, 2);
  817.        
  818.                     var KDR = Convert.ToDouble(data.PlayerRankData[player.userID].PVPKills) / (data.PlayerRankData[player.userID].Deaths);
  819.                     data.PlayerRankData[player.userID].KDR = Math.Round(KDR, 2);
  820.                 }
  821.                 else
  822.                 {
  823.                     data.PlayerRankData[player.userID].SDR = (data.PlayerRankData[player.userID].Suicides);
  824.                     data.PlayerRankData[player.userID].KDR = (data.PlayerRankData[player.userID].PVPKills);
  825.                 }
  826.             }
  827.         }
  828.        
  829.         void ProcessSkullsCrushed(BasePlayer player)
  830.         {
  831.             if (ProcessChecks(player))
  832.                 data.PlayerRankData[player.userID].SkullsCrushed++;    
  833.         }
  834.        
  835.         void ProcessTimesWounded(BasePlayer player)
  836.         {
  837.             if (ProcessChecks(player))
  838.                 data.PlayerRankData[player.userID].TimesWounded++;
  839.         }
  840.        
  841.         void ProcessTimesHealed(BasePlayer player)
  842.         {
  843.             if (ProcessChecks(player))
  844.                 data.PlayerRankData[player.userID].TimesHealed++;
  845.         }
  846.        
  847.         void ProcessHeliHits(BasePlayer player)
  848.         {
  849.             if (ProcessChecks(player))
  850.                 data.PlayerRankData[player.userID].HeliHits++;
  851.         }
  852.        
  853.         void ProcessHeliKills(BasePlayer player)
  854.         {
  855.             if (ProcessChecks(player))
  856.                 data.PlayerRankData[player.userID].HeliKills++;
  857.         }
  858.  
  859.         void ProcessAPCHits(BasePlayer player)
  860.         {
  861.             if (ProcessChecks(player))
  862.                 data.PlayerRankData[player.userID].APCHits++;
  863.         }
  864.        
  865.         void ProcessAPCKills(BasePlayer player)
  866.         {
  867.             if (ProcessChecks(player))
  868.                 data.PlayerRankData[player.userID].APCKills++;
  869.         }
  870.        
  871.         void ProcessBarrelsDestroyed(BasePlayer player)
  872.         {
  873.             if (ProcessChecks(player))
  874.                 data.PlayerRankData[player.userID].BarrelsDestroyed++;
  875.         }
  876.        
  877.         void ProcessExplosivesThrown(BasePlayer player)
  878.         {
  879.             if (ProcessChecks(player))
  880.                 data.PlayerRankData[player.userID].ExplosivesThrown++;
  881.         }
  882.        
  883.         void ProcessArrowsFired(BasePlayer player)
  884.         {
  885.             if (ProcessChecks(player))
  886.                 data.PlayerRankData[player.userID].ArrowsFired++;
  887.         }
  888.        
  889.         void ProcessBulletsFired(BasePlayer player)
  890.         {
  891.             if (ProcessChecks(player))
  892.                 data.PlayerRankData[player.userID].BulletsFired++;
  893.         }
  894.        
  895.         void ProcessRocketsLaunched(BasePlayer player)
  896.         {
  897.             if (ProcessChecks(player))
  898.                 data.PlayerRankData[player.userID].RocketsLaunched++;
  899.         }
  900.        
  901.         void ProcessWeaponTrapsDestroyed(BasePlayer player)
  902.         {
  903.             if (ProcessChecks(player))
  904.                 data.PlayerRankData[player.userID].WeaponTrapsDestroyed++;
  905.         }
  906.        
  907.         void ProcessDropsLooted(BasePlayer player)
  908.         {
  909.             if (ProcessChecks(player))
  910.                 data.PlayerRankData[player.userID].DropsLooted++;
  911.         }
  912.        
  913.         void ProcessStructuresBuilt(BasePlayer player)
  914.         {
  915.             if (ProcessChecks(player))
  916.                 data.PlayerRankData[player.userID].StructuresBuilt++;
  917.         }
  918.        
  919.         void ProcessStructuresDemolished(BasePlayer player)
  920.         {
  921.             if (ProcessChecks(player))
  922.                 data.PlayerRankData[player.userID].StructuresDemolished++;
  923.         }
  924.        
  925.         void ProcessItemsDeployed(BasePlayer player)
  926.         {
  927.             if (ProcessChecks(player))
  928.                 data.PlayerRankData[player.userID].ItemsDeployed++;
  929.         }
  930.        
  931.         void ProcessItemsCrafted(BasePlayer player)
  932.         {
  933.             if (ProcessChecks(player))
  934.                 data.PlayerRankData[player.userID].ItemsCrafted++;
  935.         }
  936.  
  937.         void ProcessEntitiesRepaired(BasePlayer player)
  938.         {
  939.             if (ProcessChecks(player))
  940.                 data.PlayerRankData[player.userID].EntitiesRepaired++;
  941.         }
  942.  
  943.         void ProcessResourcesGathered(BasePlayer player, int amount = 0)
  944.         {
  945.             if (ProcessChecks(player))
  946.                 data.PlayerRankData[player.userID].ResourcesGathered+=amount;
  947.         }
  948.    
  949.         void ProcessStructuresUpgraded(BasePlayer player)
  950.         {
  951.             if (ProcessChecks(player))
  952.                 data.PlayerRankData[player.userID].StructuresUpgraded++;
  953.         }
  954.  
  955.         void BroadcastToAll(string msg, string keyword) => PrintToChat(conf.GUI.fontColor1 + keyword + " </color>" + conf.GUI.fontColor2 + msg + "</color>");
  956.        
  957.         bool IsClanmate(ulong playerId, ulong friendId)
  958.         {
  959.         if (!Clans || !conf.Options.useClans) return false;
  960.             object playerTag = Clans?.Call("GetClanOf", playerId);
  961.             object friendTag = Clans?.Call("GetClanOf", friendId);
  962.             if (playerTag is string && friendTag is string)
  963.             if (playerTag == friendTag) return true;
  964.             return false;
  965.         }
  966.  
  967.         bool IsFriend(ulong playerID, ulong friendID)
  968.         {
  969.             if (!Friends || !conf.Options.useFriendsAPI) return false;
  970.             bool isFriend = (bool)Friends?.Call("IsFriend", playerID, friendID);
  971.             return isFriend;
  972.         }
  973.        
  974.         bool CheckEvents(BasePlayer player)
  975.         {
  976.             object isPlaying = EventManager?.Call("isPlaying", new object[] { player });
  977.             if (isPlaying is bool)
  978.             if ((bool)isPlaying)
  979.             return true;
  980.             return false;
  981.         }
  982.         #endregion
  983.         void PersonalAndCategoryUI(BasePlayer player, string personalStatsCat, string personalStatsVal, string pageTitle)
  984.         {
  985.             string guiString = String.Format("0.1 0.1 0.1 {0}", conf.GUI.guitransparency);
  986.             var elements = new CuiElementContainer();
  987.             var buttonColour = conf.GUI.buttonColour;
  988.             var mainName = elements.Add(new CuiPanel { Image = { Color = guiString }, RectTransform = { AnchorMin = "0.1 0.12", AnchorMax = "0.9 0.98" }, CursorEnabled = true, FadeOut = 0.1f }, "Overlay", "ranksgui");
  989.                 elements.Add(new CuiElement { Parent = "ranksgui", Components = { new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" } } });
  990.        
  991.                 elements.Add(new CuiButton { Button = { Command = $"CallPersonalStatsUI true", Color = buttonColour }, RectTransform = { AnchorMin = "0.03 0.95", AnchorMax = "0.22 0.98" }, Text = { Text = lang.GetMessage("mystats", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  992.                 elements.Add(new CuiButton { Button = { Command = $"CallLeaderBoardUI true", Color = buttonColour }, RectTransform = { AnchorMin = "0.27 0.95", AnchorMax = "0.47 0.98" }, Text = { Text = lang.GetMessage("leaderboard", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  993.                 elements.Add(new CuiButton { Button = { Command = $"CallCategoriesUI", Color = buttonColour }, RectTransform = { AnchorMin = "0.53 0.95", AnchorMax = "0.72 0.98" }, Text = { Text = lang.GetMessage("categories", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  994.                 if (HasPermission(player.UserIDString, permAllowed))
  995.                 elements.Add(new CuiButton { Button = { Command = $"CallAdminUI", Color = buttonColour }, RectTransform = { AnchorMin = "0.77 0.95", AnchorMax = "0.97 0.98" }, Text = { Text = lang.GetMessage("admin", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  996.  
  997.                 elements.Add(new CuiLabel { Text = { Text = pageTitle, FontSize = 20, Align = TextAnchor.MiddleCenter },  RectTransform = { AnchorMin = "0 0.88", AnchorMax = "1 0.92" } }, mainName);
  998.                
  999.                 elements.Add(new CuiLabel { Text = { Text = personalStatsCat, FontSize = 12, Align = TextAnchor.MiddleRight }, RectTransform = { AnchorMin = "0.1 0.10", AnchorMax = "0.48 0.88" } }, mainName);
  1000.                 elements.Add(new CuiLabel { Text = { Text = personalStatsVal, FontSize = 12, Align = TextAnchor.MiddleLeft }, RectTransform = { AnchorMin = "0.52 0.10", AnchorMax = "0.9 0.88" } }, mainName);
  1001.                
  1002.                 elements.Add(new CuiButton { Button = { Command = "Close", Color = buttonColour }, RectTransform = { AnchorMin = "0.4 0.02", AnchorMax = "0.6 0.082" }, Text = { Text = lang.GetMessage("close", this, player.UserIDString), FontSize = 20, Align = TextAnchor.MiddleCenter } }, mainName);
  1003.                
  1004.             CuiHelper.AddUi(player, elements);
  1005.         }
  1006.        
  1007.         void CategoriesUI(BasePlayer player, string pageTitle)
  1008.         {
  1009.             string guiString = String.Format("0.1 0.1 0.1 {0}", conf.GUI.guitransparency);
  1010.             var elements = new CuiElementContainer();
  1011.             var buttonColour = conf.GUI.buttonColour;
  1012.             var mainName = elements.Add(new CuiPanel { Image = { Color = guiString }, RectTransform = { AnchorMin = "0.1 0.12", AnchorMax = "0.9 0.98" }, CursorEnabled = true, FadeOut = 0.1f }, "Overlay", "ranksgui");
  1013.                 elements.Add(new CuiElement { Parent = "ranksgui", Components = { new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" } } });
  1014.            
  1015.                 elements.Add(new CuiButton { Button = { Command = $"CallPersonalStatsUI true", Color = buttonColour }, RectTransform = { AnchorMin = "0.03 0.95", AnchorMax = "0.22 0.98" }, Text = { Text = lang.GetMessage("mystats", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1016.                 elements.Add(new CuiButton { Button = { Command = $"CallLeaderBoardUI true", Color = buttonColour }, RectTransform = { AnchorMin = "0.27 0.95", AnchorMax = "0.47 0.98" }, Text = { Text = lang.GetMessage("leaderboard", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1017.                 elements.Add(new CuiButton { Button = { Command = null, Color = buttonColour }, RectTransform = { AnchorMin = "0.53 0.95", AnchorMax = "0.72 0.98" }, Text = { Text = lang.GetMessage("categories", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1018.                
  1019.                 if (HasPermission(player.UserIDString, permAllowed))
  1020.                 elements.Add(new CuiButton { Button = { Command = $"CallAdminUI false", Color = buttonColour }, RectTransform = { AnchorMin = "0.77 0.95", AnchorMax = "0.97 0.98" }, Text = { Text = lang.GetMessage("admin", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1021.                
  1022.                 var buttonTop = 0.853;
  1023.                 var buttonBottom = 0.873;
  1024.                 foreach (var cat in allowedCats)
  1025.                 {
  1026.                     if (cat.Value == true)
  1027.                     {
  1028.                         elements.Add(new CuiButton { Button = { Command = $"GetCategory {cat.Key}", Color = "0.7 0.32 0.17 0.0" }, RectTransform = { AnchorMin = $"0.3 {buttonTop}", AnchorMax = $"0.7 {buttonBottom}" }, Text = { Text = lang.GetMessage(cat.Key, this), FontSize = 12, Align = TextAnchor.MiddleCenter } }, mainName);
  1029.                        
  1030.                         buttonTop = buttonTop - 0.021;
  1031.                         buttonBottom = buttonBottom - 0.021;
  1032.                     }
  1033.                 }
  1034.                 elements.Add(new CuiLabel { Text = { Text = pageTitle, FontSize = 20, Align = TextAnchor.MiddleCenter },  RectTransform = { AnchorMin = "0 0.88", AnchorMax = "1 0.92" } }, mainName);
  1035.                
  1036.                 elements.Add(new CuiButton { Button = { Command = "Close", Color = buttonColour }, RectTransform = { AnchorMin = "0.4 0.02", AnchorMax = "0.6 0.082" }, Text = { Text = lang.GetMessage("close", this, player.UserIDString), FontSize = 20, Align = TextAnchor.MiddleCenter } }, mainName);
  1037.                
  1038.             CuiHelper.AddUi(player, elements);
  1039.         }
  1040.        
  1041.         void LeaderBoardUI(BasePlayer player, string leaderBoardCat, string leaderBoardName, string leaderBoardScore, string pageTitle)
  1042.         {
  1043.             string guiString = String.Format("0.1 0.1 0.1 {0}", conf.GUI.guitransparency);
  1044.             var elements = new CuiElementContainer();
  1045.             var buttonColour = conf.GUI.buttonColour;
  1046.             var mainName = elements.Add(new CuiPanel { Image = { Color = guiString }, RectTransform = { AnchorMin = "0.1 0.12", AnchorMax = "0.9 0.98" }, CursorEnabled = true, FadeOut = 0.1f }, "Overlay", "ranksgui");
  1047.                 elements.Add(new CuiElement { Parent = "ranksgui", Components = { new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" } } });
  1048.            
  1049.                 elements.Add(new CuiButton { Button = { Command = $"CallPersonalStatsUI true", Color = buttonColour }, RectTransform = { AnchorMin = "0.03 0.95", AnchorMax = "0.22 0.98" }, Text = { Text = lang.GetMessage("mystats", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1050.                 elements.Add(new CuiButton { Button = { Command = null, Color = buttonColour }, RectTransform = { AnchorMin = "0.27 0.95", AnchorMax = "0.47 0.98" }, Text = { Text = lang.GetMessage("leaderboard", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1051.                 elements.Add(new CuiButton { Button = { Command = $"CallCategoriesUI", Color = buttonColour }, RectTransform = { AnchorMin = "0.53 0.95", AnchorMax = "0.72 0.98" }, Text = { Text = lang.GetMessage("categories", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1052.                
  1053.                 if (HasPermission(player.UserIDString, permAllowed))
  1054.                 elements.Add(new CuiButton { Button = { Command = $"CallAdminUI false", Color = buttonColour }, RectTransform = { AnchorMin = "0.77 0.95", AnchorMax = "0.97 0.98" }, Text = { Text = lang.GetMessage("admin", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1055.                
  1056.                 elements.Add(new CuiLabel { Text = { Text = pageTitle, FontSize = 20, Align = TextAnchor.MiddleCenter },  RectTransform = { AnchorMin = "0 0.88", AnchorMax = "1 0.92" } }, mainName);
  1057.                
  1058.                 elements.Add(new CuiLabel { Text = { Text = leaderBoardCat, FontSize = 12, Align = TextAnchor.MiddleRight }, RectTransform = { AnchorMin = "0 0.10", AnchorMax = "0.38 0.88" } }, mainName);
  1059.                 elements.Add(new CuiLabel { Text = { Text = leaderBoardScore, FontSize = 12, Align = TextAnchor.MiddleCenter }, RectTransform = { AnchorMin = "0.4 0.10", AnchorMax = "0.6 0.88" } }, mainName);
  1060.                 elements.Add(new CuiLabel { Text = { Text = leaderBoardName, FontSize = 12, Align = TextAnchor.MiddleLeft }, RectTransform = { AnchorMin = "0.62 0.10", AnchorMax = "1 0.88" } }, mainName);
  1061.        
  1062.                 elements.Add(new CuiButton { Button = { Command = "Close", Color = buttonColour }, RectTransform = { AnchorMin = "0.4 0.02", AnchorMax = "0.6 0.082" }, Text = { Text = lang.GetMessage("close", this, player.UserIDString), FontSize = 20, Align = TextAnchor.MiddleCenter } }, mainName);
  1063.                
  1064.             CuiHelper.AddUi(player, elements);
  1065.         }
  1066.        
  1067.         void AdminUI(BasePlayer player, bool wipe)
  1068.         {
  1069.             string guiString = String.Format("0.1 0.1 0.1 {0}", conf.GUI.guitransparency);
  1070.             var elements = new CuiElementContainer();
  1071.             var buttonColour = conf.GUI.buttonColour;
  1072.             var mainName = elements.Add(new CuiPanel { Image = { Color = guiString }, RectTransform = { AnchorMin = "0.1 0.12", AnchorMax = "0.9 0.98" }, CursorEnabled = true, FadeOut = 0.1f }, "Overlay", "ranksgui");
  1073.                 elements.Add(new CuiElement { Parent = "ranksgui", Components = { new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" } } });
  1074.            
  1075.                 elements.Add(new CuiButton { Button = { Command = $"CallPersonalStatsUI true", Color = buttonColour }, RectTransform = { AnchorMin = "0.03 0.95", AnchorMax = "0.22 0.98" }, Text = { Text = lang.GetMessage("mystats", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1076.                 elements.Add(new CuiButton { Button = { Command = $"CallLeaderBoardUI true", Color = buttonColour }, RectTransform = { AnchorMin = "0.27 0.95", AnchorMax = "0.47 0.98" }, Text = { Text = lang.GetMessage("leaderboard", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1077.                 elements.Add(new CuiButton { Button = { Command = $"CallCategoriesUI", Color = buttonColour }, RectTransform = { AnchorMin = "0.53 0.95", AnchorMax = "0.72 0.98" }, Text = { Text = lang.GetMessage("categories", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1078.                 if (HasPermission(player.UserIDString, permAllowed))
  1079.                 elements.Add(new CuiButton { Button = { Command = null, Color = buttonColour }, RectTransform = { AnchorMin = "0.77 0.95", AnchorMax = "0.97 0.98" }, Text = { Text = lang.GetMessage("admin", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1080.  
  1081.                 var buttonTop = 0.853;
  1082.                 var buttonBottom = 0.873;
  1083.                
  1084.                 foreach (var cat in allowedCats)
  1085.                 {
  1086.                     elements.Add(new CuiLabel { Text = { Text = cat.Key, FontSize = 11, Align = TextAnchor.MiddleCenter },  RectTransform = { AnchorMin = $"0 {buttonTop}", AnchorMax = $"0.2 {buttonBottom}" } }, mainName);
  1087.                     if (cat.Value == true)
  1088.                     elements.Add(new CuiButton { Button = { Command = $"ToggleCategory {cat.Key}", Color = buttonColour }, RectTransform = { AnchorMin = $"0.22 {buttonTop}", AnchorMax = $"0.32 {buttonBottom}" }, Text = { Text = lang.GetMessage("on", this, player.UserIDString), FontSize = 10, Align = TextAnchor.MiddleCenter } }, mainName);
  1089.                     else
  1090.                     elements.Add(new CuiButton { Button = { Command = $"ToggleCategory {cat.Key}", Color = "0.7 0.32 0.17 0.5" }, RectTransform = { AnchorMin = $"0.22 {buttonTop}", AnchorMax = $"0.32 {buttonBottom}" }, Text = { Text = lang.GetMessage("off", this, player.UserIDString), FontSize = 10, Align = TextAnchor.MiddleCenter } }, mainName);
  1091.                    
  1092.                     buttonTop = buttonTop - 0.021;
  1093.                     buttonBottom = buttonBottom - 0.021;
  1094.                 }
  1095.                 buttonTop = buttonTop - 0.031;
  1096.                 buttonBottom = buttonBottom - 0.031;
  1097.  
  1098.                 buttonTop = buttonTop - 0.031;
  1099.                 buttonBottom = buttonBottom - 0.031;                      
  1100.                     elements.Add(new CuiLabel { Text = { Text = "Intense Options", FontSize = 11, Align = TextAnchor.MiddleCenter },  RectTransform = { AnchorMin = $"0 {buttonTop}", AnchorMax = $"0.2 {buttonBottom}" } }, mainName);
  1101.                     if (conf.Options.useIntenseOptions == true)
  1102.                     elements.Add(new CuiButton { Button = { Command = "ToggleIntenseOptions", Color = buttonColour }, RectTransform = { AnchorMin = $"0.22 {buttonTop}", AnchorMax = $"0.32 {buttonBottom}" }, Text = { Text = lang.GetMessage("on", this, player.UserIDString), FontSize = 10, Align = TextAnchor.MiddleCenter } }, mainName);
  1103.                     else
  1104.                     elements.Add(new CuiButton { Button = { Command = "ToggleIntenseOptions", Color = buttonColour }, RectTransform = { AnchorMin = $"0.22 {buttonTop}", AnchorMax = $"0.32 {buttonBottom}" }, Text = { Text = lang.GetMessage("off", this, player.UserIDString), FontSize = 10, Align = TextAnchor.MiddleCenter } }, mainName);
  1105.                    
  1106.                    
  1107.                 if (conf.Options.statCollection)
  1108.                     elements.Add(new CuiButton { Button = { Command = "toggleStatCollection", Color = buttonColour }, RectTransform = { AnchorMin = "0.7 0.8", AnchorMax = "0.9 0.83" }, Text = { Text = lang.GetMessage("gatherStatsOnButton", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1109.                 else
  1110.                     elements.Add(new CuiButton { Button = { Command = "toggleStatCollection", Color = buttonColour }, RectTransform = { AnchorMin = "0.7 0.8", AnchorMax = "0.9 0.83" }, Text = { Text = lang.GetMessage("gatherStatsOffButton", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1111.                
  1112.                 if (conf.Options.allowadmin)
  1113.                     elements.Add(new CuiButton { Button = { Command = "AllowAdmin", Color = buttonColour }, RectTransform = { AnchorMin = "0.7 0.75", AnchorMax = "0.9 0.78" }, Text = { Text = lang.GetMessage("disableAdminStatsButton", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1114.                 else
  1115.                     elements.Add(new CuiButton { Button = { Command = "AllowAdmin", Color = buttonColour }, RectTransform = { AnchorMin = "0.7 0.75", AnchorMax = "0.9 0.78" }, Text = { Text = lang.GetMessage("AllowAdminStatsButton", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);      
  1116.                                
  1117.                 elements.Add(new CuiButton { Button = { Command = "playerranks.save", Color = buttonColour }, RectTransform = { AnchorMin = "0.7 0.7", AnchorMax = "0.9 0.73" }, Text = { Text = lang.GetMessage("savePlayerDataButton", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1118.          
  1119.                 if (wipe == false)
  1120.                     elements.Add(new CuiButton { Button = { Command = "wipeFirst", Color = buttonColour }, RectTransform = { AnchorMin = "0.7 0.65", AnchorMax = "0.9 0.68" }, Text = { Text = lang.GetMessage("wipePlayerDataButton", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1121.                 else
  1122.                     elements.Add(new CuiButton { Button = { Command = "playerranks.wipe", Color = buttonColour }, RectTransform = { AnchorMin = "0.7 0.65", AnchorMax = "0.9 0.68" }, Text = { Text = lang.GetMessage("confirm", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1123.                
  1124.                 elements.Add(new CuiButton { Button = { Command = "saveLeaderboard", Color = buttonColour }, RectTransform = { AnchorMin = "0.7 0.6", AnchorMax = "0.9 0.63" }, Text = { Text = lang.GetMessage("saveLeaderBoardButton", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1125.                
  1126.                 elements.Add(new CuiButton { Button = { Command = "wipeLeaderBoards", Color = buttonColour }, RectTransform = { AnchorMin = "0.7 0.55", AnchorMax = "0.9 0.58" }, Text = { Text = "Wipe LeaderBoards", FontSize = 16, Align = TextAnchor.MiddleCenter } }, mainName);
  1127.  
  1128.                 elements.Add(new CuiButton { Button = { Command = "Close", Color = buttonColour }, RectTransform = { AnchorMin = "0.4 0.02", AnchorMax = "0.6 0.082" }, Text = { Text = lang.GetMessage("close", this, player.UserIDString), FontSize = 20, Align = TextAnchor.MiddleCenter } }, mainName);
  1129.                                
  1130.             CuiHelper.AddUi(player, elements);
  1131.         }
  1132.         #region UI methods
  1133.  
  1134.         [ConsoleCommand("CallPersonalStatsUI")]
  1135.         void PSUI(ConsoleSystem.Arg arg)
  1136.         {
  1137.             var player = arg.Connection.player as BasePlayer;
  1138.             CallPersonalStatsUI(player, arg.Args[0]);
  1139.         }
  1140.         void CallPersonalStatsUI(BasePlayer player, string button)
  1141.         {
  1142.             if (player == null) return;
  1143.             var d = data.PlayerRankData[player.userID];
  1144.             var dictToUse = data.PlayerRankData;
  1145.            
  1146.             string pageTitle = conf.GUI.fontColor1 + lang.GetMessage("title", this, player.UserIDString) + "</color>" + conf.GUI.fontColor2 + d.Name + "</color> \n";
  1147.             string playerTopStatsCat = "";
  1148.             string playerTopStatsVal = "";
  1149.            
  1150.             foreach (var cat in allowedCats)
  1151.             {
  1152.                 if (allowedCats[cat.Key] == true)
  1153.                 {  
  1154.                     playerTopStatsCat += conf.GUI.fontColor3 + lang.GetMessage($"{cat.Key}", this)+ "</color> \n";
  1155.                     if (cat.Key.Contains("Distance"))
  1156.             playerTopStatsVal += conf.GUI.fontColor1 + d.GetValue(cat.Key) + "m</color> \n";//append M to distances
  1157.             else
  1158.             playerTopStatsVal += conf.GUI.fontColor1 + d.GetValue(cat.Key) + "</color> \n";
  1159.                 }
  1160.             }
  1161.  
  1162.             if (MenuOpen.Contains(player.userID))
  1163.             {
  1164.                 CuiHelper.DestroyUi(player, "ranksgui");
  1165.                 MenuOpen.Remove(player.userID);
  1166.                 if (button != "true") return;
  1167.             }
  1168.             PersonalAndCategoryUI(player, playerTopStatsCat, playerTopStatsVal, pageTitle);
  1169.             MenuOpen.Add(player.userID);
  1170.             return;
  1171.         }
  1172.        
  1173.         [ConsoleCommand("CallCategoriesUI")]
  1174.         void CatUI(ConsoleSystem.Arg arg)
  1175.         {
  1176.             var player = arg.Connection.player as BasePlayer;
  1177.             CallCategoriesUI(player);
  1178.            
  1179.         }
  1180.         void CallCategoriesUI(BasePlayer player)
  1181.         {
  1182.             string pageTitle = conf.GUI.fontColor1 + lang.GetMessage("title", this, player.UserIDString) + "</color>" + conf.GUI.fontColor2 + lang.GetMessage("categories", this, player.UserIDString) + "</color> \n";
  1183.            
  1184.             if (MenuOpen.Contains(player.userID))
  1185.             {
  1186.                 CuiHelper.DestroyUi(player, "ranksgui");
  1187.                 MenuOpen.Remove(player.userID);
  1188.             }
  1189.             CategoriesUI(player, pageTitle);
  1190.             MenuOpen.Add(player.userID);
  1191.         }
  1192.          
  1193.         [ConsoleCommand("GetCategory")]
  1194.         void GetCategory(ConsoleSystem.Arg arg)
  1195.         {
  1196.             var player = arg.Connection.player as BasePlayer;
  1197.             string pageTitle = conf.GUI.fontColor1 + lang.GetMessage("title", this, player.UserIDString) + "</color>" + conf.GUI.fontColor2 + lang.GetMessage($"{arg.Args[0]}", this, player.UserIDString) + "</color> \n";
  1198.             string catTopName = "";
  1199.             string catTopVal = "";
  1200.            
  1201.             foreach (var cat in allowedCats)
  1202.             {
  1203.                 if (allowedCats[cat.Key] == true)
  1204.                 if (cat.Key.ToLower() == arg.Args[0].ToLower())
  1205.                 {
  1206.                     var d = data.PlayerRankData[player.userID];
  1207.                     var dictToUse = data.PlayerRankData;
  1208.                     if (conf.Options.allowadmin == false)
  1209.                     {
  1210.                         dictToUse = data.PlayerRankData.Where(pair => pair.Value.Admin == false).ToDictionary(val => val.Key, val => val.Value);
  1211.                     }
  1212.                     Dictionary<ulong, PRDATA> top = dictToUse.OrderByDescending(pair => pair.Value.GetValue(cat.Key)).Take(30).ToDictionary(pair => pair.Key, pair => pair.Value);
  1213.  
  1214.                     var addPlayerManually = true;
  1215.                     foreach (var name in top)
  1216.                     {
  1217.                         catTopName += conf.GUI.fontColor3 + name.Value.Name + "</color> \n";
  1218.                         if (cat.Key.Contains("Distance"))
  1219.                             catTopVal += conf.GUI.fontColor1 + name.Value.GetValue(cat.Key) + "m</color> \n";//append M to distances
  1220.                         else
  1221.                             catTopVal += conf.GUI.fontColor1 + name.Value.GetValue(cat.Key) + "</color> \n";
  1222.  
  1223.                         if (player.userID == name.Key) addPlayerManually = false;
  1224.                     }
  1225.                     if (addPlayerManually && dictToUse.ContainsKey(player.userID)) //admin double check
  1226.                     {
  1227.                         catTopName += "\n" + conf.GUI.fontColor3 + d.Name + "</color>";
  1228.                         if (cat.Key.Contains("Distance"))
  1229.                             catTopVal += "\n" + conf.GUI.fontColor1 + d.GetValue(cat.Key) + "m</color>";//append M to distances
  1230.                         else
  1231.                             catTopVal += "\n" + conf.GUI.fontColor1 + d.GetValue(cat.Key) + "</color>";
  1232.                     }
  1233.                     break;
  1234.                 }
  1235.             }
  1236.             if (MenuOpen.Contains(player.userID))
  1237.             {
  1238.                 CuiHelper.DestroyUi(player, "ranksgui");
  1239.                 MenuOpen.Remove(player.userID);
  1240.             }
  1241.             PersonalAndCategoryUI(player, catTopName, catTopVal, pageTitle);
  1242.             MenuOpen.Add(player.userID);
  1243.             return;
  1244.         }
  1245.  
  1246.         [ConsoleCommand("CallLeaderBoardUI")]
  1247.         void LBUI(ConsoleSystem.Arg arg)
  1248.         {
  1249.             var player = arg.Connection.player as BasePlayer;
  1250.             CallLeaderBoardUI(player, arg.Args[0]);
  1251.         }
  1252.  
  1253.         void CallLeaderBoardUI(BasePlayer player, string button)
  1254.         {
  1255.             if (player == null) return;
  1256.             var d = data.PlayerRankData[player.userID];
  1257.             var dictToUse = data.PlayerRankData;
  1258.            
  1259.             string pageTitle = conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + conf.GUI.fontColor2 + lang.GetMessage("leaderboard", this) +"</color> \n";
  1260.             string leaderBoardCat = "";
  1261.             string leaderBoardName = "";
  1262.             string leaderBoardScore = "";
  1263.        
  1264.             foreach (var cat in allowedCats)
  1265.             {
  1266.                 if (allowedCats[cat.Key] == true)
  1267.                 {
  1268.                     if (conf.Options.allowadmin == false)
  1269.                         dictToUse = data.PlayerRankData.Where(pair => pair.Value.Admin == false).ToDictionary(val => val.Key, val => val.Value);
  1270.            
  1271.                     Dictionary<string, object> top = dictToUse.OrderByDescending(pair => pair.Value.GetValue(cat.Key)).Take(1).ToDictionary(pair => pair.Value.Name, pair => pair.Value.GetValue(cat.Key));
  1272.  
  1273.                     foreach (var item in top)
  1274.                     {
  1275.                         leaderBoardCat += conf.GUI.fontColor3 + lang.GetMessage($"{cat.Key}", this) + "</color>\n";
  1276.                         if (cat.Key.Contains("Distance"))
  1277.                             leaderBoardScore += conf.GUI.fontColor1 + item.Value + "m</color>\n";//append M to distances
  1278.                         else
  1279.                             leaderBoardScore += conf.GUI.fontColor1 + item.Value + "</color>\n";
  1280.                         var nameString = item.Key;
  1281.                         if (nameString.Length > 32)
  1282.                         nameString = string.Format(item.Key.Substring(0,30) + "...");
  1283.                         leaderBoardName += conf.GUI.fontColor3 + $"{nameString}" + "</color>\n";
  1284.                         break;
  1285.                     }
  1286.                 }
  1287.             }
  1288.  
  1289.             if (MenuOpen.Contains(player.userID))
  1290.             {
  1291.                 CuiHelper.DestroyUi(player, "ranksgui");
  1292.                 MenuOpen.Remove(player.userID);
  1293.                 if (button != "true") return;
  1294.             }
  1295.  
  1296.             LeaderBoardUI(player, leaderBoardCat, leaderBoardName, leaderBoardScore, pageTitle);
  1297.             MenuOpen.Add(player.userID);
  1298.             return;
  1299.         }
  1300.        
  1301.         [ConsoleCommand("CallAdminUI")]
  1302.         void ADUI(ConsoleSystem.Arg arg)
  1303.         {
  1304.             var player = arg.Connection.player as BasePlayer;
  1305.             CallAdminUI(player, false);
  1306.         }
  1307.         void CallAdminUI(BasePlayer player, bool wipe)
  1308.         {
  1309.             if (player == null) return;
  1310.            
  1311.             string pageTitle = conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + conf.GUI.fontColor2 + lang.GetMessage("leaderBoard", this) +"</color> \n";
  1312.  
  1313.  
  1314.             if (MenuOpen.Contains(player.userID))
  1315.             {
  1316.                 CuiHelper.DestroyUi(player, "ranksgui");
  1317.                 MenuOpen.Remove(player.userID);
  1318.             }
  1319.  
  1320.             AdminUI(player, wipe);
  1321.             MenuOpen.Add(player.userID);
  1322.             return;
  1323.         }
  1324.         #endregion
  1325.        
  1326.         #region console commands
  1327.  
  1328.         [ConsoleCommand("ToggleCategory")]
  1329.         void ToggleCat(ConsoleSystem.Arg arg)
  1330.         {
  1331.             var player = arg.Connection.player as BasePlayer;
  1332.             if (arg.Args[0] == "PVPKills") conf.Categories.usepvpkills = !conf.Categories.usepvpkills;
  1333.             if (arg.Args[0] == "PVPDistance") conf.Categories.usepvpdistance = !conf.Categories.usepvpdistance;
  1334.             if (arg.Args[0] == "PVEKills") conf.Categories.usepvekills = !conf.Categories.usepvekills;
  1335.             if (arg.Args[0] == "PVEDistance") conf.Categories.usepvedistance = !conf.Categories.usepvedistance;
  1336.             if (arg.Args[0] == "NPCKills") conf.Categories.usenpckills = !conf.Categories.usenpckills;
  1337.             if (arg.Args[0] == "NPCDistance") conf.Categories.usenpcdistance = !conf.Categories.usenpcdistance;
  1338.             if (arg.Args[0] == "SleepersKilled") conf.Categories.usesleeperskilled = !conf.Categories.usesleeperskilled;
  1339.             if (arg.Args[0] == "HeadShots") conf.Categories.useheadshots = !conf.Categories.useheadshots;
  1340.             if (arg.Args[0] == "Deaths") conf.Categories.usedeaths = !conf.Categories.usedeaths;
  1341.             if (arg.Args[0] == "Suicides") conf.Categories.usesuicides = !conf.Categories.usesuicides;
  1342.             if (arg.Args[0] == "KDR") conf.Categories.usekdr = !conf.Categories.usekdr;
  1343.             if (arg.Args[0] == "SDR") conf.Categories.usesdr = !conf.Categories.usesdr;
  1344.             if (arg.Args[0] == "SkullsCrushed") conf.Categories.useskullscrushed = !conf.Categories.useskullscrushed;
  1345.             if (arg.Args[0] == "TimesWounded") conf.Categories.usetimeswounded = !conf.Categories.usetimeswounded;
  1346.             if (arg.Args[0] == "TimesHealed") conf.Categories.usetimeshealed = !conf.Categories.usetimeshealed;
  1347.             if (arg.Args[0] == "HeliHits") conf.Categories.usehelihits = !conf.Categories.usehelihits;
  1348.             if (arg.Args[0] == "HeliKills") conf.Categories.usehelikills = !conf.Categories.usehelikills;
  1349.             if (arg.Args[0] == "APCHits") conf.Categories.useapchits = !conf.Categories.useapchits;
  1350.             if (arg.Args[0] == "APCKills") conf.Categories.useapckills = !conf.Categories.useapckills;
  1351.             if (arg.Args[0] == "BarrelsDestroyed") conf.Categories.usebarrelsdestroyed = !conf.Categories.usebarrelsdestroyed;
  1352.             if (arg.Args[0] == "ExplosivesThrown") conf.Categories.useexplosivesthrown = !conf.Categories.useexplosivesthrown;
  1353.             if (arg.Args[0] == "ArrowsFired") conf.Categories.usearrowsfired = !conf.Categories.usearrowsfired;
  1354.             if (arg.Args[0] == "BulletsFired") conf.Categories.usebulletsfired = !conf.Categories.usebulletsfired;
  1355.             if (arg.Args[0] == "RocketsLaunched") conf.Categories.userocketslaunched = !conf.Categories.userocketslaunched;
  1356.             if (arg.Args[0] == "WeaponTrapsDestroyed")  conf.Categories.useweapontrapsdestroyed = !conf.Categories.useweapontrapsdestroyed;
  1357.             if (arg.Args[0] == "DropsLooted") conf.Categories.usedropslooted = !conf.Categories.usedropslooted;
  1358.        
  1359.             if (conf.Options.useIntenseOptions)
  1360.             {
  1361.                 if (arg.Args[0] == "StructuresBuilt") conf.Categories.usestructuresbuilt = !conf.Categories.usestructuresbuilt;
  1362.                 if (arg.Args[0] == "StructuresDemolished") conf.Categories.usestructuresdemolished = !conf.Categories.usestructuresdemolished;
  1363.                 if (arg.Args[0] == "ItemsDeployed") conf.Categories.useitemsdeployed = !conf.Categories.useitemsdeployed;
  1364.                 if (arg.Args[0] == "ItemsCrafted") conf.Categories.useitemscrafted = !conf.Categories.useitemscrafted;
  1365.                 if (arg.Args[0] == "EntitiesRepaired") conf.Categories.useentitiesrepaired = !conf.Categories.useentitiesrepaired;
  1366.                 if (arg.Args[0] == "ResourcesGathered") conf.Categories.useresourcesgathered = !conf.Categories.useresourcesgathered;
  1367.                 if (arg.Args[0] == "StructuresUpgraded") conf.Categories.usestructuresupgraded = !conf.Categories.usestructuresupgraded;
  1368.             }
  1369.             SaveConfig(conf);
  1370.             UpdateCategories();
  1371.             SetUpBroadcast();
  1372.             CallAdminUI(player, false);
  1373.         }
  1374.  
  1375.         [ConsoleCommand("ToggleIntenseOptions")]
  1376.         void ToggleIntense(ConsoleSystem.Arg arg)
  1377.         {
  1378.             var player = arg.Connection.player as BasePlayer;
  1379.             if (!HasPermission(player.UserIDString, permAllowed))
  1380.                 return;
  1381.             conf.Options.useIntenseOptions = !conf.Options.useIntenseOptions;
  1382.  
  1383.             SaveConfig(conf);
  1384.             UpdateCategories();
  1385.             CallAdminUI(player, false);
  1386.         }
  1387.        
  1388.         [ConsoleCommand("toggleStatCollection")]
  1389.         void ToggleCollection(ConsoleSystem.Arg arg)
  1390.         {
  1391.             var player = arg.Connection.player as BasePlayer;
  1392.             if (HasPermission(player.UserIDString, permAllowed))
  1393.             conf.Options.statCollection = !conf.Options.statCollection;
  1394.  
  1395.             SaveConfig(conf);
  1396.             UpdateCategories();
  1397.             CallAdminUI(player, false);
  1398.         }
  1399.        
  1400.         [ConsoleCommand("AllowAdmin")]
  1401.         void AllowAdmin(ConsoleSystem.Arg arg)
  1402.         {
  1403.             var player = arg.Connection.player as BasePlayer;
  1404.             if (!HasPermission(player.UserIDString, permAllowed))
  1405.                 return;
  1406.             conf.Options.allowadmin = !conf.Options.allowadmin;
  1407.            
  1408.             SaveConfig(conf);
  1409.             UpdateCategories();
  1410.             CallAdminUI(player, false);        
  1411.         }
  1412.        
  1413.         [ConsoleCommand("wipeFirst")]
  1414.         void WipeAttempt(ConsoleSystem.Arg arg)
  1415.         {
  1416.             var player = arg.Connection.player as BasePlayer;
  1417.             if (!HasPermission(player.UserIDString, permAllowed)) return;
  1418.             CallAdminUI(player, true);
  1419.         }
  1420.  
  1421.         [ConsoleCommand("saveLeaderboard")]
  1422.         void SaveBoard(ConsoleSystem.Arg arg)
  1423.         {
  1424.             var player = arg.Connection.player as BasePlayer;
  1425.             if (!HasPermission(player.UserIDString, permAllowed)) return;
  1426.             var dictToUse = data.PlayerRankData;
  1427.             var date = DateTime.UtcNow;
  1428.             data.leaderBoards.Add(date, new Dictionary<string, LeaderBoardData>());
  1429.             var lBoard = data.leaderBoards[date];
  1430.             //create leaderboard            
  1431.             foreach (var cat in allowedCats)
  1432.             {
  1433.                 if (allowedCats[cat.Key] == true)
  1434.                 {
  1435.                     if (conf.Options.allowadmin == false)
  1436.                         dictToUse = data.PlayerRankData.Where(pair => pair.Value.Admin == false).ToDictionary(val => val.Key, val => val.Value);
  1437.            
  1438.                     Dictionary<ulong, PRDATA> top = dictToUse.OrderByDescending(pair => pair.Value.GetValue(cat.Key)).Take(1).ToDictionary(pair => pair.Key, pair => pair.Value);
  1439.                     foreach (var leader in top)
  1440.                     {
  1441.                         data.leaderBoards[date].Add(cat.Key, new LeaderBoardData
  1442.                         {
  1443.                             UserID = leader.Key,
  1444.                             UserName = leader.Value.Name,
  1445.                             Score = Convert.ToDouble(data.PlayerRankData[leader.Key].GetValue(cat.Key))  
  1446.                         });
  1447.                     }
  1448.                 }
  1449.             }
  1450.             SaveConfig(conf);
  1451.             SaveData(true);
  1452.             CallAdminUI(player, false);
  1453.         }        
  1454.      
  1455.         [ConsoleCommand("wipeLeaderBoards")]
  1456.         void WipeBoards(ConsoleSystem.Arg arg)
  1457.         {
  1458.             var player = arg.Connection.player as BasePlayer;
  1459.             if (!HasPermission(player.UserIDString, permAllowed))
  1460.                 return;
  1461.             var dictToUse = data.PlayerRankData;
  1462.            
  1463.             data.leaderBoards.Clear();
  1464.             SaveData(true);
  1465.             CallAdminUI(player, false);
  1466.         }
  1467.        
  1468.         [ConsoleCommand("Close")]
  1469.         void Close(ConsoleSystem.Arg arg)
  1470.         {
  1471.             var player = arg.Connection.player as BasePlayer;
  1472.             if (MenuOpen.Contains(player.userID))
  1473.                 {
  1474.                     CuiHelper.DestroyUi(player, "ranksgui");
  1475.                     MenuOpen.Remove(player.userID);
  1476.                 }
  1477.             return;
  1478.         }
  1479.        
  1480.         [ConsoleCommand("playerranks.save")]
  1481.         void CmdSave()
  1482.         {
  1483.             SaveData(true);
  1484.             Puts("PlayerRanks database was saved.");
  1485.         }
  1486.  
  1487.         [ConsoleCommand("playerranks.wipe")]
  1488.         void CmdWipe(ConsoleSystem.Arg arg)
  1489.         {
  1490.             if (arg.Connection != null)
  1491.             {
  1492.                 var player = arg.Connection.player as BasePlayer;
  1493.                 if (!HasPermission(player.UserIDString, permAllowed))
  1494.                 return;
  1495.             }
  1496.             data.PlayerRankData.Clear();
  1497.             PRData.WriteObject(data);
  1498.             SetUp();
  1499.             if (conf.MySQL.useMySQL)
  1500.                 LoadMySQL(true);
  1501.             Puts("PlayerRanks database was wiped.");
  1502.         }
  1503.         #endregion
  1504.        
  1505.         #region chat commands
  1506.         [ChatCommand("pr")]
  1507.         void CmdTarget(BasePlayer player, string command, string[] args)
  1508.         {
  1509.             var dictToUse = data.PlayerRankData;
  1510.        
  1511.             if (args == null || args.Length == 0)
  1512.             {
  1513.                 CallPersonalStatsUI(player, "true");
  1514.                 return;
  1515.             }
  1516.            
  1517.             var d = data.PlayerRankData[player.userID];
  1518.            
  1519.             switch (args[0].ToLower())
  1520.             {
  1521.                 case "save":
  1522.                     if (HasPermission(player.UserIDString, permAllowed))
  1523.                     {
  1524.                         SaveData(true);
  1525.                         SendReply(player, conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + lang.GetMessage("save", this));
  1526.                     }
  1527.                     return;            
  1528.            
  1529.                 case "wipe":
  1530.                     if (HasPermission(player.UserIDString, permAllowed))
  1531.                     {
  1532.                         data.PlayerRankData.Clear();
  1533.                         PRData.WriteObject(data);
  1534.                         SetUp();
  1535.                         if (conf.MySQL.useMySQL)
  1536.                             LoadMySQL(true);
  1537.                         SendReply(player, conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + lang.GetMessage("wipe", this));
  1538.                     }
  1539.                     return;
  1540.                    
  1541.                 case "del":
  1542.                     if (HasPermission(player.UserIDString, permAllowed))
  1543.                     {
  1544.                         if (args.Length == 2)
  1545.                         {
  1546.                             string s = args[1];
  1547.                             ulong result;
  1548.                             if (ulong.TryParse(s, out result))
  1549.                             {
  1550.                                 ulong arg = Convert.ToUInt64(args[1]);
  1551.                                 if (data.PlayerRankData.ContainsKey(arg))
  1552.                                 {
  1553.                                     data.PlayerRankData.Remove(arg);
  1554.                                     PRData.WriteObject(data);
  1555.                                     SetUp();
  1556.                                     SendReply(player, conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + lang.GetMessage("dbremoved", this));
  1557.                                 }
  1558.                                 else
  1559.                                 SendReply(player, conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + lang.GetMessage("noentry", this));
  1560.                             }
  1561.                             else
  1562.                             SendReply(player, conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + lang.GetMessage("syntax", this));
  1563.                         }
  1564.                     }
  1565.                     return;
  1566.  
  1567.                 case "wipecategory":
  1568.                     if (args.Length == 2)
  1569.                     {
  1570.                         if (HasPermission(player.UserIDString, permAllowed))
  1571.                         {
  1572.                             var request = args[1].ToLower();
  1573.                             bool found = false;
  1574.                             foreach (var cat in allowedCats)
  1575.                             {
  1576.                                 if (cat.Key.ToLower() == request)
  1577.                                 {
  1578.                                     foreach (var Entry in data.PlayerRankData)
  1579.                                     {
  1580.                                         if (request == "pvpkills") data.PlayerRankData[Entry.Key].PVPKills = 0;
  1581.                                         if (request == "pvpdistance") data.PlayerRankData[Entry.Key].PVPDistance = 0;
  1582.                                         if (request == "pvekills") data.PlayerRankData[Entry.Key].PVEKills = 0;
  1583.                                         if (request == "pvedistance") data.PlayerRankData[Entry.Key].PVEDistance = 0;
  1584.                                         if (request == "npckills") data.PlayerRankData[Entry.Key].NPCKills = 0;
  1585.                                         if (request == "npcdistance") data.PlayerRankData[Entry.Key].NPCDistance = 0;
  1586.                                         if (request == "sleeperskilled") data.PlayerRankData[Entry.Key].SleepersKilled = 0;
  1587.                                         if (request == "headshots") data.PlayerRankData[Entry.Key].HeadShots = 0;
  1588.                                         if (request == "deaths") data.PlayerRankData[Entry.Key].Deaths = 0;
  1589.                                         if (request == "suicides") data.PlayerRankData[Entry.Key].Suicides = 0;
  1590.                                         if (request == "kdr") data.PlayerRankData[Entry.Key].KDR = 0;
  1591.                                         if (request == "sdr") data.PlayerRankData[Entry.Key].SDR = 0;
  1592.                                         if (request == "skullscrushed") data.PlayerRankData[Entry.Key].SkullsCrushed = 0;
  1593.                                         if (request == "timeswounded") data.PlayerRankData[Entry.Key].TimesWounded = 0;
  1594.                                         if (request == "timeshealed") data.PlayerRankData[Entry.Key].TimesHealed = 0;
  1595.                                         if (request == "helihits") data.PlayerRankData[Entry.Key].HeliHits = 0;
  1596.                                         if (request == "helikills") data.PlayerRankData[Entry.Key].HeliKills = 0;
  1597.                                         if (request == "apchits") data.PlayerRankData[Entry.Key].APCHits = 0;
  1598.                                         if (request == "apckills") data.PlayerRankData[Entry.Key].APCKills = 0;
  1599.                                         if (request == "barrelsdestroyed") data.PlayerRankData[Entry.Key].BarrelsDestroyed = 0;
  1600.                                         if (request == "explosivesthrown") data.PlayerRankData[Entry.Key].ExplosivesThrown = 0;
  1601.                                         if (request == "arrowsfired") data.PlayerRankData[Entry.Key].ArrowsFired = 0;
  1602.                                         if (request == "bulletsfired") data.PlayerRankData[Entry.Key].BulletsFired = 0;
  1603.                                         if (request == "rocketslaunched") data.PlayerRankData[Entry.Key].RocketsLaunched = 0;
  1604.                                         if (request == "weapontrapsdestroyed") data.PlayerRankData[Entry.Key].WeaponTrapsDestroyed = 0;
  1605.                                         if (request == "dropslooted") data.PlayerRankData[Entry.Key].DropsLooted = 0;
  1606.                                         if (request == "structuresbuilt") data.PlayerRankData[Entry.Key].StructuresBuilt = 0;
  1607.                                         if (request == "structuresdemolished") data.PlayerRankData[Entry.Key].StructuresDemolished = 0;
  1608.                                         if (request == "itemsdeployed") data.PlayerRankData[Entry.Key].ItemsDeployed = 0;
  1609.                                         if (request == "itemscrafted") data.PlayerRankData[Entry.Key].ItemsCrafted = 0;
  1610.                                         if (request == "entitiesrepaired") data.PlayerRankData[Entry.Key].EntitiesRepaired = 0;
  1611.                                         if (request == "resourcesgathered") data.PlayerRankData[Entry.Key].ResourcesGathered = 0;
  1612.                                         if (request == "structuresupgraded") data.PlayerRankData[Entry.Key].StructuresUpgraded = 0;
  1613.                                     }
  1614.                                     found = true;
  1615.                                     break;
  1616.                                 }
  1617.                             }
  1618.                             if (found ==true)
  1619.                             {
  1620.                                 PRData.WriteObject(data);
  1621.                                 SetUp();
  1622.                                 SendReply(player, conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + lang.GetMessage("category", this));
  1623.                             }
  1624.                             else
  1625.                                 SendReply(player, conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + lang.GetMessage("nocategory", this));
  1626.                         }
  1627.                     }
  1628.                     return;
  1629.             }
  1630.         }
  1631.  
  1632.         bool BroadcastMethod(String category)
  1633.         {
  1634.             var dictToUse = data.PlayerRankData;
  1635.             int amount = conf.Options.TimedTopListAmount;
  1636.             if (conf.Options.allowadmin == false)
  1637.             {
  1638.                 dictToUse = data.PlayerRankData.Where(pair => pair.Value.Admin == false).ToDictionary(val => val.Key, val => val.Value);
  1639.             }
  1640.             Dictionary<string, object> top = dictToUse.OrderByDescending(pair => pair.Value.GetValue(category)).Take(amount).ToDictionary(pair => pair.Value.Name, pair => pair.Value.GetValue(category));
  1641.             top = top.Where(kvp =>  Convert.ToDouble(kvp.Value) > 0).ToDictionary(x => x.Key, x => x.Value);
  1642.             if (top.Count > 0)
  1643.             {
  1644.                 var outMsg = conf.GUI.fontColor1 + lang.GetMessage("title", this) + "</color>" + conf.GUI.fontColor2 + lang.GetMessage("bestHits", this) + lang.GetMessage(category, this) + "</color> \n";
  1645.                 foreach (var name in top)
  1646.                 {
  1647.                     outMsg += string.Format(conf.GUI.fontColor3 + "{0} : " + "</color>" + conf.GUI.fontColor1 + "{1}" + "</color>" + "\n", name.Key, name.Value);
  1648.                 }
  1649.                 if (outMsg != "")
  1650.                 Server.Broadcast($"<size={conf.Options.TimedTopListSize}>{outMsg}</size>");
  1651.                 return true;
  1652.             }
  1653.             return false;
  1654.         }
  1655.        
  1656.         bool IsAuth(BasePlayer player)  
  1657.         {
  1658.             return (player.net.connection != null && player.net.connection.authLevel == 2);
  1659.         }
  1660.        
  1661.         public static string RemoveSurrogatePairs(string str, string replacementCharacter = "?")
  1662.         {
  1663.             if (str == null) return null;
  1664.        
  1665.             StringBuilder sb = null;
  1666.        
  1667.             for (int i = 0; i < str.Length; i++)
  1668.             {
  1669.                 char ch = str[i];
  1670.                 if (char.IsSurrogate(ch))
  1671.                 {
  1672.                     if (sb == null)
  1673.                         sb = new StringBuilder(str, 0, i, str.Length);
  1674.        
  1675.                     sb.Append(replacementCharacter);
  1676.        
  1677.                     if (i + 1 < str.Length && char.IsHighSurrogate(ch) && char.IsLowSurrogate(str[i + 1]))
  1678.                         i++;
  1679.                 }
  1680.                 else if (sb != null)
  1681.                 sb.Append(ch);
  1682.             }
  1683.             return sb == null ? str : sb.ToString();
  1684.         }
  1685.    
  1686.    
  1687.         Core.MySql.Libraries.MySql Sql = Interface.Oxide.GetLibrary<Core.MySql.Libraries.MySql>();
  1688.         Core.Database.Connection Sql_conn;
  1689.        
  1690.         void LoadMySQL(bool wipe)
  1691.         {
  1692.             try { Sql_conn.Con.Open(); }
  1693.             catch (Exception e) { PrintWarning(e.Message); return; }
  1694.  
  1695.             try
  1696.             {
  1697.                 if (Sql_conn == null || Sql_conn.Con == null)
  1698.                 {
  1699.                     Puts("Player Ranks MySQL connection has failed. Please check your credentials.");    
  1700.                     return;
  1701.                 }
  1702.                 if (wipe && conf.MySQL.autoWipe)
  1703.                 {    
  1704.                     Sql.Insert(Core.Database.Sql.Builder.Append($"DROP TABLE IF EXISTS {conf.MySQL.tablename}"), Sql_conn);
  1705.                     Puts("Player Ranks MySQL Table Was Dropped.");
  1706.                 }
  1707.  
  1708.                 Sql.Insert(Core.Database.Sql.Builder.Append($"CREATE TABLE IF NOT EXISTS {conf.MySQL.tablename} ( `UserID` VARCHAR(17) NOT NULL, `Name` LONGTEXT NOT NULL, `PVPKills` INT(11) NOT NULL, `PVPDistance` DOUBLE NOT NULL, `PVEKills` INT(11) NOT NULL, `PVEDistance` DOUBLE NOT NULL, `NPCKills` INT(11) NOT NULL, `NPCDistance` DOUBLE NOT NULL, `SleepersKilled` INT(11) NOT NULL, `HeadShots` Int(11) NOT NULL, `Deaths` INT(11) NOT NULL, `Suicides` INT(11) NOT NULL, `KDR` DOUBLE NOT NULL, `SDR` DOUBLE NOT NULL, `SkullsCrushed` INT(11) NOT NULL, `TimesWounded` INT(11) NOT NULL, `TimesHealed` INT(11) NOT NULL, `HeliHits` INT(11) NOT NULL, `HeliKills` INT(11) NOT NULL, `APCHits` INT(11) NOT NULL, `APCKills` INT(11) NOT NULL, `BarrelsDestroyed` INT(11) NOT NULL, `ExplosivesThrown` INT(11) NOT NULL, `ArrowsFired` INT(11) NOT NULL, `BulletsFired` INT(11) NOT NULL, `RocketsLaunched` INT(11) NOT NULL, `WeaponTrapsDestroyed` INT(11) NOT NULL, `DropsLooted` Int(11) NOT NULL,  `StructuresBuilt` INT(11) NOT NULL, `StructuresDemolished` INT(11) NOT NULL, `ItemsDeployed` INT(11) NOT NULL, `ItemsCrafted` INT(11) NOT NULL, `EntitiesRepaired` INT(11) NOT NULL, `ResourcesGathered` INT(11) NOT NULL, `StructuresUpgraded` INT(11) NOT NULL, `Status` VARCHAR(11) NOT NULL, `TimePlayed` TIME NOT NULL, `Admin` BOOLEAN NOT NULL, `Economics` INT(11) NOT NULL, `ActiveDate` DateTime NOT NULL, PRIMARY KEY (`UserID`));"), Sql_conn);
  1709.                    
  1710.                 Sql.Insert(Core.Database.Sql.Builder.Append($"CREATE TABLE IF NOT EXISTS {conf.MySQL.LBtableName} ( `Date` DateTime NOT NULL,`PVPKillsName` LONGTEXT NOT NULL,`PVPKills` INT(11) NOT NULL,`PVPDistanceName` LONGTEXT NOT NULL,`PVPDistance` DOUBLE NOT NULL,`PVEKillsName` LONGTEXT NOT NULL,`PVEKills` INT(11) NOT NULL,`PVEDistanceName` LONGTEXT NOT NULL,`PVEDistance` DOUBLE NOT NULL,`NPCKillsName` LONGTEXT NOT NULL,`NPCKills` INT(11) NOT NULL,`NPCDistanceName` LONGTEXT NOT NULL,`NPCDistance` DOUBLE NOT NULL,`SleepersKilledName` LONGTEXT NOT NULL,`SleepersKilled` INT(11) NOT NULL,`HeadShotsName` LONGTEXT NOT NULL,`HeadShots` Int(11) NOT NULL,`DeathsName` LONGTEXT NOT NULL,`Deaths` INT(11) NOT NULL,`SuicidesName` LONGTEXT NOT NULL,`Suicides` INT(11) NOT NULL,`KDRName` LONGTEXT NOT NULL,`KDR` DOUBLE NOT NULL,`SDRName` LONGTEXT NOT NULL,`SDR` DOUBLE NOT NULL,`SkullsCrushedName` LONGTEXT NOT NULL,`SkullsCrushed` INT(11) NOT NULL,`TimesWoundedName` LONGTEXT NOT NULL,`TimesWounded` INT(11) NOT NULL,`TimesHealedName` LONGTEXT NOT NULL,`TimesHealed` INT(11) NOT NULL,`HeliHitsName` LONGTEXT NOT NULL,`HeliHits` INT(11) NOT NULL,`HeliKillsName` LONGTEXT NOT NULL,`HeliKills` INT(11) NOT NULL,`APCHitsName` LONGTEXT NOT NULL,`APCHits` INT(11) NOT NULL,`APCKillsName` LONGTEXT NOT NULL,`APCKills` INT(11) NOT NULL,`BarrelsDestroyedName` LONGTEXT NOT NULL,`BarrelsDestroyed` INT(11) NOT NULL,`ExplosivesThrownName` LONGTEXT NOT NULL,`ExplosivesThrown` INT(11) NOT NULL,`ArrowsFiredName` LONGTEXT NOT NULL,`ArrowsFired` INT(11) NOT NULL,`BulletsFiredName` LONGTEXT NOT NULL,`BulletsFired` INT(11) NOT NULL,`RocketsLaunchedName` LONGTEXT NOT NULL,`RocketsLaunched` INT(11) NOT NULL,`WeaponTrapsDestroyedName` LONGTEXT NOT NULL,`WeaponTrapsDestroyed` INT(11) NOT NULL,`DropsLootedName` LONGTEXT NOT NULL,`DropsLooted` Int(11) NOT NULL,`StructuresBuiltName` LONGTEXT NOT NULL,`StructuresBuilt` INT(11) NOT NULL,`StructuresDemolishedName` LONGTEXT NOT NULL,`StructuresDemolished` INT(11) NOT NULL,`ItemsDeployedName` LONGTEXT NOT NULL,`ItemsDeployed` INT(11) NOT NULL,`ItemsCraftedName` LONGTEXT NOT NULL,`ItemsCrafted` INT(11) NOT NULL,`EntitiesRepairedName` LONGTEXT NOT NULL,`EntitiesRepaired` INT(11) NOT NULL,`ResourcesGatheredName` LONGTEXT NOT NULL,`ResourcesGathered` INT(11) NOT NULL,`StructuresUpgradedName` LONGTEXT NOT NULL,`StructuresUpgraded` INT(11) NOT NULL,PRIMARY KEY (`Date`));"), Sql_conn);
  1711.             }
  1712.             catch (Exception e)
  1713.             {
  1714.                 Puts("Player Ranks did not succesfully create a table.");    
  1715.             }
  1716.  
  1717.             try
  1718.             {
  1719.                 foreach(var c in data.PlayerRankData)  
  1720.                 {
  1721.                     Sql.Insert(Core.Database.Sql.Builder.Append($"INSERT INTO {conf.MySQL.tablename} ( `UserID`, `Name`, `PVPKills`, `PVPDistance`, `PVEKills`, `PVEDistance`, `NPCKills`, `NPCDistance`, `SleepersKilled`, `Headshots`, `Deaths`, `Suicides`, `KDR`, `SDR`, `SkullsCrushed`, `TimesWounded`, `TimesHealed`, `HeliHits`, `HeliKills`, `APCHits`, `APCKills`, `BarrelsDestroyed`, `ExplosivesThrown`, `ArrowsFired`, `BulletsFired`, `RocketsLaunched`, `WeaponTrapsDestroyed`, `DropsLooted`, `StructuresBuilt`, `StructuresDemolished`, `ItemsDeployed`, `ItemsCrafted`, `EntitiesRepaired`, `ResourcesGathered`, `StructuresUpgraded`, `Status`, `TimePlayed`, `Admin`, `Economics`, `ActiveDate`) VALUES ( @0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29, @30, @31, @32, @33, @34, @35, @36, @37, @38, @39) ON DUPLICATE KEY UPDATE UserID = @0, Name = @1, PVPKills = @2, PVPDistance = @3, PVEKills = @4, PVEDistance = @5, NPCKills = @6, NPCDistance = @7,SleepersKilled = @8, HeadShots = @9, Deaths = @10, Suicides = @11, KDR = @12, SDR = @13, SkullsCrushed = @14, TimesWounded = @15, TimesHealed = @16, HeliHits = @17, HeliKills = @18, APCHits = @19, APCKills = @20, BarrelsDestroyed = @21, ExplosivesThrown = @22, ArrowsFired = @23, BulletsFired = @24, RocketsLaunched = @25, WeaponTrapsDestroyed = @26, DropsLooted = @27, StructuresBuilt = @28, StructuresDemolished = @29, ItemsDeployed = @30, ItemsCrafted = @31, EntitiesRepaired = @32, ResourcesGathered = @33, StructuresUpgraded = @34, Status = @35, TimePlayed = @36, Admin = @37, Economics = @38, ActiveDate = @39;", c.Value.UserID, RemoveSurrogatePairs(c.Value.Name, ""), c.Value.PVPKills, c.Value.PVPDistance, c.Value.PVEKills, c.Value.PVEDistance, c.Value.NPCKills, c.Value.NPCDistance, c.Value.SleepersKilled, c.Value.HeadShots, c.Value.Deaths, c.Value.Suicides, c.Value.KDR, c.Value.SDR, c.Value.SkullsCrushed, c.Value.TimesWounded, c.Value.TimesHealed, c.Value.HeliHits, c.Value.HeliKills, c.Value.APCHits, c.Value.APCKills, c.Value.BarrelsDestroyed, c.Value.ExplosivesThrown, c.Value.ArrowsFired, c.Value.BulletsFired, c.Value.RocketsLaunched, c.Value.WeaponTrapsDestroyed, c.Value.DropsLooted, c.Value.StructuresBuilt, c.Value.StructuresDemolished, c.Value.ItemsDeployed, c.Value.ItemsCrafted, c.Value.EntitiesRepaired, c.Value.ResourcesGathered, c.Value.StructuresUpgraded, c.Value.Status, c.Value.TimePlayed, c.Value.Admin, c.Value.Economics, c.Value.ActiveDate), Sql_conn);
  1722.                 }
  1723.  
  1724.                 foreach(var c in data.leaderBoards)  
  1725.                 {
  1726.                     Sql.Insert(Core.Database.Sql.Builder.Append($"INSERT INTO {conf.MySQL.LBtableName} ( `Date`,`PVPKillsName`, `PVPKills`, `PVPDistanceName`, `PVPDistance`, `PVEKillsName`, `PVEKills`, `PVEDistanceName`, `PVEDistance`, `NPCKillsName`, `NPCKills`, `NPCDistanceName`, `NPCDistance`, `SleepersKilledName`, `SleepersKilled`, `HeadshotsName`,`Headshots`,`DeathsName`, `Deaths`, `SuicidesName`, `Suicides`, `KDRName`, `KDR`, `SDRName`,`SDR`,`SkullsCrushedName`, `SkullsCrushed`, `TimesWoundedName`, `TimesWounded`, `TimesHealedName`, `TimesHealed`, `HeliHitsName`,`HeliHits`,`HeliKillsName`, `HeliKills`, `APCHitsName`, `APCHits`, `APCKillsName`, `APCKills`, `BarrelsDestroyedName`,`BarrelsDestroyed`,`ExplosivesThrownName`, `ExplosivesThrown`, `ArrowsFiredName`, `ArrowsFired`, `BulletsFiredName`, `BulletsFired`, `RocketsLaunchedName`,`RocketsLaunched`,`WeaponTrapsDestroyedName`, `WeaponTrapsDestroyed`, `DropsLootedName`, `DropsLooted`, `StructuresBuiltName`, `StructuresBuilt`, `StructuresDemolishedName`,`StructuresDemolished`,`ItemsDeployedName`, `ItemsDeployed`, `ItemsCraftedName`, `ItemsCrafted`, `EntitiesRepairedName`, `EntitiesRepaired`, `ResourcesGatheredName`,`ResourcesGathered`,`StructuresUpgradedName`, `StructuresUpgraded`) VALUES ( @0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29, @30, @31, @32, @33, @34, @35, @36, @37, @38, @39, @40, @41, @42, @43, @44, @45, @46, @47, @48, @49, @50, @51, @52, @53, @54, @55, @56, @57, @58, @59, @60, @61, @62, @63, @64, @65, @66 ) ON DUPLICATE KEY UPDATE Date = @0, PVPKillsName = @1, PVPKills = @2, PVPDistanceName = @3, PVPDistance = @4, PVEKillsName = @5, PVEKills = @6, PVEDistanceName = @7,PVEDistance = @8,NPCKillsName = @9, NPCKills = @10, NPCDistanceName = @11, NPCDistance = @12, SleepersKilledName = @13, SleepersKilled = @14, HeadshotsName = @15, Headshots = @16,DeathsName = @17, Deaths = @18, SuicidesName = @19, Suicides = @20, KDRName = @21, KDR = @22, SDRName = @23, SDR = @24,SkullsCrushedName = @25, SkullsCrushed = @26, TimesWoundedName = @27, TimesWounded = @28, TimesHealedName = @29, TimesHealed = @30, HeliHitsName = @31, HeliHits = @32,HeliKillsName = @33, HeliKills = @34, APCHitsName = @35, APCHits = @36, APCKillsName = @37, APCKills = @38, BarrelsDestroyedName = @39, BarrelsDestroyed = @40,ExplosivesThrownName = @41, ExplosivesThrown = @42, ArrowsFiredName = @43, ArrowsFired = @44, BulletsFiredName = @45, BulletsFired = @46, RocketsLaunchedName = @47, RocketsLaunched = @48,WeaponTrapsDestroyedName = @49, WeaponTrapsDestroyed = @50, DropsLootedName = @51, DropsLooted = @52, StructuresBuiltName = @53, StructuresBuilt = @54, StructuresDemolishedName = @55, StructuresDemolished = @56,ItemsDeployedName = @57, ItemsDeployed = @58, ItemsCraftedName = @59, ItemsCrafted = @60, EntitiesRepairedName = @61, EntitiesRepaired = @62, ResourcesGatheredName = @63, ResourcesGathered = @64,StructuresUpgradedName = @65, StructuresUpgraded = @66;",c.Key,RemoveSurrogatePairs(c.Value["PVPKills"].UserName, ""),c.Value["PVPKills"].Score,RemoveSurrogatePairs(c.Value["PVPDistance"].UserName, ""),c.Value["PVPDistance"].Score,RemoveSurrogatePairs(c.Value["PVEKills"].UserName, ""),c.Value["PVEKills"].Score,RemoveSurrogatePairs(c.Value["PVEDistance"].UserName, ""),c.Value["PVEDistance"].Score,RemoveSurrogatePairs(c.Value["NPCKills"].UserName, ""),c.Value["NPCKills"].Score,RemoveSurrogatePairs(c.Value["NPCDistance"].UserName, ""),c.Value["NPCDistance"].Score,RemoveSurrogatePairs(c.Value["SleepersKilled"].UserName, ""),c.Value["SleepersKilled"].Score,RemoveSurrogatePairs(c.Value["HeadShots"].UserName, ""),c.Value["HeadShots"].UserName,RemoveSurrogatePairs(c.Value["Deaths"].UserName, ""),c.Value["Deaths"].Score,RemoveSurrogatePairs(c.Value["Suicides"].UserName, ""),c.Value["Suicides"].Score,RemoveSurrogatePairs(c.Value["KDR"].UserName, ""),c.Value["KDR"].Score,RemoveSurrogatePairs(c.Value["SDR"].UserName, ""),c.Value["SDR"].Score,RemoveSurrogatePairs(c.Value["SkullsCrushed"].UserName, ""),c.Value["SkullsCrushed"].Score,RemoveSurrogatePairs(c.Value["TimesWounded"].UserName, ""),c.Value["TimesWounded"].Score,RemoveSurrogatePairs(c.Value["TimesHealed"].UserName, ""),c.Value["TimesHealed"].Score,RemoveSurrogatePairs(c.Value["HeliHits"].UserName, ""),c.Value["HeliHits"].Score,RemoveSurrogatePairs(c.Value["HeliKills"].UserName, ""),c.Value["HeliKills"].Score,RemoveSurrogatePairs(c.Value["APCHits"].UserName, ""),c.Value["APCHits"].Score,RemoveSurrogatePairs(c.Value["APCKills"].UserName, ""),c.Value["APCKills"].Score,RemoveSurrogatePairs(c.Value["BarrelsDestroyed"].UserName, ""),c.Value["BarrelsDestroyed"].Score,RemoveSurrogatePairs(c.Value["ExplosivesThrown"].UserName, ""),c.Value["ExplosivesThrown"].Score,RemoveSurrogatePairs(c.Value["ArrowsFired"].UserName, ""),c.Value["ArrowsFired"].Score,RemoveSurrogatePairs(c.Value["BulletsFired"].UserName, ""),c.Value["BulletsFired"].Score,RemoveSurrogatePairs(c.Value["RocketsLaunched"].UserName, ""),c.Value["RocketsLaunched"].Score,RemoveSurrogatePairs(c.Value["WeaponTrapsDestroyed"].UserName, ""),c.Value["WeaponTrapsDestroyed"].Score,RemoveSurrogatePairs(c.Value["DropsLooted"].UserName, ""),c.Value["DropsLooted"].Score,RemoveSurrogatePairs(c.Value["StructuresBuilt"].UserName, ""),c.Value["StructuresBuilt"].Score,RemoveSurrogatePairs(c.Value["StructuresDemolished"].UserName, ""),c.Value["StructuresDemolished"].Score,RemoveSurrogatePairs(c.Value["ItemsDeployed"].UserName, ""),c.Value["ItemsDeployed"].Score,RemoveSurrogatePairs(c.Value["ItemsCrafted"].UserName, ""),c.Value["ItemsCrafted"].Score,RemoveSurrogatePairs(c.Value["EntitiesRepaired"].UserName, ""),c.Value["EntitiesRepaired"].Score,RemoveSurrogatePairs(c.Value["ResourcesGathered"].UserName, ""),c.Value["ResourcesGathered"].Score,RemoveSurrogatePairs(c.Value["StructuresUpgraded"].UserName, ""),c.Value["StructuresUpgraded"].Score), Sql_conn);
  1727.                 }
  1728.                 Puts("Player Ranks MySQL Table Was Saved.");
  1729.             }
  1730.             catch (Exception e)  
  1731.             {
  1732.                 Puts("Player Ranks did not succesfully save data to SQL.");  
  1733.             }
  1734.  
  1735.         }    
  1736.         #endregion
  1737.  
  1738.         #region config
  1739.         private ConfigData conf;
  1740.         public class ConfigData
  1741.         {
  1742.             public Options Options = new Options();
  1743.             public GUI GUI = new GUI();
  1744.             public Categories Categories = new Categories();
  1745.             public MySQL MySQL = new MySQL();
  1746.         }
  1747.         public class Options
  1748.         {
  1749.             public bool useFriendsAPI;
  1750.             public bool useClans;
  1751.             public bool useRustIO;
  1752.             public bool blockEvents = true;
  1753.             public bool useIntenseOptions = true;
  1754.             public int TimedTopListTimer = 10;
  1755.             public int TimedTopListAmount = 3;
  1756.             public bool useTimedTopList = true;
  1757.             public int TimedTopListSize = 12;
  1758.             public int saveTimer = 30;
  1759.             public string chatCommandAlias = "ranks";
  1760.             public bool allowadmin;
  1761.             public bool statCollection = true;
  1762.             public int lastLoginLimit;
  1763.         }
  1764.        
  1765.         public class GUI
  1766.         {
  1767.             public string fontColor1 = "<color=orange>";
  1768.             public string fontColor2 = "<color=#939393>";
  1769.             public string fontColor3 = "<color=white>";
  1770.             public string buttonColour = "0.7 0.32 0.17 1";
  1771.             public double guitransparency = 0.5;            
  1772.         }
  1773.         public class Categories
  1774.         {
  1775.             public bool usepvpkills = true;
  1776.             public bool usepvpdistance = true;
  1777.             public bool usepvekills = true;
  1778.             public bool usepvedistance = true;
  1779.             public bool usenpckills = true;
  1780.             public bool usenpcdistance = true;
  1781.             public bool usesleeperskilled = true;
  1782.             public bool useheadshots = true;
  1783.             public bool usedeaths = true;
  1784.             public bool usesuicides = true;
  1785.             public bool usekdr = true;
  1786.             public bool usesdr = true;
  1787.             public bool useskullscrushed = true;
  1788.             public bool usetimeswounded = true;
  1789.             public bool usetimeshealed = true;
  1790.             public bool usehelihits = true;
  1791.             public bool usehelikills = true;
  1792.             public bool useapchits = true;
  1793.             public bool useapckills = true;
  1794.             public bool usebarrelsdestroyed = true;
  1795.             public bool useexplosivesthrown = true;
  1796.             public bool usearrowsfired = true;
  1797.             public bool usebulletsfired = true;
  1798.             public bool userocketslaunched = true;
  1799.             public bool useweapontrapsdestroyed = true;
  1800.             public bool usedropslooted = true;
  1801.             public bool usestructuresbuilt = true;
  1802.             public bool usestructuresdemolished = true;
  1803.             public bool useitemsdeployed = true;
  1804.             public bool useitemscrafted = true;
  1805.             public bool useentitiesrepaired = true;
  1806.             public bool useresourcesgathered = true;
  1807.             public bool usestructuresupgraded = true;
  1808.         }
  1809.            
  1810.         public class MySQL
  1811.         {
  1812.             public bool useMySQL;
  1813.             public string sql_host = "";
  1814.             public int sql_port = 3306;
  1815.             public string sql_db = "";
  1816.             public string sql_user = "";
  1817.             public string sql_pass = "";
  1818.             public string tablename = "playerranksdb";
  1819.             public string LBtableName = "playerranksLeaderdb";
  1820.             public bool autoWipe = true;
  1821.         }
  1822.  
  1823.         protected override void LoadDefaultConfig()
  1824.         {
  1825.             Puts("Creating new config file.");
  1826.             var config = new ConfigData();
  1827.             SaveConfig();
  1828.         }
  1829.        
  1830.         private void LoadConfigVariables()
  1831.         {
  1832.             conf = Config.ReadObject<ConfigData>();
  1833.             SaveConfig(conf);
  1834.         }
  1835.        
  1836.         void SaveConfig(ConfigData config)
  1837.         {
  1838.             Config.WriteObject(config, true);
  1839.         }
  1840.         #endregion
  1841.  
  1842.         #region classes and data storage
  1843.  
  1844.         void SaveData(bool sql)
  1845.         {
  1846.             var banlist = new List<ulong>();
  1847.             foreach(var entry in data.PlayerRankData)  
  1848.             {
  1849.                 if (ServerUsers.Is(entry.Key, ServerUsers.UserGroup.Banned))
  1850.                 {
  1851.                     banlist.Add(entry.Key);
  1852.                 }
  1853.                 entry.Value.Status = "offline";
  1854.             }
  1855.             foreach (var banned in banlist)
  1856.                 if (data.PlayerRankData.ContainsKey(banned))
  1857.                     data.PlayerRankData.Remove(banned);
  1858.                    
  1859.             DateTime cutoff = DateTime.UtcNow.Subtract(TimeSpan.FromDays(conf.Options.lastLoginLimit));
  1860.             if (conf.Options.lastLoginLimit > 0)
  1861.             data.PlayerRankData = data.PlayerRankData.Where(x=>x.Value.ActiveDate > cutoff).ToDictionary(x=>x.Key,x=>x.Value);
  1862.  
  1863.             foreach(BasePlayer player in BasePlayer.activePlayerList)
  1864.                 if (data.PlayerRankData.ContainsKey(player.userID))
  1865.                     UpdatePlayer(player);
  1866.  
  1867.             PRData.WriteObject(data);
  1868.             if (conf.MySQL.useMySQL && sql)
  1869.                 LoadMySQL(false);
  1870.         }
  1871.  
  1872.         void LoadData()
  1873.         {
  1874.             try
  1875.             {
  1876.                 data = Interface.GetMod().DataFileSystem.ReadObject<DataStorage>("PlayerRanks");
  1877.                 PRData.WriteObject(data);//forces to conform immediately if structure has changed
  1878.             }
  1879.             catch
  1880.             {
  1881.                 data = new DataStorage();
  1882.             }
  1883.         }
  1884.         #endregion
  1885.  
  1886.         #region messages
  1887.  
  1888.         readonly Dictionary<string, string> messages = new Dictionary<string, string>()
  1889.         {
  1890.             {"title", "PlayerRanks: " },
  1891.             {"wipe", "PlayerRanks database wiped."},
  1892.             {"nowipe", "PlayerRanks database was already empty."},
  1893.             {"save", "PlayerRanks database saved."},
  1894.             {"del", "PlayerRanks for this player were wiped."},
  1895.             {"bestHits", "Top " },
  1896.             {"dbremoved", "Details for this ID have been removed." },          
  1897.             {"noentry", "There is no entry in the database for this ID." },
  1898.             {"syntax", "ID must be 17 digits." },
  1899.             {"category", "Stats for this category have been removed." },
  1900.             {"nocategory", "This is not a recognised category." },          
  1901.             {"noResults", "There are no statistics for this category." },
  1902.             {"disabled", "This category has been disabled." },
  1903.             {"leaderboard", "Leader Board" },
  1904.             {"categories", "Categories" },
  1905.             {"close", "Close" },
  1906.             {"mystats", "My Stats" },
  1907.             {"admin", "Admin" },
  1908.            
  1909.            
  1910.             {"gatherStatsOnButton", "Gather Stats - On" },
  1911.             {"gatherStatsOffButton", "Gather Stats - Off" },
  1912.             {"disableAdminStatsButton", "Disable Admin Stats" },
  1913.             {"AllowAdminStatsButton", "Allow Admin Stats" },
  1914.             {"savePlayerDataButton", "Save Player Data" },
  1915.             {"wipePlayerDataButton", "Wipe Player Data" },
  1916.             {"confirmbutton", "Confirm" },
  1917.             {"saveLeaderBoardButton", "Save Leaderboard" },
  1918.             {"wipeLeaderBoardButton", "Wipe Leaderboards" },
  1919.             {"on", "On" },
  1920.             {"off", "Off" },
  1921.  
  1922.             {"PVPKills", "PVP Kills " },
  1923.             {"PVPDistance", "PVP Distance " },
  1924.             {"PVEKills", "PVE Kills " },
  1925.             {"PVEDistance", "PVE Distance " },
  1926.             {"NPCKills", "NPC Kills " },
  1927.             {"NPCDistance", "NPC Distance " },
  1928.             {"SleepersKilled", "Sleepers Killed " },  
  1929.             {"HeadShots", "Head Shots " },
  1930.             {"Deaths", "Deaths " },
  1931.             {"Suicides", "Suicides " },
  1932.             {"KDR", "KDR " },
  1933.             {"SDR", "SDR " },
  1934.             {"SkullsCrushed", "Skulls Crushed " },
  1935.             {"TimesWounded", "Times Wounded " },
  1936.             {"TimesHealed", "Times Healed " },
  1937.             {"HeliHits", "Heli Hits " },
  1938.             {"HeliKills", "Heli Kills " },
  1939.             {"APCHits", "APC Hits " },
  1940.             {"APCKills", "APC Kills " },
  1941.             {"BarrelsDestroyed", "Barrels Destroyed " },
  1942.             {"ExplosivesThrown", "Explosives Thrown " },
  1943.             {"ArrowsFired", "Arrows Fired " },
  1944.             {"BulletsFired", "Bullets Fired " },
  1945.             {"RocketsLaunched", "Rockets Launched " },
  1946.             {"WeaponTrapsDestroyed", "Weapon Traps Destroyed " },
  1947.             {"DropsLooted", "Airdrops Looted " },
  1948.  
  1949.             //intense options
  1950.             {"StructuresBuilt", "Structures Built " },
  1951.             {"StructuresDemolished", "Structures Demolished " },
  1952.             {"ItemsDeployed", "Items Deployed " },
  1953.             {"ItemsCrafted", "Items Crafted " },
  1954.             {"EntitiesRepaired", "Entities Repaired " },
  1955.             {"ResourcesGathered", "Resources Gathered " },
  1956.             {"StructuresUpgraded", "Structures Upgraded " },
  1957.         };
  1958.         #endregion
  1959.     }
  1960. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement