Guest User

RemoverTool.cs fix

a guest
Oct 2nd, 2025
240
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 212.13 KB | None | 0 0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Text;
  7. using Facepunch;
  8. using Newtonsoft.Json;
  9. using Newtonsoft.Json.Linq;
  10. using Oxide.Core;
  11. using Oxide.Core.Plugins;
  12. using Oxide.Game.Rust;
  13. using Oxide.Game.Rust.Cui;
  14. using Rust;
  15. using UnityEngine;
  16. using VLB;
  17. using Random = UnityEngine.Random;
  18.  
  19. namespace Oxide.Plugins
  20. {
  21.     [Info("Remover Tool", "Reneb/Fuji/Arainrr/Tryhard", "4.3.43", ResourceId = 651)]
  22.     [Description("Building and entity removal tool")]
  23.     public class RemoverTool : RustPlugin
  24.     {
  25.         #region Fields
  26.  
  27.         [PluginReference]
  28.         private readonly Plugin Friends, ServerRewards, Clans, Economics, ImageLibrary, BuildingOwners, RustTranslationAPI, NoEscape;
  29.  
  30.         private const string ECONOMICS_KEY = "economics";
  31.         private const string SERVER_REWARDS_KEY = "serverrewards";
  32.  
  33.         private const string PERMISSION_ALL = "removertool.all";
  34.         private const string PERMISSION_ADMIN = "removertool.admin";
  35.         private const string PERMISSION_NORMAL = "removertool.normal";
  36.         private const string PERMISSION_TARGET = "removertool.target";
  37.         private const string PERMISSION_EXTERNAL = "removertool.external";
  38.         private const string PERMISSION_OVERRIDE = "removertool.override";
  39.         private const string PERMISSION_STRUCTURE = "removertool.structure";
  40.  
  41.         private const string PREFAB_ITEM_DROP = "assets/prefabs/misc/item drop/item_drop.prefab";
  42.  
  43.         private const int LAYER_ALL = 1 << 0 | 1 << 8 | 1 << 21;
  44.         private const int LAYER_TARGET = ~(1 << 2 | 1 << 3 | 1 << 4 | 1 << 10 | 1 << 18 | 1 << 28 | 1 << 29);
  45.  
  46.         private static RemoverTool _instance;
  47.         private static BUTTON _removeButton;
  48.         private static RemoveMode _removeMode;
  49.  
  50.         private readonly object _false = false;
  51.         private bool _configChanged;
  52.         private bool _removeOverride;
  53.         private Coroutine _removeAllCoroutine;
  54.         private Coroutine _removeStructureCoroutine;
  55.         private Coroutine _removeExternalCoroutine;
  56.         private Coroutine _removePlayerEntityCoroutine;
  57.  
  58.         private StringBuilder _debugStringBuilder;
  59.         private Hash<ulong, float> _entitySpawnedTimes;
  60.         private readonly Hash<ulong, float> _cooldownTimes = new Hash<ulong, float>();
  61.  
  62.         private enum RemoveMode
  63.         {
  64.             None,
  65.             NoHeld,
  66.             MeleeHit,
  67.             SpecificTool
  68.         }
  69.  
  70.         private enum RemoveType
  71.         {
  72.             None,
  73.             All,
  74.             Admin,
  75.             Normal,
  76.             External,
  77.             Structure
  78.         }
  79.  
  80.         private enum PlayerEntityRemoveType
  81.         {
  82.             All,
  83.             Cupboard,
  84.             Building
  85.         }
  86.  
  87.         #endregion Fields
  88.  
  89.         #region Oxide Hooks
  90.  
  91.         private void Init()
  92.         {
  93.             _instance = this;
  94.             permission.RegisterPermission(PERMISSION_ALL, this);
  95.             permission.RegisterPermission(PERMISSION_ADMIN, this);
  96.             permission.RegisterPermission(PERMISSION_NORMAL, this);
  97.             permission.RegisterPermission(PERMISSION_TARGET, this);
  98.             permission.RegisterPermission(PERMISSION_OVERRIDE, this);
  99.             permission.RegisterPermission(PERMISSION_EXTERNAL, this);
  100.             permission.RegisterPermission(PERMISSION_STRUCTURE, this);
  101.  
  102.             Unsubscribe(nameof(OnHammerHit));
  103.             Unsubscribe(nameof(OnEntitySpawned));
  104.             Unsubscribe(nameof(OnEntityKill));
  105.             Unsubscribe(nameof(OnPlayerAttack));
  106.             Unsubscribe(nameof(OnActiveItemChanged));
  107.             Unsubscribe(nameof(OnRaidBlock));
  108.             Unsubscribe(nameof(OnCombatBlock));
  109.  
  110.             foreach (var perm in _configData.permission.Keys)
  111.             {
  112.                 if (!permission.PermissionExists(perm, this))
  113.                 {
  114.                     permission.RegisterPermission(perm, this);
  115.                 }
  116.             }
  117.             cmd.AddChatCommand(_configData.chat.command, this, nameof(CmdRemove));
  118.         }
  119.  
  120.         private void OnServerInitialized()
  121.         {
  122.             Initialize();
  123.             UpdateConfig();
  124.             LoadConfig();
  125.             _removeMode = RemoveMode.None;
  126.             if (_configData.removerMode.noHeldMode)
  127.             {
  128.                 _removeMode = RemoveMode.NoHeld;
  129.             }
  130.             if (_configData.removerMode.meleeHitMode)
  131.             {
  132.                 _removeMode = RemoveMode.MeleeHit;
  133.             }
  134.             if (_configData.removerMode.specificToolMode)
  135.             {
  136.                 _removeMode = RemoveMode.SpecificTool;
  137.             }
  138.             if (_removeMode == RemoveMode.MeleeHit)
  139.             {
  140.                 BaseMelee baseMelee;
  141.                 ItemDefinition itemDefinition;
  142.                 if (string.IsNullOrEmpty(_configData.removerMode.meleeHitItemShortname) ||
  143.                         (itemDefinition = ItemManager.FindItemDefinition(_configData.removerMode.meleeHitItemShortname)) == null ||
  144.                         (baseMelee = itemDefinition.GetComponent<ItemModEntity>()?.entityPrefab.Get()?.GetComponent<BaseMelee>()) == null)
  145.                 {
  146.                     PrintError($"{_configData.removerMode.meleeHitItemShortname} is not an item shortname for a melee tool");
  147.                     _removeMode = RemoveMode.None;
  148.                 }
  149.                 else
  150.                 {
  151.                     Subscribe(baseMelee is Hammer ? nameof(OnHammerHit) : nameof(OnPlayerAttack));
  152.                 }
  153.             }
  154.  
  155.             if (_configData.noEscape.useRaidBlocker)
  156.             {
  157.                 Subscribe(nameof(OnRaidBlock));
  158.             }
  159.             if (_configData.noEscape.useCombatBlocker)
  160.             {
  161.                 Subscribe(nameof(OnCombatBlock));
  162.             }
  163.  
  164.             if (_configData.global.entityTimeLimit)
  165.             {
  166.                 _entitySpawnedTimes = new Hash<ulong, float>();
  167.                 Subscribe(nameof(OnEntitySpawned));
  168.                 Subscribe(nameof(OnEntityKill));
  169.             }
  170.             if (_configData.global.logToFile)
  171.             {
  172.                 _debugStringBuilder = new StringBuilder();
  173.             }
  174.  
  175.             if (_removeMode == RemoveMode.MeleeHit && _configData.removerMode.meleeHitEnableInHand ||
  176.                     _removeMode == RemoveMode.SpecificTool && _configData.removerMode.specificToolEnableInHand)
  177.             {
  178.                 Subscribe(nameof(OnActiveItemChanged));
  179.             }
  180.  
  181.             if (!Enum.TryParse(_configData.global.removeButton, true, out _removeButton) || !Enum.IsDefined(typeof(BUTTON), _removeButton))
  182.             {
  183.                 PrintError($"{_configData.global.removeButton} is an invalid button. The remove button has been changed to 'FIRE_PRIMARY'.");
  184.                 _removeButton = BUTTON.FIRE_PRIMARY;
  185.                 _configData.global.removeButton = _removeButton.ToString();
  186.                 SaveConfig();
  187.             }
  188.             if (ImageLibrary != null)
  189.             {
  190.                 foreach (var image in _configData.imageUrls)
  191.                 {
  192.                     AddImageToLibrary(image.Value, image.Key);
  193.                 }
  194.                 if (_configData.ui.showCrosshair)
  195.                 {
  196.                     AddImageToLibrary(_configData.ui.crosshairImageUrl, UINAME_CROSSHAIR);
  197.                 }
  198.             }
  199.         }
  200.  
  201.         private void Unload()
  202.         {
  203.             // if (_configChanged)
  204.             // {
  205.             //     SaveConfig();
  206.             // }
  207.             SaveDebug();
  208.             if (_removeAllCoroutine != null)
  209.             {
  210.                 ServerMgr.Instance.StopCoroutine(_removeAllCoroutine);
  211.             }
  212.             if (_removeStructureCoroutine != null)
  213.             {
  214.                 ServerMgr.Instance.StopCoroutine(_removeStructureCoroutine);
  215.             }
  216.             if (_removeExternalCoroutine != null)
  217.             {
  218.                 ServerMgr.Instance.StopCoroutine(_removeExternalCoroutine);
  219.             }
  220.             if (_removePlayerEntityCoroutine != null)
  221.             {
  222.                 ServerMgr.Instance.StopCoroutine(_removePlayerEntityCoroutine);
  223.             }
  224.             foreach (var player in BasePlayer.activePlayerList)
  225.             {
  226.                 player.GetComponent<ToolRemover>()?.DisableTool();
  227.             }
  228.             _configData = null;
  229.             _instance = null;
  230.         }
  231.  
  232.         private void OnServerSave()
  233.         {
  234.             if (_configChanged)
  235.             {
  236.                 _configChanged = false;
  237.                 timer.Once(Random.Range(0f, 60f), SaveConfig);
  238.             }
  239.             if (_configData.global.logToFile)
  240.             {
  241.                 timer.Once(Random.Range(0f, 60f), SaveDebug);
  242.             }
  243.             if (_configData.global.entityTimeLimit && _entitySpawnedTimes != null)
  244.             {
  245.                 var currentTime = Time.realtimeSinceStartup;
  246.                 foreach (var entry in _entitySpawnedTimes.ToArray())
  247.                 {
  248.                     if (currentTime - entry.Value > _configData.global.limitTime)
  249.                     {
  250.                         _entitySpawnedTimes.Remove(entry.Key);
  251.                     }
  252.                 }
  253.             }
  254.         }
  255.  
  256.         private void OnEntitySpawned(BaseEntity entity)
  257.         {
  258.             if (entity == null || entity.net == null)
  259.             {
  260.                 return;
  261.             }
  262.             // if (!CanEntityBeSaved(entity)) return;
  263.             _entitySpawnedTimes[entity.net.ID.Value] = Time.realtimeSinceStartup;
  264.         }
  265.  
  266.         private void OnEntityKill(BaseEntity entity)
  267.         {
  268.             if (entity == null || entity.net == null)
  269.             {
  270.                 return;
  271.             }
  272.             _entitySpawnedTimes.Remove(entity.net.ID.Value);
  273.         }
  274.  
  275.         private object OnPlayerAttack(BasePlayer player, HitInfo info)
  276.         {
  277.             return OnHammerHit(player, info);
  278.         }
  279.  
  280.         private object OnHammerHit(BasePlayer player, HitInfo info)
  281.         {
  282.             if (player == null || info.HitEntity == null)
  283.             {
  284.                 return null;
  285.             }
  286.             var toolRemover = player.GetComponent<ToolRemover>();
  287.             if (toolRemover == null)
  288.             {
  289.                 return null;
  290.             }
  291.             if (!IsMeleeTool(player))
  292.             {
  293.                 return null;
  294.             }
  295.             toolRemover.HitEntity = info.HitEntity;
  296.             return _false;
  297.         }
  298.  
  299.         private void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem)
  300.         {
  301.             if (newItem == null)
  302.             {
  303.                 return;
  304.             }
  305.             if (player == null || !player.userID.IsSteamId())
  306.             {
  307.                 return;
  308.             }
  309.             if (IsToolRemover(player))
  310.             {
  311.                 return;
  312.             }
  313.             if (_removeMode == RemoveMode.MeleeHit && IsMeleeTool(newItem))
  314.             {
  315.                 ToggleRemove(player, RemoveType.Normal);
  316.                 return;
  317.             }
  318.             if (_removeMode == RemoveMode.SpecificTool && IsSpecificTool(newItem))
  319.             {
  320.                 ToggleRemove(player, RemoveType.Normal);
  321.             }
  322.         }
  323.  
  324.         #endregion Oxide Hooks
  325.  
  326.         #region Initializing
  327.  
  328.         private readonly HashSet<Construction> _constructions = new HashSet<Construction>();
  329.         private readonly Dictionary<string, int> _itemShortNameToItemId = new Dictionary<string, int>();
  330.         private readonly Dictionary<string, string> _prefabNameToStructure = new Dictionary<string, string>();
  331.         private readonly Dictionary<string, string> _shortPrefabNameToDeployable = new Dictionary<string, string>();
  332.  
  333.         private void Initialize()
  334.         {
  335.             foreach (var itemDefinition in ItemManager.GetItemDefinitions())
  336.             {
  337.                 if (!_itemShortNameToItemId.ContainsKey(itemDefinition.shortname))
  338.                 {
  339.                     _itemShortNameToItemId.Add(itemDefinition.shortname, itemDefinition.itemid);
  340.                 }
  341.                 var deployablePrefab = itemDefinition.GetComponent<ItemModDeployable>()?.entityPrefab?.resourcePath;
  342.                 if (string.IsNullOrEmpty(deployablePrefab))
  343.                 {
  344.                     continue;
  345.                 }
  346.                 var shortPrefabName = Utility.GetFileNameWithoutExtension(deployablePrefab);
  347.                 if (!string.IsNullOrEmpty(shortPrefabName) && !_shortPrefabNameToDeployable.ContainsKey(shortPrefabName))
  348.                 {
  349.                     _shortPrefabNameToDeployable.Add(shortPrefabName, itemDefinition.shortname);
  350.                 }
  351.             }
  352.             foreach (var entry in PrefabAttribute.server.prefabs)
  353.             {
  354.                 var construction = entry.Value.Find<Construction>().FirstOrDefault();
  355.                 if (construction != null && construction.deployable == null && !string.IsNullOrEmpty(construction.info.name.english))
  356.                 {
  357.                     _constructions.Add(construction);
  358.                     if (!_prefabNameToStructure.ContainsKey(construction.fullName))
  359.                     {
  360.                         _prefabNameToStructure.Add(construction.fullName, construction.info.name.english);
  361.                     }
  362.                 }
  363.             }
  364.         }
  365.  
  366.         #endregion Initializing
  367.  
  368.         #region Methods
  369.  
  370.         private static string GetRemoveTypeName(RemoveType removeType)
  371.         {
  372.             return _configData.removeType[removeType].displayName;
  373.         }
  374.  
  375.         private static void DropItemContainer(ItemContainer itemContainer, Vector3 position, Quaternion rotation)
  376.         {
  377.             itemContainer?.Drop(PREFAB_ITEM_DROP, position, rotation, 0);
  378.         }
  379.  
  380.         private static bool IsExternalWall(StabilityEntity stabilityEntity)
  381.         {
  382.             return stabilityEntity.ShortPrefabName.Contains("external");
  383.         }
  384.  
  385.         private static bool CanEntityBeDisplayed(BaseEntity entity, BasePlayer player)
  386.         {
  387.             var stash = entity as StashContainer;
  388.             return stash == null || !stash.IsHidden() || stash.PlayerInRange(player);
  389.         }
  390.  
  391.         private static bool CanEntityBeSaved(BaseEntity entity)
  392.         {
  393.             if (entity is BuildingBlock)
  394.             {
  395.                 return true;
  396.             }
  397.             EntitySettings entitySettings;
  398.             if (_configData.remove.entity.TryGetValue(entity.ShortPrefabName, out entitySettings) && entitySettings.enabled)
  399.             {
  400.                 return true;
  401.             }
  402.             return false;
  403.         }
  404.  
  405.         private static bool HasEntityEnabled(BaseEntity entity)
  406.         {
  407.             var buildingBlock = entity as BuildingBlock;
  408.             if (buildingBlock != null)
  409.             {
  410.                 bool valid;
  411.                 if (_configData.remove.validConstruction.TryGetValue(buildingBlock.grade, out valid) && valid)
  412.                 {
  413.                     return true;
  414.                 }
  415.             }
  416.             EntitySettings entitySettings;
  417.             if (_configData.remove.entity.TryGetValue(entity.ShortPrefabName, out entitySettings) && entitySettings.enabled)
  418.             {
  419.                 return true;
  420.             }
  421.             return false;
  422.         }
  423.  
  424.         private static bool IsRemovableEntity(BaseEntity entity)
  425.         {
  426.             if (_instance._shortPrefabNameToDeployable.ContainsKey(entity.ShortPrefabName)
  427.                     || _instance._prefabNameToStructure.ContainsKey(entity.PrefabName)
  428.                     || _configData.remove.entity.ContainsKey(entity.ShortPrefabName))
  429.             {
  430.                 var baseCombatEntity = entity as BaseCombatEntity;
  431.                 if (baseCombatEntity != null)
  432.                 {
  433.                     if (baseCombatEntity.IsDead())
  434.                     {
  435.                         return false;
  436.                     }
  437.                     if (baseCombatEntity.pickup.itemTarget != null)
  438.                     {
  439.                         return true;
  440.                     }
  441.                 }
  442.                 return true;
  443.             }
  444.             return false;
  445.         }
  446.  
  447.         private static string GetEntityImage(string name)
  448.         {
  449.             if (_instance.ImageLibrary == null)
  450.             {
  451.                 return null;
  452.             }
  453.             if (_configData.imageUrls.ContainsKey(name))
  454.             {
  455.                 return GetImageFromLibrary(name);
  456.             }
  457.             if (_instance._itemShortNameToItemId.ContainsKey(name))
  458.             {
  459.                 return GetImageFromLibrary(name);
  460.             }
  461.             return null;
  462.         }
  463.  
  464.         private static string GetItemImage(string shortname)
  465.         {
  466.             if (_instance.ImageLibrary == null)
  467.             {
  468.                 return null;
  469.             }
  470.             switch (shortname.ToLower())
  471.             {
  472.                 case ECONOMICS_KEY:
  473.                     return GetImageFromLibrary(ECONOMICS_KEY);
  474.  
  475.                 case SERVER_REWARDS_KEY:
  476.                     return GetImageFromLibrary(SERVER_REWARDS_KEY);
  477.             }
  478.             return GetEntityImage(shortname);
  479.         }
  480.  
  481.         private static void TryFindEntityName(BasePlayer player, BaseEntity entity, out string displayName, out string imageName)
  482.         {
  483.             var target = entity as BasePlayer;
  484.             if (target != null)
  485.             {
  486.                 imageName = target.userID.IsSteamId() ? target.UserIDString : target.ShortPrefabName;
  487.                 displayName = $"{target.displayName} ({target.ShortPrefabName})";
  488.                 return;
  489.             }
  490.             EntitySettings entitySettings;
  491.             if (_configData.remove.entity.TryGetValue(entity.ShortPrefabName, out entitySettings))
  492.             {
  493.                 imageName = entity.ShortPrefabName;
  494.                 displayName = _instance.GetDeployableDisplayName(player, entity.ShortPrefabName, entitySettings.displayName);
  495.                 return;
  496.             }
  497.  
  498.             string structureName;
  499.             if (_instance._prefabNameToStructure.TryGetValue(entity.PrefabName, out structureName))
  500.             {
  501.                 BuildingBlocksSettings buildingBlockSettings;
  502.                 if (_configData.remove.buildingBlock.TryGetValue(structureName, out buildingBlockSettings))
  503.                 {
  504.                     imageName = structureName;
  505.                     displayName = _instance.GetConstructionDisplayName(player, entity.PrefabName, buildingBlockSettings.displayName);
  506.                     return;
  507.                 }
  508.             }
  509.  
  510.             imageName = entity.ShortPrefabName;
  511.             displayName = entity.ShortPrefabName;
  512.         }
  513.  
  514.         private static string GetDisplayNameByCurrencyName(string language, string currencyName, long skinId)
  515.         {
  516.             var itemDefinition = ItemManager.FindItemDefinition(currencyName);
  517.             if (itemDefinition != null)
  518.             {
  519.                 var translationKey = $"{itemDefinition.shortname}_{skinId}";
  520.                 var translationValue = GetCurrencyDisplayName(translationKey, itemDefinition.displayName.english, true);
  521.                 if (skinId <= 0 || string.IsNullOrEmpty(translationValue))
  522.                 {
  523.                     var displayName = _instance.GetItemDisplayName(language, itemDefinition.shortname);
  524.                     if (!string.IsNullOrEmpty(displayName))
  525.                     {
  526.                         return displayName;
  527.                     }
  528.                     return itemDefinition.displayName.english;
  529.                 }
  530.                 return GetCurrencyDisplayName(translationKey, itemDefinition.displayName.english);
  531.             }
  532.             return GetCurrencyDisplayName(currencyName, currencyName);
  533.         }
  534.  
  535.         private static string GetCurrencyDisplayName(string currencyName, string defaultName = null, bool readOnly = false)
  536.         {
  537.             string displayName;
  538.             if (_configData.remove.displayNames.TryGetValue(currencyName, out displayName))
  539.             {
  540.                 return displayName;
  541.             }
  542.             if (!readOnly)
  543.             {
  544.                 _configData.remove.displayNames.Add(currencyName, defaultName);
  545.                 _instance._configChanged = true;
  546.             }
  547.             return defaultName;
  548.         }
  549.  
  550.         private static PermissionSettings GetPermissionSettings(BasePlayer player)
  551.         {
  552.             var priority = 0;
  553.             PermissionSettings permissionSettings = null;
  554.             foreach (var entry in _configData.permission)
  555.             {
  556.                 if (entry.Value.priority >= priority && _instance.permission.UserHasPermission(player.UserIDString, entry.Key))
  557.                 {
  558.                     priority = entry.Value.priority;
  559.                     permissionSettings = entry.Value;
  560.                 }
  561.             }
  562.             return permissionSettings ?? new PermissionSettings();
  563.         }
  564.  
  565.         private static Vector2 GetAnchor(string anchor)
  566.         {
  567.             var array = anchor.Split(' ');
  568.             return new Vector2(float.Parse(array[0]), float.Parse(array[1]));
  569.         }
  570.  
  571.         private static bool AddImageToLibrary(string url, string shortname, ulong skin = 0)
  572.         {
  573.             return (bool)_instance.ImageLibrary.Call("AddImage", url, shortname.ToLower(), skin);
  574.         }
  575.  
  576.         private static string GetImageFromLibrary(string shortname, ulong skin = 0, bool returnUrl = false)
  577.         {
  578.             return string.IsNullOrEmpty(shortname) ? null : (string)_instance.ImageLibrary.Call("GetImage", shortname.ToLower(), skin, returnUrl);
  579.         }
  580.  
  581.         #endregion Methods
  582.  
  583.         #region NoEscape
  584.  
  585.         private void OnRaidBlock(BasePlayer player)
  586.         {
  587.             if (_configData.noEscape.useRaidBlocker)
  588.             {
  589.                 // Print(player, Lang("RaidBlocked", player.UserIDString));
  590.                 player.GetComponent<ToolRemover>()?.DisableTool(false);
  591.             }
  592.         }
  593.  
  594.         private void OnCombatBlock(BasePlayer player)
  595.         {
  596.             if (_configData.noEscape.useCombatBlocker)
  597.             {
  598.                 // Print(player, Lang("CombatBlocked", player.UserIDString));
  599.                 player.GetComponent<ToolRemover>()?.DisableTool(false);
  600.             }
  601.         }
  602.  
  603.         private bool IsPlayerBlocked(BasePlayer player, out string reason)
  604.         {
  605.             if (NoEscape != null)
  606.             {
  607.                 if (_configData.noEscape.useRaidBlocker && IsRaidBlocked(player.UserIDString))
  608.                 {
  609.                     reason = Lang("RaidBlocked", player.UserIDString);
  610.                     return true;
  611.                 }
  612.                 if (_configData.noEscape.useCombatBlocker && IsCombatBlocked(player.UserIDString))
  613.                 {
  614.                     reason = Lang("CombatBlocked", player.UserIDString);
  615.                     return true;
  616.                 }
  617.             }
  618.  
  619.             reason = null;
  620.             return false;
  621.         }
  622.  
  623.         private bool IsRaidBlocked(string playerID)
  624.         {
  625.             return (bool)NoEscape.Call("IsRaidBlocked", playerID);
  626.         }
  627.  
  628.         private bool IsCombatBlocked(string playerID)
  629.         {
  630.             return (bool)NoEscape.Call("IsCombatBlocked", playerID);
  631.         }
  632.  
  633.         #endregion NoEscape
  634.  
  635.         #region UI
  636.  
  637.         private static class UI
  638.         {
  639.             public static CuiElementContainer CreateElementContainer(string parent, string panelName, string backgroundColor, string anchorMin, string anchorMax, string offsetMin = "", string offsetMax = "", bool cursor = false)
  640.             {
  641.                 return new CuiElementContainer
  642.                 {
  643.                     {
  644.                         new CuiPanel
  645.                         {
  646.                             Image = { Color = backgroundColor },
  647.                             RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax, OffsetMin = offsetMin, OffsetMax = offsetMax },
  648.                             CursorEnabled = cursor
  649.                         },
  650.                         parent, panelName
  651.                     }
  652.                 };
  653.             }
  654.  
  655.             public static void CreatePanel(ref CuiElementContainer container, string panelName, string backgroundColor, string anchorMin, string anchorMax, bool cursor = false)
  656.             {
  657.                 container.Add(new CuiPanel
  658.                 {
  659.                     Image = { Color = backgroundColor },
  660.                     RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax },
  661.                     CursorEnabled = cursor
  662.                 }, panelName);
  663.             }
  664.  
  665.             public static void CreateLabel(ref CuiElementContainer container, string panelName, string textColor, string text, int fontSize, string anchorMin, string anchorMax, TextAnchor align = TextAnchor.MiddleCenter, float fadeIn = 0f)
  666.             {
  667.                 container.Add(new CuiLabel
  668.                 {
  669.                     Text = { Color = textColor, FontSize = fontSize, Align = align, Text = text, FadeIn = fadeIn },
  670.                     RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax }
  671.                 }, panelName);
  672.             }
  673.  
  674.             public static void CreateImage(ref CuiElementContainer container, string panelName, string image, string anchorMin, string anchorMax, string color = "1 1 1 1")
  675.             {
  676.                 container.Add(new CuiElement
  677.                 {
  678.                     Name = CuiHelper.GetGuid(),
  679.                     Parent = panelName,
  680.                     Components =
  681.                     {
  682.                         new CuiRawImageComponent { Color = color, Png = image },
  683.                         new CuiRectTransformComponent { AnchorMin = anchorMin, AnchorMax = anchorMax }
  684.                     }
  685.                 });
  686.             }
  687.  
  688.             public static void CreateImage(ref CuiElementContainer container, string panelName, int itemId, ulong skinId, string anchorMin, string anchorMax)
  689.             {
  690.                 container.Add(new CuiPanel
  691.                 {
  692.                     Image = { ItemId = itemId, SkinId = skinId },
  693.                     RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax },
  694.                     CursorEnabled = false
  695.                 }, panelName);
  696.             }
  697.         }
  698.  
  699.         [Flags]
  700.         private enum UiEntry
  701.         {
  702.             None = 0,
  703.             Entity = 1,
  704.             Price = 1 << 1,
  705.             Refund = 1 << 2,
  706.             Auth = 1 << 3
  707.         }
  708.  
  709.         private const string UINAME_MAIN = "RemoverToolUI_Main";
  710.         private const string UINAME_TIMELEFT = "RemoverToolUI_TimeLeft";
  711.         private const string UINAME_ENTITY = "RemoverToolUI_Entity";
  712.         private const string UINAME_PRICE = "RemoverToolUI_Price";
  713.         private const string UINAME_REFUND = "RemoverToolUI_Refund";
  714.         private const string UINAME_AUTH = "RemoverToolUI_Auth";
  715.         private const string UINAME_CROSSHAIR = "RemoverToolUI_Crosshair";
  716.  
  717.         private static void CreateCrosshairUI(BasePlayer player)
  718.         {
  719.             if (_instance.ImageLibrary == null)
  720.             {
  721.                 return;
  722.             }
  723.             var image = GetImageFromLibrary(UINAME_CROSSHAIR);
  724.             if (string.IsNullOrEmpty(image))
  725.             {
  726.                 return;
  727.             }
  728.             var container = UI.CreateElementContainer("Hud", UINAME_CROSSHAIR, "0 0 0 0", _configData.ui.crosshairAnchorMin, _configData.ui.crosshairAnchorMax, _configData.ui.crosshairOffsetMin, _configData.ui.crosshairOffsetMax);
  729.             UI.CreateImage(ref container, UINAME_CROSSHAIR, image, "0 0", "1 1", _configData.ui.crosshairColor);
  730.             CuiHelper.DestroyUi(player, UINAME_CROSSHAIR);
  731.             CuiHelper.AddUi(player, container);
  732.         }
  733.  
  734.         private static void CreateMainUI(BasePlayer player, RemoveType removeType)
  735.         {
  736.             var container = UI.CreateElementContainer("Hud", UINAME_MAIN, _configData.ui.removerToolBackgroundColor, _configData.ui.removerToolAnchorMin, _configData.ui.removerToolAnchorMax, _configData.ui.removerToolOffsetMin, _configData.ui.removerToolOffsetMax);
  737.             UI.CreatePanel(ref container, UINAME_MAIN, _configData.ui.removeBackgroundColor, _configData.ui.removeAnchorMin, _configData.ui.removeAnchorMax);
  738.             UI.CreateLabel(ref container, UINAME_MAIN, _configData.ui.removeTextColor, _instance.Lang("RemoverToolType", player.UserIDString, GetRemoveTypeName(removeType)), _configData.ui.removeTextSize, _configData.ui.removeTextAnchorMin, _configData.ui.removeTextAnchorMax, TextAnchor.MiddleLeft);
  739.             CuiHelper.DestroyUi(player, UINAME_MAIN);
  740.             CuiHelper.AddUi(player, container);
  741.         }
  742.  
  743.         private static void UpdateTimeLeftUI(BasePlayer player, RemoveType removeType, int timeLeft, int currentRemoved, int maxRemovable)
  744.         {
  745.             var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_TIMELEFT, _configData.ui.timeLeftBackgroundColor, _configData.ui.timeLeftAnchorMin, _configData.ui.timeLeftAnchorMax);
  746.             UI.CreateLabel(ref container, UINAME_TIMELEFT, _configData.ui.timeLeftTextColor, _instance.Lang("TimeLeft", player.UserIDString, timeLeft, removeType == RemoveType.Normal || removeType == RemoveType.Admin ? maxRemovable == 0 ? $"{currentRemoved} / {_instance.Lang("Unlimit", player.UserIDString)}" : $"{currentRemoved} / {maxRemovable}" : currentRemoved.ToString()), _configData.ui.timeLeftTextSize, _configData.ui.timeLeftTextAnchorMin, _configData.ui.timeLeftTextAnchorMax, TextAnchor.MiddleLeft);
  747.             CuiHelper.DestroyUi(player, UINAME_TIMELEFT);
  748.             CuiHelper.AddUi(player, container);
  749.         }
  750.  
  751.         private static void UpdateEntityUI(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info)
  752.         {
  753.             var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_ENTITY, _configData.ui.entityBackgroundColor, _configData.ui.entityAnchorMin, _configData.ui.entityAnchorMax);
  754.  
  755.             string displayName, imageName;
  756.             TryFindEntityName(player, targetEntity, out displayName, out imageName);
  757.             if (info.HasValue && !string.IsNullOrEmpty(info.Value.DisplayName.Value))
  758.             {
  759.                 displayName = info.Value.DisplayName.Value;
  760.             }
  761.             UI.CreateLabel(ref container, UINAME_ENTITY, _configData.ui.entityTextColor, displayName, _configData.ui.entityTextSize, _configData.ui.entityTextAnchorMin, _configData.ui.entityTextAnchorMax, TextAnchor.MiddleLeft);
  762.             if (_configData.ui.entityImageEnabled)
  763.             {
  764.                 var imageAnchorMin = _configData.ui.entityImageAnchorMin;
  765.                 var imageAnchorMax = _configData.ui.entityImageAnchorMax;
  766.                 if (info.HasValue && !string.IsNullOrEmpty(info.Value.ImageId.Value))
  767.                 {
  768.                     var image = info.Value.ImageId.Value;
  769.                     if (!string.IsNullOrEmpty(image))
  770.                     {
  771.                         UI.CreateImage(ref container, UINAME_ENTITY, image, imageAnchorMin, imageAnchorMax);
  772.                     }
  773.                 }
  774.                 else if (!string.IsNullOrEmpty(imageName))
  775.                 {
  776.                     string shortname;
  777.                     int itemId;
  778.                     if (_instance._shortPrefabNameToDeployable.TryGetValue(imageName, out shortname) && _instance._itemShortNameToItemId.TryGetValue(shortname, out itemId))
  779.                     {
  780.                         UI.CreateImage(ref container, UINAME_ENTITY, itemId, targetEntity.skinID, imageAnchorMin, imageAnchorMax);
  781.                     }
  782.                     else
  783.                     {
  784.                         var image = GetEntityImage(imageName);
  785.                         if (!string.IsNullOrEmpty(image))
  786.                         {
  787.                             UI.CreateImage(ref container, UINAME_ENTITY, image, imageAnchorMin, imageAnchorMax);
  788.                         }
  789.                     }
  790.                 }
  791.             }
  792.             CuiHelper.DestroyUi(player, UINAME_ENTITY);
  793.             CuiHelper.AddUi(player, container);
  794.         }
  795.  
  796.         private static void UpdatePriceUI(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info, bool usePrice)
  797.         {
  798.             Dictionary<string, CurrencyInfo> price = null;
  799.             if (usePrice)
  800.             {
  801.                 price = _instance.GetPrice(targetEntity, info);
  802.             }
  803.             var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_PRICE, _configData.ui.priceBackgroundColor, _configData.ui.priceAnchorMin, _configData.ui.priceAnchorMax);
  804.             UI.CreateLabel(ref container, UINAME_PRICE, _configData.ui.priceTextColor, _instance.Lang("Price", player.UserIDString), _configData.ui.priceTextSize, _configData.ui.priceTextAnchorMin, _configData.ui.priceTextAnchorMax, TextAnchor.MiddleLeft);
  805.             if (price == null || price.Count == 0)
  806.             {
  807.                 UI.CreateLabel(ref container, UINAME_PRICE, _configData.ui.price2TextColor, _instance.Lang("Free", player.UserIDString), _configData.ui.price2TextSize, _configData.ui.price2TextAnchorMin, _configData.ui.price2TextAnchorMax, TextAnchor.MiddleLeft);
  808.             }
  809.             else
  810.             {
  811.                 var anchorMin = _configData.ui.Price2TextAnchorMin;
  812.                 var anchorMax = _configData.ui.Price2TextAnchorMax;
  813.                 var x = (anchorMax.y - anchorMin.y) / price.Count;
  814.                 var textSize = _configData.ui.price2TextSize - price.Count;
  815.                 var language = _instance.lang.GetLanguage(player.UserIDString);
  816.  
  817.                 var i = 0;
  818.                 foreach (var entry in price)
  819.                 {
  820.                     var externalItemInfo = info?.Price[entry.Key];
  821.                     var displayText = !externalItemInfo.HasValue || string.IsNullOrEmpty(externalItemInfo.Value.DisplayName.Value)
  822.                             ? $"{GetDisplayNameByCurrencyName(language, entry.Key, entry.Value.SkinId)}  <color=#00B5FF>x{entry.Value.Amount}</color>"
  823.                             : $"{externalItemInfo.Value.DisplayName.Value} x{externalItemInfo.Value.Amount.Value}";
  824.  
  825.                     UI.CreateLabel(ref container, UINAME_PRICE, _configData.ui.price2TextColor, displayText, textSize, $"{anchorMin.x} {anchorMin.y + i * x}", $"{anchorMax.x} {anchorMin.y + (i + 1) * x}", TextAnchor.MiddleLeft);
  826.                     if (_configData.ui.imageEnabled)
  827.                     {
  828.                         var imageAnchorMin = $"{anchorMax.x - _configData.ui.rightDistance - x * _configData.ui.imageScale} {anchorMin.y + i * x}";
  829.                         var imageAnchorMax = $"{anchorMax.x - _configData.ui.rightDistance} {anchorMin.y + (i + 1) * x}";
  830.                         if (externalItemInfo.HasValue && !string.IsNullOrEmpty(externalItemInfo.Value.ImageId.Value))
  831.                         {
  832.                             var image = externalItemInfo.Value.ImageId.Value;
  833.                             if (!string.IsNullOrEmpty(image))
  834.                             {
  835.                                 UI.CreateImage(ref container, UINAME_PRICE, image, imageAnchorMin, imageAnchorMax);
  836.                             }
  837.                         }
  838.                         else
  839.                         {
  840.                             int itemId;
  841.                             if (_instance._itemShortNameToItemId.TryGetValue(entry.Key, out itemId))
  842.                             {
  843.                                 UI.CreateImage(ref container, UINAME_PRICE, itemId, entry.Value.SkinId >= 0 ? (ulong)entry.Value.SkinId : 0, imageAnchorMin, imageAnchorMax);
  844.                             }
  845.                             else
  846.                             {
  847.                                 var image = GetItemImage(entry.Key);
  848.                                 if (!string.IsNullOrEmpty(image))
  849.                                 {
  850.                                     UI.CreateImage(ref container, UINAME_PRICE, image, imageAnchorMin, imageAnchorMax);
  851.                                 }
  852.                             }
  853.                         }
  854.                     }
  855.                     i++;
  856.                 }
  857.             }
  858.             CuiHelper.DestroyUi(player, UINAME_PRICE);
  859.             CuiHelper.AddUi(player, container);
  860.         }
  861.  
  862.         private static void UpdateRefundUI(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info, bool useRefund)
  863.         {
  864.             Dictionary<string, CurrencyInfo> refund = null;
  865.             if (useRefund)
  866.             {
  867.                 refund = _instance.GetRefund(targetEntity, info);
  868.             }
  869.             var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_REFUND, _configData.ui.refundBackgroundColor, _configData.ui.refundAnchorMin, _configData.ui.refundAnchorMax);
  870.             UI.CreateLabel(ref container, UINAME_REFUND, _configData.ui.refundTextColor, _instance.Lang("Refund", player.UserIDString), _configData.ui.refundTextSize, _configData.ui.refundTextAnchorMin, _configData.ui.refundTextAnchorMax, TextAnchor.MiddleLeft);
  871.  
  872.             if (refund == null || refund.Count == 0)
  873.             {
  874.                 UI.CreateLabel(ref container, UINAME_REFUND, _configData.ui.refund2TextColor, _instance.Lang("Nothing", player.UserIDString), _configData.ui.refund2TextSize, _configData.ui.refund2TextAnchorMin, _configData.ui.refund2TextAnchorMax, TextAnchor.MiddleLeft);
  875.             }
  876.             else
  877.             {
  878.                 var anchorMin = _configData.ui.Refund2TextAnchorMin;
  879.                 var anchorMax = _configData.ui.Refund2TextAnchorMax;
  880.                 var x = (anchorMax.y - anchorMin.y) / refund.Count;
  881.                 var textSize = _configData.ui.refund2TextSize - refund.Count;
  882.                 var language = _instance.lang.GetLanguage(player.UserIDString);
  883.  
  884.                 var i = 0;
  885.                 foreach (var entry in refund)
  886.                 {
  887.                     var externalItemInfo = info?.Refund[entry.Key];
  888.                     var displayText = !externalItemInfo.HasValue || string.IsNullOrEmpty(externalItemInfo.Value.DisplayName.Value)
  889.                             ? $"{GetDisplayNameByCurrencyName(language, entry.Key, entry.Value.SkinId)}  <color=#00B5FF>x{entry.Value.Amount}</color>"
  890.                             : $"{externalItemInfo.Value.DisplayName.Value} x{externalItemInfo.Value.Amount.Value}";
  891.  
  892.                     UI.CreateLabel(ref container, UINAME_REFUND, _configData.ui.refund2TextColor, displayText, textSize, $"{anchorMin.x} {anchorMin.y + i * x}", $"{anchorMax.x} {anchorMin.y + (i + 1) * x}", TextAnchor.MiddleLeft);
  893.                     if (_configData.ui.imageEnabled)
  894.                     {
  895.                         var imageAnchorMin = $"{anchorMax.x - _configData.ui.rightDistance - x * _configData.ui.imageScale} {anchorMin.y + i * x}";
  896.                         var imageAnchorMax = $"{anchorMax.x - _configData.ui.rightDistance} {anchorMin.y + (i + 1) * x}";
  897.                         if (externalItemInfo.HasValue && !string.IsNullOrEmpty(externalItemInfo.Value.ImageId.Value))
  898.                         {
  899.                             var image = externalItemInfo.Value.ImageId.Value;
  900.                             if (!string.IsNullOrEmpty(image))
  901.                             {
  902.                                 UI.CreateImage(ref container, UINAME_REFUND, image, imageAnchorMin, imageAnchorMax);
  903.                             }
  904.                         }
  905.                         else
  906.                         {
  907.                             int itemId;
  908.                             if (_instance._itemShortNameToItemId.TryGetValue(entry.Key, out itemId))
  909.                             {
  910.                                 UI.CreateImage(ref container, UINAME_REFUND, itemId, entry.Value.SkinId >= 0 ? (ulong)entry.Value.SkinId : 0, imageAnchorMin, imageAnchorMax);
  911.                             }
  912.                             else
  913.                             {
  914.                                 var image = GetItemImage(entry.Key);
  915.                                 if (!string.IsNullOrEmpty(image))
  916.                                 {
  917.                                     UI.CreateImage(ref container, UINAME_REFUND, image, imageAnchorMin, imageAnchorMax);
  918.                                 }
  919.                             }
  920.                         }
  921.                     }
  922.                     i++;
  923.                 }
  924.             }
  925.             CuiHelper.DestroyUi(player, UINAME_REFUND);
  926.             CuiHelper.AddUi(player, container);
  927.         }
  928.  
  929.         private static void UpdateAuthorizationUI(BasePlayer player, RemoveType removeType, BaseEntity targetEntity, RemovableEntityInfo? info, bool shouldPay)
  930.         {
  931.             string reason;
  932.             var color = _instance.CanRemoveEntity(player, removeType, targetEntity, info, shouldPay, out reason) ? _configData.ui.allowedBackgroundColor : _configData.ui.refusedBackgroundColor;
  933.             var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_AUTH, color, _configData.ui.authorizationsAnchorMin, _configData.ui.authorizationsAnchorMax);
  934.             UI.CreateLabel(ref container, UINAME_AUTH, _configData.ui.authorizationsTextColor, reason, _configData.ui.authorizationsTextSize, _configData.ui.authorizationsTextAnchorMin, _configData.ui.authorizationsTextAnchorMax, TextAnchor.MiddleLeft);
  935.             CuiHelper.DestroyUi(player, UINAME_AUTH);
  936.             CuiHelper.AddUi(player, container);
  937.         }
  938.  
  939.         private static void DestroyAllUI(BasePlayer player)
  940.         {
  941.             CuiHelper.DestroyUi(player, UINAME_CROSSHAIR);
  942.             CuiHelper.DestroyUi(player, UINAME_MAIN);
  943.         }
  944.  
  945.         private static void DestroyUiEntry(BasePlayer player, UiEntry uiEntry)
  946.         {
  947.             switch (uiEntry)
  948.             {
  949.                 case UiEntry.Entity:
  950.                     CuiHelper.DestroyUi(player, UINAME_ENTITY);
  951.                     return;
  952.  
  953.                 case UiEntry.Price:
  954.                     CuiHelper.DestroyUi(player, UINAME_PRICE);
  955.                     return;
  956.  
  957.                 case UiEntry.Refund:
  958.                     CuiHelper.DestroyUi(player, UINAME_REFUND);
  959.                     return;
  960.  
  961.                 case UiEntry.Auth:
  962.                     CuiHelper.DestroyUi(player, UINAME_AUTH);
  963.                     return;
  964.             }
  965.         }
  966.  
  967.         #endregion UI
  968.  
  969.         #region ToolRemover Component
  970.  
  971.         #region Tool Helpers
  972.  
  973.         private static bool IsSpecificTool(BasePlayer player)
  974.         {
  975.             var heldItem = player.GetActiveItem();
  976.             return IsSpecificTool(heldItem);
  977.         }
  978.  
  979.         private static bool IsSpecificTool(Item heldItem)
  980.         {
  981.             if (heldItem != null && heldItem.info.shortname == _configData.removerMode.specificToolShortName)
  982.             {
  983.                 if (_configData.removerMode.specificToolSkin < 0)
  984.                 {
  985.                     return true;
  986.                 }
  987.                 return heldItem.skin == (ulong)_configData.removerMode.specificToolSkin;
  988.             }
  989.             return false;
  990.         }
  991.  
  992.         private static bool IsMeleeTool(BasePlayer player)
  993.         {
  994.             var heldItem = player.GetActiveItem();
  995.             return IsMeleeTool(heldItem);
  996.         }
  997.  
  998.         private static bool IsMeleeTool(Item heldItem)
  999.         {
  1000.             if (heldItem != null && heldItem.info.shortname == _configData.removerMode.meleeHitItemShortname)
  1001.             {
  1002.                 if (_configData.removerMode.meleeHitModeSkin < 0)
  1003.                 {
  1004.                     return true;
  1005.                 }
  1006.                 return heldItem.skin == (ulong)_configData.removerMode.meleeHitModeSkin;
  1007.             }
  1008.             return false;
  1009.         }
  1010.  
  1011.         #endregion Tool Helpers
  1012.  
  1013.         private class ToolRemover : FacepunchBehaviour
  1014.         {
  1015.             private const float MinInterval = 0.2f;
  1016.  
  1017.             public int CurrentRemoved { get; set; }
  1018.             public BaseEntity HitEntity { get; set; }
  1019.             public bool CanOverride { get; private set; }
  1020.             public BasePlayer Player { get; private set; }
  1021.             public RemoveType RemoveType { get; private set; }
  1022.  
  1023.             private bool _resetTime;
  1024.             private bool _shouldPay;
  1025.             private bool _shouldRefund;
  1026.             private int _removeTime;
  1027.             private int _maxRemovable;
  1028.             private float _distance;
  1029.             private float _removeInterval;
  1030.  
  1031.             private int _timeLeft;
  1032.             private float _lastRemove;
  1033.             private ItemId _currentItemId;
  1034.             private bool _disableInHand;
  1035.  
  1036.             private Item _lastHeldItem;
  1037.             private BaseEntity _targetEntity;
  1038.             private UiEntry _activeUiEntries;
  1039.  
  1040.             private void Awake()
  1041.             {
  1042.                 Player = GetComponent<BasePlayer>();
  1043.                 _currentItemId = Player.svActiveItemID;
  1044.                 _disableInHand = _removeMode == RemoveMode.MeleeHit && _configData.removerMode.meleeHitDisableInHand
  1045.                         || _removeMode == RemoveMode.SpecificTool && _configData.removerMode.specificToolDisableInHand;
  1046.                 if (_disableInHand)
  1047.                 {
  1048.                     _lastHeldItem = Player.GetActiveItem();
  1049.                 }
  1050.                 if (_removeMode == RemoveMode.NoHeld)
  1051.                 {
  1052.                     UnEquip();
  1053.                 }
  1054.             }
  1055.  
  1056.             public void Init(RemoveType removeType, int removeTime, int maxRemovable, float distance, float removeInterval, bool shouldPay, bool shouldRefund, bool resetTime, bool canOverride)
  1057.             {
  1058.                 RemoveType = removeType;
  1059.                 CanOverride = canOverride;
  1060.  
  1061.                 _distance = distance;
  1062.                 _resetTime = resetTime;
  1063.                 _removeTime = _timeLeft = removeTime;
  1064.                 _removeInterval = Mathf.Max(MinInterval, removeInterval);
  1065.                 if (RemoveType == RemoveType.Normal)
  1066.                 {
  1067.                     _maxRemovable = maxRemovable;
  1068.                     _shouldPay = shouldPay && _configData.remove.priceEnabled;
  1069.                     _shouldRefund = shouldRefund && _configData.remove.refundEnabled;
  1070.                     _instance.PrintDebug($"{Player.displayName}({Player.userID}) have Enabled the remover tool.");
  1071.                     Interface.CallHook("OnRemoverToolActivated", Player);
  1072.                 }
  1073.                 else
  1074.                 {
  1075.                     _maxRemovable = CurrentRemoved = 0;
  1076.                     _shouldPay = _shouldRefund = false;
  1077.                 }
  1078.  
  1079.                 DestroyAllUI(Player);
  1080.                 if (_configData.ui.showCrosshair)
  1081.                 {
  1082.                     CreateCrosshairUI(Player);
  1083.                 }
  1084.  
  1085.                 if (_configData.ui.enabled)
  1086.                 {
  1087.                     CreateMainUI(Player, RemoveType);
  1088.                 }
  1089.  
  1090.                 CancelInvoke(RemoveUpdate);
  1091.                 InvokeRepeating(RemoveUpdate, 0f, 1f);
  1092.             }
  1093.  
  1094.             private void RemoveUpdate()
  1095.             {
  1096.                 if (_configData.ui.enabled)
  1097.                 {
  1098.                     _targetEntity = GetTargetEntity();
  1099.                     UpdateTimeLeftUI(Player, RemoveType, _timeLeft, CurrentRemoved, _maxRemovable);
  1100.  
  1101.                     var info = RemoveType == RemoveType.Normal ? GetRemovableEntityInfo(_targetEntity, Player) : null;
  1102.                     var canShow = (info.HasValue || _targetEntity != null) && CanEntityBeDisplayed(_targetEntity, Player);
  1103.                     if (HandleUiEntry(UiEntry.Entity, canShow))
  1104.                     {
  1105.                         UpdateEntityUI(Player, _targetEntity, info);
  1106.                     }
  1107.                     if (RemoveType == RemoveType.Normal)
  1108.                     {
  1109.                         if (_configData.ui.authorizationEnabled)
  1110.                         {
  1111.                             if (HandleUiEntry(UiEntry.Auth, canShow))
  1112.                             {
  1113.                                 UpdateAuthorizationUI(Player, RemoveType, _targetEntity, info, _shouldPay);
  1114.                             }
  1115.                         }
  1116.                         if (_configData.ui.priceEnabled || _configData.ui.refundEnabled)
  1117.                         {
  1118.                             canShow = canShow && (info.HasValue || HasEntityEnabled(_targetEntity));
  1119.                             if (_configData.ui.priceEnabled)
  1120.                             {
  1121.                                 if (HandleUiEntry(UiEntry.Price, canShow))
  1122.                                 {
  1123.                                     UpdatePriceUI(Player, _targetEntity, info, _shouldPay);
  1124.                                 }
  1125.                             }
  1126.                             if (_configData.ui.refundEnabled)
  1127.                             {
  1128.                                 if (HandleUiEntry(UiEntry.Refund, canShow))
  1129.                                 {
  1130.                                     UpdateRefundUI(Player, _targetEntity, info, _shouldRefund);
  1131.                                 }
  1132.                             }
  1133.                         }
  1134.                     }
  1135.                 }
  1136.  
  1137.                 if (_timeLeft-- <= 0)
  1138.                 {
  1139.                     DisableTool();
  1140.                 }
  1141.             }
  1142.  
  1143.             private BaseEntity GetTargetEntity()
  1144.             {
  1145.                 BaseEntity target = null;
  1146.                 List<RaycastHit> hitInfos = Pool.GetList<RaycastHit>();
  1147.                 GamePhysics.TraceAll(Player.eyes.HeadRay(), 0f, hitInfos, _distance, LAYER_TARGET);
  1148.                 foreach (var hitInfo in hitInfos)
  1149.                 {
  1150.                     var hitEntity = hitInfo.GetEntity();
  1151.                     if (hitEntity != null)
  1152.                     {
  1153.                         if (target == null)
  1154.                         {
  1155.                             target = hitEntity;
  1156.                         }
  1157.                         else if (hitEntity.GetParentEntity() == target)
  1158.                         {
  1159.                             target = hitEntity;
  1160.                             break;
  1161.                         }
  1162.                     }
  1163.                 }
  1164.                 Pool.FreeUnmanaged(ref hitInfos);
  1165.                 return target;
  1166.                 // RaycastHit hitInfo;
  1167.                 // if (Physics.Raycast(Player.eyes.HeadRay(), out hitInfo, _distance, LAYER_TARGET))
  1168.                 // {
  1169.                 //     return hitInfo.GetEntity();
  1170.                 // }
  1171.                 // return null;
  1172.             }
  1173.  
  1174.             private void Update()
  1175.             {
  1176.                 if (Player == null || !Player.IsConnected || !Player.CanInteract())
  1177.                 {
  1178.                     DisableTool();
  1179.                     return;
  1180.                 }
  1181.                 if (Player.svActiveItemID != _currentItemId)
  1182.                 {
  1183.                     if (_disableInHand)
  1184.                     {
  1185.                         var heldItem = Player.GetActiveItem();
  1186.                         if (_removeMode == RemoveMode.MeleeHit && IsMeleeTool(_lastHeldItem) && !IsMeleeTool(heldItem) ||
  1187.                                 _removeMode == RemoveMode.SpecificTool && IsSpecificTool(_lastHeldItem) && !IsSpecificTool(heldItem))
  1188.                         {
  1189.                             DisableTool();
  1190.                             return;
  1191.                         }
  1192.                         _lastHeldItem = heldItem;
  1193.                     }
  1194.                     if (_removeMode == RemoveMode.NoHeld)
  1195.                     {
  1196.                         if (Player.svActiveItemID.IsValid)
  1197.                         {
  1198.                             if (_configData.removerMode.noHeldDisableInHand)
  1199.                             {
  1200.                                 DisableTool();
  1201.                                 return;
  1202.                             }
  1203.                             UnEquip();
  1204.                         }
  1205.                     }
  1206.                     _currentItemId = Player.svActiveItemID;
  1207.                 }
  1208.                 if (Time.realtimeSinceStartup - _lastRemove >= _removeInterval)
  1209.                 {
  1210.                     if (_removeMode == RemoveMode.MeleeHit)
  1211.                     {
  1212.                         if (HitEntity == null)
  1213.                         {
  1214.                             return;
  1215.                         }
  1216.                         _targetEntity = HitEntity;
  1217.                         HitEntity = null;
  1218.                     }
  1219.                     else
  1220.                     {
  1221.                         if (!Player.serverInput.IsDown(_removeButton))
  1222.                         {
  1223.                             return;
  1224.                         }
  1225.                         if (_removeMode == RemoveMode.SpecificTool && !IsSpecificTool(Player))
  1226.                         {
  1227.                             //rt.Print(player,rt.Lang("UsageOfRemove",player.UserIDString));
  1228.                             return;
  1229.                         }
  1230.                         _targetEntity = GetTargetEntity();
  1231.                     }
  1232.                     if (_instance.TryRemove(Player, _targetEntity, RemoveType, _shouldPay, _shouldRefund))
  1233.                     {
  1234.                         if (_resetTime)
  1235.                         {
  1236.                             _timeLeft = _removeTime;
  1237.                         }
  1238.                         if (RemoveType == RemoveType.Normal || RemoveType == RemoveType.Admin)
  1239.                         {
  1240.                             CurrentRemoved++;
  1241.                         }
  1242.                         if (_configData.global.startCooldownOnRemoved && RemoveType == RemoveType.Normal)
  1243.                         {
  1244.                             _instance._cooldownTimes[Player.userID] = Time.realtimeSinceStartup;
  1245.                         }
  1246.                     }
  1247.                     _lastRemove = Time.realtimeSinceStartup;
  1248.                 }
  1249.                 if (RemoveType == RemoveType.Normal && _maxRemovable > 0 && CurrentRemoved >= _maxRemovable)
  1250.                 {
  1251.                     _instance.Print(Player, _instance.Lang("EntityLimit", Player.UserIDString, _maxRemovable));
  1252.                     DisableTool(false);
  1253.                 }
  1254.                 ;
  1255.             }
  1256.  
  1257.             private void UnEquip()
  1258.             {
  1259.                 // Player.lastReceivedTick.activeItem = 0;
  1260.                 var activeItem = Player.GetActiveItem();
  1261.                 if (activeItem?.GetHeldEntity() is HeldEntity)
  1262.                 {
  1263.                     var slot = activeItem.position;
  1264.                     activeItem.SetParent(null);
  1265.                     Player.Invoke(() =>
  1266.                     {
  1267.                         if (activeItem == null || !activeItem.IsValid())
  1268.                         {
  1269.                             return;
  1270.                         }
  1271.                         if (Player.inventory.containerBelt.GetSlot(slot) == null)
  1272.                         {
  1273.                             activeItem.position = slot;
  1274.                             activeItem.SetParent(Player.inventory.containerBelt);
  1275.                         }
  1276.                         else
  1277.                         {
  1278.                             Player.GiveItem(activeItem);
  1279.                         }
  1280.                     }, 0.2f);
  1281.                 }
  1282.             }
  1283.  
  1284.             private bool HandleUiEntry(UiEntry uiEntry, bool canShow)
  1285.             {
  1286.                 if (canShow)
  1287.                 {
  1288.                     _activeUiEntries |= uiEntry;
  1289.                     return true;
  1290.                 }
  1291.  
  1292.                 if (_activeUiEntries.HasFlag(uiEntry))
  1293.                 {
  1294.                     _activeUiEntries &= ~uiEntry;
  1295.                     DestroyUiEntry(Player, uiEntry);
  1296.                 }
  1297.                 return false;
  1298.             }
  1299.  
  1300.             public void DisableTool(bool showMessage = true)
  1301.             {
  1302.                 if (showMessage)
  1303.                 {
  1304.                     if (_instance != null && Player != null && Player.IsConnected)
  1305.                     {
  1306.                         if (_configData != null && _configData.chat.showMessageWhenEnabledOrDisabled)
  1307.                         {
  1308.                             _instance.Print(Player, _instance.Lang("ToolDisabled", Player.UserIDString));
  1309.                         }
  1310.                     }
  1311.                 }
  1312.  
  1313.                 if (RemoveType == RemoveType.Normal)
  1314.                 {
  1315.                     if (_instance != null && Player != null)
  1316.                     {
  1317.                         _instance.PrintDebug($"{Player.displayName}({Player.userID}) have Disabled the remover tool.");
  1318.                     }
  1319.                     Interface.CallHook("OnRemoverToolDeactivated", Player);
  1320.                 }
  1321.                 DestroyAllUI(Player);
  1322.                 Destroy(this);
  1323.             }
  1324.  
  1325.             private void OnDestroy()
  1326.             {
  1327.                 if (_instance != null && RemoveType == RemoveType.Normal)
  1328.                 {
  1329.                     if (_configData != null && !_configData.global.startCooldownOnRemoved)
  1330.                     {
  1331.                         _instance._cooldownTimes[Player.userID] = Time.realtimeSinceStartup;
  1332.                     }
  1333.                 }
  1334.             }
  1335.         }
  1336.  
  1337.         #endregion ToolRemover Component
  1338.  
  1339.         #region TryRemove
  1340.  
  1341.         private bool TryRemove(BasePlayer player, BaseEntity targetEntity, RemoveType removeType, bool shouldPay, bool shouldRefund)
  1342.         {
  1343.             if (targetEntity == null)
  1344.             {
  1345.                 Print(player, Lang("NotFoundOrFar", player.UserIDString));
  1346.                 return false;
  1347.             }
  1348.             if (targetEntity.IsDestroyed)
  1349.             {
  1350.                 Print(player, Lang("InvalidEntity", player.UserIDString));
  1351.                 return false;
  1352.             }
  1353.             if (removeType != RemoveType.Normal)
  1354.             {
  1355.                 var result = Interface.CallHook("CanAdminRemove", player, targetEntity, removeType.ToString());
  1356.                 if (result != null)
  1357.                 {
  1358.                     Print(player, result is string ? (string)result : Lang("BeBlocked", player.UserIDString));
  1359.                     return false;
  1360.                 }
  1361.                 switch (removeType)
  1362.                 {
  1363.                     case RemoveType.Admin:
  1364.                         {
  1365.                             var target = targetEntity as BasePlayer;
  1366.                             if (target != null)
  1367.                             {
  1368.                                 if (target.userID.IsSteamId() && target.IsConnected)
  1369.                                 {
  1370.                                     target.Kick("From RemoverTool Plugin");
  1371.                                     return true;
  1372.                                 }
  1373.                             }
  1374.                             DoRemove(targetEntity, _configData.removeType[RemoveType.Admin].gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None);
  1375.                             return true;
  1376.                         }
  1377.                     case RemoveType.All:
  1378.                         {
  1379.                             if (_removeAllCoroutine != null)
  1380.                             {
  1381.                                 Print(player, Lang("AlreadyRemoveAll", player.UserIDString));
  1382.                                 return false;
  1383.                             }
  1384.                             _removeAllCoroutine = ServerMgr.Instance.StartCoroutine(RemoveAll(targetEntity, player));
  1385.                             Print(player, Lang("StartRemoveAll", player.UserIDString));
  1386.                             return true;
  1387.                         }
  1388.                     case RemoveType.External:
  1389.                         {
  1390.                             var stabilityEntity = targetEntity as StabilityEntity;
  1391.                             if (stabilityEntity == null || !IsExternalWall(stabilityEntity))
  1392.                             {
  1393.                                 Print(player, Lang("NotExternalWall", player.UserIDString));
  1394.                                 return false;
  1395.                             }
  1396.                             if (_removeExternalCoroutine != null)
  1397.                             {
  1398.                                 Print(player, Lang("AlreadyRemoveExternal", player.UserIDString));
  1399.                                 return false;
  1400.                             }
  1401.                             _removeExternalCoroutine = ServerMgr.Instance.StartCoroutine(RemoveExternal(stabilityEntity, player));
  1402.                             Print(player, Lang("StartRemoveExternal", player.UserIDString));
  1403.                             return true;
  1404.                         }
  1405.                     case RemoveType.Structure:
  1406.                         {
  1407.                             var decayEntity = targetEntity as DecayEntity;
  1408.                             if (decayEntity == null)
  1409.                             {
  1410.                                 Print(player, Lang("NotStructure", player.UserIDString));
  1411.                                 return false;
  1412.                             }
  1413.                             if (_removeStructureCoroutine != null)
  1414.                             {
  1415.                                 Print(player, Lang("AlreadyRemoveStructure", player.UserIDString));
  1416.                                 return false;
  1417.                             }
  1418.                             _removeStructureCoroutine = ServerMgr.Instance.StartCoroutine(RemoveStructure(decayEntity, player));
  1419.                             Print(player, Lang("StartRemoveStructure", player.UserIDString));
  1420.                             return true;
  1421.                         }
  1422.                 }
  1423.             }
  1424.  
  1425.             var info = GetRemovableEntityInfo(targetEntity, player);
  1426.  
  1427.             string reason;
  1428.             if (!CanRemoveEntity(player, removeType, targetEntity, info, shouldPay, out reason))
  1429.             {
  1430.                 Print(player, reason);
  1431.                 return false;
  1432.             }
  1433.  
  1434.             DropContainerEntity(targetEntity);
  1435.  
  1436.             if (shouldPay)
  1437.             {
  1438.                 var flag = TryPay(player, targetEntity, info);
  1439.                 if (!flag)
  1440.                 {
  1441.                     Print(player, Lang("CantPay", player.UserIDString));
  1442.                     return false;
  1443.                 }
  1444.             }
  1445.  
  1446.             if (shouldRefund)
  1447.             {
  1448.                 GiveRefund(player, targetEntity, info);
  1449.             }
  1450.  
  1451.             DoNormalRemove(player, targetEntity, _configData.removeType[RemoveType.Normal].gibs);
  1452.             return true;
  1453.         }
  1454.  
  1455.         private bool CanRemoveEntity(BasePlayer player, RemoveType removeType, BaseEntity targetEntity, RemovableEntityInfo? info, bool shouldPay, out string reason)
  1456.         {
  1457.             if (removeType != RemoveType.Normal)
  1458.             {
  1459.                 reason = null;
  1460.                 return true;
  1461.             }
  1462.             if (targetEntity == null || !CanEntityBeDisplayed(targetEntity, player))
  1463.             {
  1464.                 reason = Lang("NotFoundOrFar", player.UserIDString);
  1465.                 return false;
  1466.             }
  1467.             if (targetEntity.IsDestroyed)
  1468.             {
  1469.                 reason = Lang("InvalidEntity", player.UserIDString);
  1470.                 return false;
  1471.             }
  1472.             if (!info.HasValue)
  1473.             {
  1474.                 if (!IsRemovableEntity(targetEntity))
  1475.                 {
  1476.                     reason = Lang("InvalidEntity", player.UserIDString);
  1477.                     return false;
  1478.                 }
  1479.                 if (!HasEntityEnabled(targetEntity))
  1480.                 {
  1481.                     reason = Lang("EntityDisabled", player.UserIDString);
  1482.                     return false;
  1483.                 }
  1484.             }
  1485.             var result = Interface.CallHook("canRemove", player, targetEntity);
  1486.             if (result != null)
  1487.             {
  1488.                 reason = result is string ? (string)result : Lang("BeBlocked", player.UserIDString);
  1489.                 return false;
  1490.             }
  1491.             if (!_configData.damagedEntity.enabled && IsDamagedEntity(targetEntity))
  1492.             {
  1493.                 reason = Lang("DamagedEntity", player.UserIDString);
  1494.                 return false;
  1495.             }
  1496.             if (IsPlayerBlocked(player, out reason))
  1497.             {
  1498.                 return false;
  1499.             }
  1500.             if (_configData.global.entityTimeLimit && IsEntityTimeLimit(targetEntity))
  1501.             {
  1502.                 reason = Lang("EntityTimeLimit", player.UserIDString, _configData.global.limitTime);
  1503.                 return false;
  1504.             }
  1505.             if (!_configData.container.removeNotEmptyStorage)
  1506.             {
  1507.                 var storageContainer = targetEntity as StorageContainer;
  1508.                 if (storageContainer != null && storageContainer.inventory?.itemList?.Count > 0)
  1509.                 {
  1510.                     reason = Lang("StorageNotEmpty", player.UserIDString);
  1511.                     return false;
  1512.                 }
  1513.             }
  1514.             if (!_configData.container.removeNotEmptyIoEntity)
  1515.             {
  1516.                 var containerIOEntity = targetEntity as ContainerIOEntity;
  1517.                 if (containerIOEntity != null && containerIOEntity.inventory?.itemList?.Count > 0)
  1518.                 {
  1519.                     reason = Lang("StorageNotEmpty", player.UserIDString);
  1520.                     return false;
  1521.                 }
  1522.             }
  1523.             if (shouldPay && !CanPay(player, targetEntity, info))
  1524.             {
  1525.                 reason = Lang("NotEnoughCost", player.UserIDString);
  1526.                 return false;
  1527.             }
  1528.             if (!HasAccess(player, targetEntity))
  1529.             {
  1530.                 reason = Lang("NotRemoveAccess", player.UserIDString);
  1531.                 return false;
  1532.             }
  1533.             reason = Lang("CanRemove", player.UserIDString);
  1534.             return true;
  1535.         }
  1536.  
  1537.         private bool HasAccess(BasePlayer player, BaseEntity targetEntity)
  1538.         {
  1539.             if (_configData.global.useBuildingOwners && BuildingOwners != null)
  1540.             {
  1541.                 var buildingBlock = targetEntity as BuildingBlock;
  1542.                 if (buildingBlock != null)
  1543.                 {
  1544.                     var result = BuildingOwners?.Call("FindBlockData", buildingBlock) as string;
  1545.                     if (result != null)
  1546.                     {
  1547.                         var ownerID = ulong.Parse(result);
  1548.                         if (AreFriends(ownerID, player.userID))
  1549.                         {
  1550.                             return true;
  1551.                         }
  1552.                     }
  1553.                 }
  1554.             }
  1555.             //var 1 = configData.globalS.excludeTwigs && (targetEntity as BuildingBlock)?.grade == BuildingGrade.Enum.Twigs;
  1556.             if (_configData.global.useEntityOwners)
  1557.             {
  1558.                 if (AreFriends(targetEntity.OwnerID, player.userID))
  1559.                 {
  1560.                     if (!_configData.global.useToolCupboards)
  1561.                     {
  1562.                         return true;
  1563.                     }
  1564.                     if (HasTotalAccess(player, targetEntity))
  1565.                     {
  1566.                         return true;
  1567.                     }
  1568.                 }
  1569.  
  1570.                 return false;
  1571.             }
  1572.             if (_configData.global.useToolCupboards)
  1573.             {
  1574.                 if (HasTotalAccess(player, targetEntity))
  1575.                 {
  1576.                     return true;
  1577.                 }
  1578.             }
  1579.             return false;
  1580.         }
  1581.  
  1582.         private static bool HasTotalAccess(BasePlayer player, BaseEntity targetEntity)
  1583.         {
  1584.             if (player.IsBuildingBlocked(targetEntity.WorldSpaceBounds()))
  1585.             {
  1586.                 return false;
  1587.             }
  1588.             if (_configData.global.useBuildingLocks && !CanOpenAllLocks(player, targetEntity))
  1589.             {
  1590.                 //reason = Lang("Can'tOpenAllLocks", player.UserIDString);
  1591.                 return false;
  1592.             }
  1593.             return true;
  1594.         }
  1595.  
  1596.         private static bool CanOpenAllLocks(BasePlayer player, BaseEntity targetEntity)
  1597.         {
  1598.             var decayEntities = Pool.GetList<DecayEntity>();
  1599.             var building = targetEntity.GetBuildingPrivilege()?.GetBuilding() ?? (targetEntity as DecayEntity)?.GetBuilding();
  1600.             if (building != null)
  1601.             {
  1602.                 decayEntities.AddRange(building.decayEntities);
  1603.             }
  1604.             /*else//An entity placed outside
  1605.             {
  1606.                 Vis.Entities(targetEntity.transform.position, 9f, decayEntities, Layers.Mask.Construction | Layers.Mask.Deployed);
  1607.             }*/
  1608.             foreach (var decayEntity in decayEntities)
  1609.             {
  1610.                 if ((decayEntity is Door || decayEntity is BoxStorage) && decayEntity.OwnerID.IsSteamId())
  1611.                 {
  1612.                     var lockEntity = decayEntity.GetSlot(BaseEntity.Slot.Lock) as BaseLock;
  1613.                     if (lockEntity != null && !OnTryToOpen(player, lockEntity))
  1614.                     {
  1615.                         Pool.FreeList(ref decayEntities);
  1616.                         return false;
  1617.                     }
  1618.                 }
  1619.             }
  1620.             Pool.FreeUnmanaged(ref decayEntities);
  1621.             return true;
  1622.         }
  1623.  
  1624.         private static bool OnTryToOpen(BasePlayer player, BaseLock baseLock)
  1625.         {
  1626.             var codeLock = baseLock as CodeLock;
  1627.             if (codeLock != null)
  1628.             {
  1629.                 var obj = Interface.CallHook("CanUseLockedEntity", player, codeLock);
  1630.                 if (obj is bool)
  1631.                 {
  1632.                     return (bool)obj;
  1633.                 }
  1634.                 if (!codeLock.IsLocked())
  1635.                 {
  1636.                     return true;
  1637.                 }
  1638.                 // Make no sound during the check
  1639.                 if (codeLock.whitelistPlayers.Contains(player.userID) || codeLock.guestPlayers.Contains(player.userID))
  1640.                 {
  1641.                     return true;
  1642.                 }
  1643.                 return false;
  1644.             }
  1645.             var keyLock = baseLock as KeyLock;
  1646.             if (keyLock != null)
  1647.             {
  1648.                 return keyLock.OnTryToOpen(player);
  1649.             }
  1650.  
  1651.             return false;
  1652.         }
  1653.  
  1654.         private static bool IsDamagedEntity(BaseEntity entity)
  1655.         {
  1656.             var baseCombatEntity = entity as BaseCombatEntity;
  1657.             if (baseCombatEntity == null || !baseCombatEntity.repair.enabled)
  1658.             {
  1659.                 return false;
  1660.             }
  1661.             if (_configData.damagedEntity.excludeBuildingBlocks && (baseCombatEntity is BuildingBlock || baseCombatEntity is SimpleBuildingBlock))
  1662.             {
  1663.                 return false;
  1664.             }
  1665.             if (_configData.damagedEntity.excludeQuarries && !(baseCombatEntity is BuildingBlock))
  1666.             {
  1667.                 // Quarries
  1668.                 if (baseCombatEntity.repair.itemTarget == null || baseCombatEntity.repair.itemTarget.Blueprint == null)
  1669.                 {
  1670.                     return false;
  1671.                 }
  1672.             }
  1673.  
  1674.             if (baseCombatEntity.healthFraction * 100f >= _configData.damagedEntity.percentage)
  1675.             {
  1676.                 return false;
  1677.             }
  1678.             return true;
  1679.         }
  1680.  
  1681.         private static bool IsEntityTimeLimit(BaseEntity entity)
  1682.         {
  1683.             if (entity.net == null)
  1684.             {
  1685.                 return true;
  1686.             }
  1687.             float spawnedTime;
  1688.             if (_instance._entitySpawnedTimes.TryGetValue(entity.net.ID.Value, out spawnedTime))
  1689.             {
  1690.                 return Time.realtimeSinceStartup - spawnedTime > _configData.global.limitTime;
  1691.             }
  1692.             return true;
  1693.         }
  1694.  
  1695.         private static void DropContainerEntity(BaseEntity targetEntity)
  1696.         {
  1697.             var itemContainerEntity = targetEntity as IItemContainerEntity;
  1698.             if (itemContainerEntity != null && itemContainerEntity.inventory?.itemList?.Count > 0)
  1699.             {
  1700.                 bool dropContainer = false, dropItems = false;
  1701.                 var storageContainer = targetEntity as StorageContainer;
  1702.                 if (storageContainer != null)
  1703.                 {
  1704.                     dropContainer = _configData.container.dropContainerStorage;
  1705.                     dropItems = _configData.container.dropItemsStorage;
  1706.                 }
  1707.                 else
  1708.                 {
  1709.                     var containerIoEntity = targetEntity as ContainerIOEntity;
  1710.                     if (containerIoEntity != null)
  1711.                     {
  1712.                         dropContainer = _configData.container.dropContainerIoEntity;
  1713.                         dropItems = _configData.container.dropItemsIoEntity;
  1714.                     }
  1715.                 }
  1716.                 if (dropContainer || dropItems)
  1717.                 {
  1718.                     if (Interface.CallHook("OnDropContainerEntity", targetEntity) == null)
  1719.                     {
  1720.                         if (dropContainer)
  1721.                         {
  1722.                             DropItemContainer(itemContainerEntity.inventory, itemContainerEntity.GetDropPosition(), itemContainerEntity.Transform.rotation);
  1723.                         }
  1724.                         else if (dropItems)
  1725.                         {
  1726.                             itemContainerEntity.DropItems();
  1727.                         }
  1728.                     }
  1729.                 }
  1730.             }
  1731.         }
  1732.  
  1733.         #region AreFriends
  1734.  
  1735.         private bool AreFriends(ulong playerID, ulong friendID)
  1736.         {
  1737.             if (!playerID.IsSteamId())
  1738.             {
  1739.                 return false;
  1740.             }
  1741.             if (playerID == friendID)
  1742.             {
  1743.                 return true;
  1744.             }
  1745.             if (_configData.global.useTeams && SameTeam(playerID, friendID))
  1746.             {
  1747.                 return true;
  1748.             }
  1749.             if (_configData.global.useFriends && HasFriend(playerID, friendID))
  1750.             {
  1751.                 return true;
  1752.             }
  1753.             if (_configData.global.useClans && SameClan(playerID, friendID))
  1754.             {
  1755.                 return true;
  1756.             }
  1757.             return false;
  1758.         }
  1759.  
  1760.         private static bool SameTeam(ulong playerID, ulong friendID)
  1761.         {
  1762.             if (!RelationshipManager.TeamsEnabled())
  1763.             {
  1764.                 return false;
  1765.             }
  1766.             var playerTeam = RelationshipManager.ServerInstance.FindPlayersTeam(playerID);
  1767.             if (playerTeam == null)
  1768.             {
  1769.                 return false;
  1770.             }
  1771.             var friendTeam = RelationshipManager.ServerInstance.FindPlayersTeam(friendID);
  1772.             if (friendTeam == null)
  1773.             {
  1774.                 return false;
  1775.             }
  1776.             return playerTeam == friendTeam;
  1777.         }
  1778.  
  1779.         private bool HasFriend(ulong playerID, ulong friendID)
  1780.         {
  1781.             if (Friends == null)
  1782.             {
  1783.                 return false;
  1784.             }
  1785.             return (bool)Friends.Call("HasFriend", playerID, friendID);
  1786.         }
  1787.  
  1788.         private bool SameClan(ulong playerID, ulong friendID)
  1789.         {
  1790.             if (Clans == null)
  1791.             {
  1792.                 return false;
  1793.             }
  1794.             //Clans
  1795.             var isMember = Clans.Call("IsClanMember", playerID.ToString(), friendID.ToString());
  1796.             if (isMember != null)
  1797.             {
  1798.                 return (bool)isMember;
  1799.             }
  1800.             //Rust:IO Clans
  1801.             var playerClan = Clans.Call("GetClanOf", playerID);
  1802.             if (playerClan == null)
  1803.             {
  1804.                 return false;
  1805.             }
  1806.             var friendClan = Clans.Call("GetClanOf", friendID);
  1807.             if (friendClan == null)
  1808.             {
  1809.                 return false;
  1810.             }
  1811.             return (string)playerClan == (string)friendClan;
  1812.         }
  1813.  
  1814.         #endregion AreFriends
  1815.  
  1816.         #endregion TryRemove
  1817.  
  1818.         #region Pay
  1819.  
  1820.  
  1821.         private Dictionary<string, CurrencyInfo> GetPrice(BaseEntity targetEntity, RemovableEntityInfo? info)
  1822.         {
  1823.             if (info.HasValue)
  1824.             {
  1825.                 return info.Value.Price.ValueName2Currency;
  1826.             }
  1827.             var buildingBlock = targetEntity as BuildingBlock;
  1828.             if (buildingBlock != null)
  1829.             {
  1830.                 string entityName;
  1831.                
  1832.                 // Try case-sensitive lookup first
  1833.                 if (!_prefabNameToStructure.TryGetValue(buildingBlock.PrefabName, out entityName))
  1834.                 {
  1835.                     // If case-sensitive fails, try case-insensitive lookup
  1836.                     var kvp = _prefabNameToStructure.FirstOrDefault(x => string.Equals(x.Key, buildingBlock.PrefabName, StringComparison.OrdinalIgnoreCase));
  1837.                     if (!string.IsNullOrEmpty(kvp.Key))
  1838.                     {
  1839.                         entityName = kvp.Value;
  1840.                     }
  1841.                     else
  1842.                     {
  1843.                         return null;
  1844.                     }
  1845.                 }
  1846.  
  1847.                 BuildingBlocksSettings buildingBlockSettings;
  1848.                 if (_configData.remove.buildingBlock.TryGetValue(entityName, out buildingBlockSettings))
  1849.                 {
  1850.                     BuildingGradeSettings buildingGradeSettings;
  1851.                     if (buildingBlockSettings.buildingGrade.TryGetValue(buildingBlock.grade, out buildingGradeSettings))
  1852.                     {
  1853.                         if (buildingGradeSettings.priceDict != null)
  1854.                         {
  1855.                             return buildingGradeSettings.priceDict;
  1856.                         }
  1857.                         if (buildingGradeSettings.pricePercentage > 0f)
  1858.                         {
  1859.                             var currentGrade = buildingBlock.currentGrade;
  1860.                             if (currentGrade != null)
  1861.                             {
  1862.                                 var price = new Dictionary<string, CurrencyInfo>();
  1863.                                 var costToBuild = buildingBlock.blockDefinition.GetGrade(buildingBlock.grade, buildingBlock.skinID).CostToBuild();
  1864.                                 foreach (var itemAmount in costToBuild)
  1865.                                 {
  1866.                                     var amount = Mathf.RoundToInt(itemAmount.amount * buildingGradeSettings.pricePercentage / 100);
  1867.                                     if (amount <= 0)
  1868.                                     {
  1869.                                         continue;
  1870.                                     }
  1871.                                     price.Add(itemAmount.itemDef.shortname, new CurrencyInfo(amount));
  1872.                                 }
  1873.  
  1874.                                 return price;
  1875.                             }
  1876.                         }
  1877.                         else if (buildingGradeSettings.pricePercentage < 0f)
  1878.                         {
  1879.                             var currentGrade = buildingBlock.currentGrade;
  1880.                             if (currentGrade != null)
  1881.                             {
  1882.                                 return buildingBlock.blockDefinition.GetGrade(buildingBlock.grade, buildingBlock.skinID).CostToBuild().ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(Mathf.RoundToInt(y.amount)));
  1883.                             }
  1884.                         }
  1885.                     }
  1886.                 }
  1887.             }
  1888.             else
  1889.             {
  1890.                 EntitySettings entitySettings;
  1891.                 if (_configData.remove.entity.TryGetValue(targetEntity.ShortPrefabName, out entitySettings))
  1892.                 {
  1893.                     return entitySettings.priceDict;
  1894.                 }
  1895.             }
  1896.             return null;
  1897.         }
  1898.  
  1899.         private bool TryPay(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info)
  1900.         {
  1901.             var price = GetPrice(targetEntity, info);
  1902.             if (price == null || price.Count == 0)
  1903.             {
  1904.                 return true;
  1905.             }
  1906.             var collect = Pool.GetList<Item>();
  1907.             try
  1908.             {
  1909.                 foreach (var entry in price)
  1910.                 {
  1911.                     if (entry.Value.Amount <= 0)
  1912.                     {
  1913.                         continue;
  1914.                     }
  1915.                     int itemId;
  1916.                     if (_itemShortNameToItemId.TryGetValue(entry.Key, out itemId))
  1917.                     {
  1918.                         var take = TakeInventory(player, itemId, entry.Value, collect);
  1919.                         player.Command("note.inv", itemId, -take);
  1920.                     }
  1921.                     else if (!CheckOrPay(targetEntity, player, entry.Key, entry.Value, false))
  1922.                     {
  1923.                         return false;
  1924.                     }
  1925.                 }
  1926.             }
  1927.             catch (Exception e)
  1928.             {
  1929.                 PrintError($"{player} couldn't pay to remove entity. Error: {e}");
  1930.                 return false;
  1931.             }
  1932.             finally
  1933.             {
  1934.                 foreach (var item in collect)
  1935.                 {
  1936.                     item.Remove();
  1937.                 }
  1938.                 Pool.FreeUnmanaged(ref collect);
  1939.             }
  1940.             return true;
  1941.         }
  1942.  
  1943.         private bool CanPay(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info)
  1944.         {
  1945.             var price = GetPrice(targetEntity, info);
  1946.             if (price == null || price.Count == 0)
  1947.             {
  1948.                 return true;
  1949.             }
  1950.             foreach (var entry in price)
  1951.             {
  1952.                 if (entry.Value.Amount <= 0)
  1953.                 {
  1954.                     continue;
  1955.                 }
  1956.                 int itemId;
  1957.                 if (_itemShortNameToItemId.TryGetValue(entry.Key, out itemId))
  1958.                 {
  1959.                     var amount = GetInventoryAmount(player, itemId, entry.Value);
  1960.                     if (amount < entry.Value.Amount)
  1961.                     {
  1962.                         return false;
  1963.                     }
  1964.                 }
  1965.                 else if (!CheckOrPay(targetEntity, player, entry.Key, entry.Value, true))
  1966.                 {
  1967.                     return false;
  1968.                 }
  1969.             }
  1970.             return true;
  1971.         }
  1972.  
  1973.         private bool CheckOrPay(BaseEntity targetEntity, BasePlayer player, string itemName, CurrencyInfo currencyInfo, bool check)
  1974.         {
  1975.             if (currencyInfo.Amount <= 0)
  1976.             {
  1977.                 return true;
  1978.             }
  1979.             switch (itemName.ToLower())
  1980.             {
  1981.                 case ECONOMICS_KEY:
  1982.                     if (Economics == null)
  1983.                     {
  1984.                         return false;
  1985.                     }
  1986.                     if (check)
  1987.                     {
  1988.                         var balance = Economics.Call("Balance", player.userID);
  1989.                         if (balance == null)
  1990.                         {
  1991.                             return false;
  1992.                         }
  1993.                         if ((double)balance < currencyInfo.Amount)
  1994.                         {
  1995.                             return false;
  1996.                         }
  1997.                     }
  1998.                     else
  1999.                     {
  2000.                         var withdraw = Economics.Call("Withdraw", player.userID, (double)currencyInfo.Amount);
  2001.                         if (withdraw == null || !(bool)withdraw)
  2002.                         {
  2003.                             return false;
  2004.                         }
  2005.                     }
  2006.                     return true;
  2007.  
  2008.                 case SERVER_REWARDS_KEY:
  2009.                     if (ServerRewards == null)
  2010.                     {
  2011.                         return false;
  2012.                     }
  2013.                     if (check)
  2014.                     {
  2015.                         var points = ServerRewards.Call("CheckPoints", player.userID);
  2016.                         if (points == null)
  2017.                         {
  2018.                             return false;
  2019.                         }
  2020.  
  2021.                         if ((int)points < currencyInfo.Amount)
  2022.                         {
  2023.                             return false;
  2024.                         }
  2025.                     }
  2026.                     else
  2027.                     {
  2028.                         var takePoints = ServerRewards.Call("TakePoints", player.userID, currencyInfo.Amount);
  2029.                         if (takePoints == null || !(bool)takePoints)
  2030.                         {
  2031.                             return false;
  2032.                         }
  2033.                     }
  2034.                     return true;
  2035.  
  2036.                 default:
  2037.                     {
  2038.                         var result = Interface.CallHook("OnRemovableEntityCheckOrPay", targetEntity, player, itemName, currencyInfo.Amount, currencyInfo.SkinId, check);
  2039.                         if (result is bool)
  2040.                         {
  2041.                             return (bool)result;
  2042.                         }
  2043.                     }
  2044.  
  2045.                     return true;
  2046.             }
  2047.         }
  2048.  
  2049.         private static int GetInventoryAmount(BasePlayer player, int itemId, CurrencyInfo currencyInfo)
  2050.         {
  2051.             var count = 0;
  2052.             if (itemId == 0)
  2053.             {
  2054.                 return count;
  2055.             }
  2056.             var list = Pool.Get<List<Item>>();
  2057.             player.inventory.GetAllItems(list);
  2058.             foreach (var item in list)
  2059.             {
  2060.                 if (item.info.itemid == itemId && !item.IsBusy() && (currencyInfo.SkinId < 0 || item.skin == (ulong)currencyInfo.SkinId))
  2061.                 {
  2062.                     count += item.amount;
  2063.                 }
  2064.             }
  2065.             Pool.FreeUnmanaged(ref list);
  2066.             return count;
  2067.         }
  2068.  
  2069.         private static int TakeInventory(BasePlayer player, int itemId, CurrencyInfo currencyInfo, List<Item> collect)
  2070.         {
  2071.             var take = 0;
  2072.             if (itemId == 0)
  2073.             {
  2074.                 return take;
  2075.             }
  2076.             var amount = currencyInfo.Amount;
  2077.             var list = Pool.Get<List<Item>>();
  2078.             player.inventory.GetAllItems(list);
  2079.             foreach (var item in list)
  2080.             {
  2081.                 if (item.info.itemid == itemId && !item.IsBusy() && (currencyInfo.SkinId < 0 || item.skin == (ulong)currencyInfo.SkinId))
  2082.                 {
  2083.                     var need = amount - take;
  2084.                     if (need > 0)
  2085.                     {
  2086.                         if (item.amount > need)
  2087.                         {
  2088.                             item.MarkDirty();
  2089.                             item.amount -= need;
  2090.                             take += need;
  2091.                             var newItem = ItemManager.CreateByItemID(itemId, 1, item.skin);
  2092.                             newItem.amount = need;
  2093.                             newItem.CollectedForCrafting(player);
  2094.                             collect?.Add(newItem);
  2095.                             break;
  2096.                         }
  2097.                         if (item.amount <= need)
  2098.                         {
  2099.                             take += item.amount;
  2100.                             item.RemoveFromContainer();
  2101.                             collect?.Add(item);
  2102.                         }
  2103.                         if (take == amount)
  2104.                         {
  2105.                             break;
  2106.                         }
  2107.                     }
  2108.                 }
  2109.             }
  2110.             Pool.FreeUnmanaged(ref list);
  2111.             return take;
  2112.         }
  2113.  
  2114.         #endregion Pay
  2115.  
  2116.         #region Refund
  2117.  
  2118.         private void GiveRefund(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info)
  2119.         {
  2120.             var refund = GetRefund(targetEntity, info);
  2121.             if (refund == null || refund.Count == 0)
  2122.             {
  2123.                 return;
  2124.             }
  2125.             foreach (var entry in refund)
  2126.             {
  2127.                 var itemName = entry.Key;
  2128.                 var currencyInfo = entry.Value;
  2129.                 if (currencyInfo.Amount <= 0)
  2130.                 {
  2131.                     continue;
  2132.                 }
  2133.                 int itemId;
  2134.                 string shortname;
  2135.                 _shortPrefabNameToDeployable.TryGetValue(targetEntity.ShortPrefabName, out shortname);
  2136.                 if (_itemShortNameToItemId.TryGetValue(itemName, out itemId))
  2137.                 {
  2138.                     var isOriginalItem = itemName == shortname;
  2139.                     var isSpecifiedSkinId = currencyInfo.SkinId >= 0;
  2140.                     var item = ItemManager.CreateByItemID(itemId, currencyInfo.Amount, isSpecifiedSkinId ? (ulong)currencyInfo.SkinId : isOriginalItem ? targetEntity.skinID : 0);
  2141.                     if (!isSpecifiedSkinId && isOriginalItem && item.hasCondition && targetEntity is BaseCombatEntity)
  2142.                     {
  2143.                         item.condition = item.maxCondition * (targetEntity.Health() / targetEntity.MaxHealth());
  2144.                     }
  2145.                     player.GiveItem(item);
  2146.                 }
  2147.                 else
  2148.                 {
  2149.                     var flag = false;
  2150.                     switch (itemName.ToLower())
  2151.                     {
  2152.                         case ECONOMICS_KEY:
  2153.                             {
  2154.                                 if (Economics == null)
  2155.                                 {
  2156.                                     continue;
  2157.                                 }
  2158.                                 var result = Economics.Call("Deposit", player.userID, (double)currencyInfo.Amount);
  2159.                                 if (result != null)
  2160.                                 {
  2161.                                     flag = true;
  2162.                                 }
  2163.                                 break;
  2164.                             }
  2165.  
  2166.                         case SERVER_REWARDS_KEY:
  2167.                             {
  2168.                                 if (ServerRewards == null)
  2169.                                 {
  2170.                                     continue;
  2171.                                 }
  2172.                                 var result = ServerRewards.Call("AddPoints", player.userID, currencyInfo.Amount);
  2173.                                 if (result != null)
  2174.                                 {
  2175.                                     flag = true;
  2176.                                 }
  2177.                                 break;
  2178.                             }
  2179.  
  2180.                         default:
  2181.                             {
  2182.                                 var result = Interface.CallHook("OnRemovableEntityGiveRefund", targetEntity, player, itemName, currencyInfo.Amount, currencyInfo.SkinId);
  2183.                                 if (result == null)
  2184.                                 {
  2185.                                     flag = true;
  2186.                                 }
  2187.                                 break;
  2188.                             }
  2189.                     }
  2190.  
  2191.                     if (!flag)
  2192.                     {
  2193.                         PrintError($"{player} didn't receive refund maybe {itemName} doesn't seem to be a valid item name");
  2194.                     }
  2195.                 }
  2196.             }
  2197.         }
  2198.  
  2199.         private Dictionary<string, CurrencyInfo> GetRefund(BaseEntity targetEntity, RemovableEntityInfo? info)
  2200.         {
  2201.             if (info.HasValue)
  2202.             {
  2203.                 return info.Value.Refund.ValueName2Currency;
  2204.             }
  2205.             var buildingBlock = targetEntity.GetComponent<BuildingBlock>();
  2206.             if (buildingBlock != null)
  2207.             {
  2208.                 string entityName;
  2209.                
  2210.                 // Try case-sensitive lookup first
  2211.                 if (!_prefabNameToStructure.TryGetValue(buildingBlock.PrefabName, out entityName))
  2212.                 {
  2213.                     // If case-sensitive fails, try case-insensitive lookup
  2214.                     var kvp = _prefabNameToStructure.FirstOrDefault(x => string.Equals(x.Key, buildingBlock.PrefabName, StringComparison.OrdinalIgnoreCase));
  2215.                     if (!string.IsNullOrEmpty(kvp.Key))
  2216.                     {
  2217.                         entityName = kvp.Value;
  2218.                     }
  2219.                     else
  2220.                     {
  2221.                         return null;
  2222.                     }
  2223.                 }
  2224.  
  2225.                 BuildingBlocksSettings buildingBlockSettings;
  2226.                 if (_configData.remove.buildingBlock.TryGetValue(entityName, out buildingBlockSettings))
  2227.                 {
  2228.                     BuildingGradeSettings buildingGradeSettings;
  2229.                     if (buildingBlockSettings.buildingGrade.TryGetValue(buildingBlock.grade, out buildingGradeSettings))
  2230.                     {
  2231.                         if (buildingGradeSettings.refundDict != null)
  2232.                         {
  2233.                             return buildingGradeSettings.refundDict;
  2234.                         }
  2235.                         if (buildingGradeSettings.refundPercentage > 0f)
  2236.                         {
  2237.                             var currentGrade = buildingBlock.currentGrade;
  2238.                             if (currentGrade != null)
  2239.                             {
  2240.                                 var refund = new Dictionary<string, CurrencyInfo>();
  2241.                                 var costToBuild = buildingBlock.blockDefinition.GetGrade(buildingBlock.grade, buildingBlock.skinID).CostToBuild();
  2242.                                 foreach (var itemAmount in costToBuild)
  2243.                                 {
  2244.                                     var amount = Mathf.RoundToInt(itemAmount.amount * buildingGradeSettings.refundPercentage / 100);
  2245.                                     if (amount <= 0)
  2246.                                     {
  2247.                                         continue;
  2248.                                     }
  2249.                                     refund.Add(itemAmount.itemDef.shortname, new CurrencyInfo(amount));
  2250.                                 }
  2251.                                 return refund;
  2252.                             }
  2253.                         }
  2254.                         else if (buildingGradeSettings.refundPercentage < 0f)
  2255.                         {
  2256.                             var currentGrade = buildingBlock.currentGrade;
  2257.                             if (currentGrade != null)
  2258.                             {
  2259.                                 return buildingBlock.blockDefinition.GetGrade(buildingBlock.grade, buildingBlock.skinID).CostToBuild().ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(Mathf.RoundToInt(y.amount)));
  2260.                             }
  2261.                         }
  2262.                     }
  2263.                 }
  2264.             }
  2265.             else
  2266.             {
  2267.                 EntitySettings entitySettings;
  2268.                 if (_configData.remove.entity.TryGetValue(targetEntity.ShortPrefabName, out entitySettings))
  2269.                 {
  2270.                     if (_configData.remove.refundSlot)
  2271.                     {
  2272.                         var slots = GetSlots(targetEntity);
  2273.                         if (slots.Any())
  2274.                         {
  2275.                             var refund = new Dictionary<string, CurrencyInfo>(entitySettings.refundDict);
  2276.                             foreach (var slotName in slots)
  2277.                             {
  2278.                                 if (!refund.ContainsKey(slotName))
  2279.                                 {
  2280.                                     refund.Add(slotName, new CurrencyInfo(0));
  2281.                                 }
  2282.                                 refund[slotName] = new CurrencyInfo(refund[slotName].Amount + 1, refund[slotName].SkinId);
  2283.                             }
  2284.                             return refund;
  2285.                         }
  2286.                     }
  2287.                     return entitySettings.refundDict;
  2288.                 }
  2289.             }
  2290.             return null;
  2291.         }
  2292.  
  2293.         private IEnumerable<string> GetSlots(BaseEntity targetEntity)
  2294.         {
  2295.             foreach (BaseEntity.Slot slot in Enum.GetValues(typeof(BaseEntity.Slot)))
  2296.             {
  2297.                 if (targetEntity.HasSlot(slot))
  2298.                 {
  2299.                     var entity = targetEntity.GetSlot(slot);
  2300.                     if (entity != null)
  2301.                     {
  2302.                         string slotName;
  2303.                         if (_shortPrefabNameToDeployable.TryGetValue(entity.ShortPrefabName, out slotName))
  2304.                         {
  2305.                             yield return slotName;
  2306.                         }
  2307.                     }
  2308.                 }
  2309.             }
  2310.         }
  2311.  
  2312.         #endregion Refund
  2313.  
  2314.         #region RemoveEntity
  2315.  
  2316.         private IEnumerator RemoveAll(BaseEntity sourceEntity, BasePlayer player)
  2317.         {
  2318.             var removeList = Pool.Get<HashSet<BaseEntity>>();
  2319.             yield return GetNearbyEntities(sourceEntity, removeList, LAYER_ALL);
  2320.             yield return ProcessContainers(removeList);
  2321.             yield return DelayRemove(removeList, player, RemoveType.All);
  2322.             removeList.Clear();
  2323.             Pool.FreeUnmanaged(ref removeList);
  2324.             _removeAllCoroutine = null;
  2325.         }
  2326.  
  2327.         private IEnumerator RemoveExternal(StabilityEntity sourceEntity, BasePlayer player)
  2328.         {
  2329.             var removeList = Pool.Get<HashSet<StabilityEntity>>();
  2330.             yield return GetNearbyEntities(sourceEntity, removeList, Layers.Mask.Construction, IsExternalWall);
  2331.             yield return DelayRemove(removeList, player, RemoveType.External);
  2332.             removeList.Clear();
  2333.             Pool.FreeUnmanaged(ref removeList);
  2334.             _removeExternalCoroutine = null;
  2335.         }
  2336.  
  2337.         private IEnumerator RemoveStructure(DecayEntity sourceEntity, BasePlayer player)
  2338.         {
  2339.             var removeList = Pool.Get<HashSet<BaseEntity>>();
  2340.             yield return ProcessBuilding(sourceEntity, removeList);
  2341.             yield return DelayRemove(removeList, player, RemoveType.Structure);
  2342.             removeList.Clear();
  2343.             Pool.FreeUnmanaged(ref removeList);
  2344.             _removeStructureCoroutine = null;
  2345.         }
  2346.  
  2347.         private IEnumerator RemovePlayerEntity(ConsoleSystem.Arg arg, ulong targetID, PlayerEntityRemoveType playerEntityRemoveType)
  2348.         {
  2349.             var current = 0;
  2350.             var removeList = Pool.Get<HashSet<BaseEntity>>();
  2351.             switch (playerEntityRemoveType)
  2352.             {
  2353.                 case PlayerEntityRemoveType.All:
  2354.                 case PlayerEntityRemoveType.Building:
  2355.                     var onlyBuilding = playerEntityRemoveType == PlayerEntityRemoveType.Building;
  2356.                     foreach (var serverEntity in BaseNetworkable.serverEntities)
  2357.                     {
  2358.                         if (++current % 500 == 0)
  2359.                         {
  2360.                             yield return CoroutineEx.waitForEndOfFrame;
  2361.                         }
  2362.                         var entity = serverEntity as BaseEntity;
  2363.                         if (entity == null || entity.OwnerID != targetID)
  2364.                         {
  2365.                             continue;
  2366.                         }
  2367.                         if (!onlyBuilding || entity is BuildingBlock)
  2368.                         {
  2369.                             removeList.Add(entity);
  2370.                         }
  2371.                     }
  2372.                     foreach (var player in BasePlayer.allPlayerList)
  2373.                     {
  2374.                         if (player.userID == targetID)
  2375.                         {
  2376.                             if (player.IsConnected)
  2377.                             {
  2378.                                 player.Kick("From RemoverTool Plugin");
  2379.                             }
  2380.                             removeList.Add(player);
  2381.                             break;
  2382.                         }
  2383.                     }
  2384.                     break;
  2385.  
  2386.                 case PlayerEntityRemoveType.Cupboard:
  2387.                     foreach (var serverEntity in BaseNetworkable.serverEntities)
  2388.                     {
  2389.                         if (++current % 500 == 0)
  2390.                         {
  2391.                             yield return CoroutineEx.waitForEndOfFrame;
  2392.                         }
  2393.                         var entity = serverEntity as BuildingPrivlidge;
  2394.                         if (entity == null || entity.OwnerID != targetID)
  2395.                         {
  2396.                             continue;
  2397.                         }
  2398.                         yield return ProcessBuilding(entity, removeList);
  2399.                     }
  2400.                     break;
  2401.             }
  2402.             var removed = removeList.Count(x => x != null && !x.IsDestroyed);
  2403.             yield return DelayRemove(removeList);
  2404.             removeList.Clear();
  2405.             Pool.FreeUnmanaged(ref removeList);
  2406.             Print(arg, $"You have successfully removed {removed} entities of player {targetID}.");
  2407.             _removePlayerEntityCoroutine = null;
  2408.         }
  2409.  
  2410.         private IEnumerator DelayRemove(IEnumerable<BaseEntity> entities, BasePlayer player = null, RemoveType removeType = RemoveType.None)
  2411.         {
  2412.             var removed = 0;
  2413.             var destroyMode = removeType == RemoveType.None ? BaseNetworkable.DestroyMode.None : _configData.removeType[removeType].gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None;
  2414.             foreach (var entity in entities)
  2415.             {
  2416.                 if (DoRemove(entity, destroyMode) && ++removed % _configData.global.removePerFrame == 0)
  2417.                 {
  2418.                     yield return CoroutineEx.waitForEndOfFrame;
  2419.                 }
  2420.             }
  2421.  
  2422.             if (removeType == RemoveType.None)
  2423.             {
  2424.                 yield break;
  2425.             }
  2426.             var toolRemover = player?.GetComponent<ToolRemover>();
  2427.             if (toolRemover != null && toolRemover.RemoveType == removeType)
  2428.             {
  2429.                 toolRemover.CurrentRemoved += removed;
  2430.             }
  2431.             if (player != null)
  2432.             {
  2433.                 Print(player, Lang($"CompletedRemove{removeType}", player.UserIDString, removed));
  2434.             }
  2435.         }
  2436.  
  2437.         #region RemoveEntity Helpers
  2438.  
  2439.         private static IEnumerator GetNearbyEntities<T>(T sourceEntity, HashSet<T> removeList, int layers, Func<T, bool> filter = null) where T : BaseEntity
  2440.         {
  2441.             var current = 0;
  2442.             var checkFrom = Pool.Get<Queue<Vector3>>();
  2443.             var nearbyEntities = Pool.GetList<T>();
  2444.             removeList.Add(sourceEntity);
  2445.             checkFrom.Enqueue(sourceEntity.transform.position);
  2446.             while (checkFrom.Count > 0)
  2447.             {
  2448.                 nearbyEntities.Clear();
  2449.                 var position = checkFrom.Dequeue();
  2450.                 Vis.Entities(position, 3f, nearbyEntities, layers);
  2451.                 for (var i = 0; i < nearbyEntities.Count; i++)
  2452.                 {
  2453.                     var entity = nearbyEntities[i];
  2454.                     if (filter != null && !filter(entity))
  2455.                     {
  2456.                         continue;
  2457.                     }
  2458.                     if (!removeList.Add(entity))
  2459.                     {
  2460.                         continue;
  2461.                     }
  2462.                     checkFrom.Enqueue(entity.transform.position);
  2463.                 }
  2464.                 if (++current % _configData.global.removePerFrame == 0)
  2465.                 {
  2466.                     yield return CoroutineEx.waitForEndOfFrame;
  2467.                 }
  2468.             }
  2469.             checkFrom.Clear();
  2470.             Pool.FreeUnmanaged(ref checkFrom);
  2471.             Pool.FreeUnmanaged(ref nearbyEntities);
  2472.         }
  2473.  
  2474.         private static IEnumerator ProcessContainers(HashSet<BaseEntity> removeList)
  2475.         {
  2476.             foreach (var entity in removeList)
  2477.             {
  2478.                 ProcessContainer(entity);
  2479.             }
  2480.  
  2481.             ItemManager.DoRemoves();
  2482.             yield break;
  2483.         }
  2484.  
  2485.         private static IEnumerator ProcessBuilding(DecayEntity sourceEntity, HashSet<BaseEntity> removeList)
  2486.         {
  2487.             var building = sourceEntity.GetBuilding();
  2488.             if (building != null)
  2489.             {
  2490.                 foreach (var entity in building.decayEntities)
  2491.                 {
  2492.                     if (!removeList.Add(entity))
  2493.                     {
  2494.                         continue;
  2495.                     }
  2496.                     ProcessContainer(entity);
  2497.                 }
  2498.             }
  2499.             else
  2500.             {
  2501.                 removeList.Add(sourceEntity);
  2502.             }
  2503.  
  2504.             ItemManager.DoRemoves();
  2505.             yield break;
  2506.         }
  2507.  
  2508.         private static void ProcessContainer(BaseEntity entity)
  2509.         {
  2510.             var itemContainerEntity = entity as IItemContainerEntity;
  2511.             if (itemContainerEntity != null && itemContainerEntity.inventory?.itemList?.Count > 0)
  2512.             {
  2513.                 if (_configData.global.noItemContainerDrop)
  2514.                 {
  2515.                     itemContainerEntity.inventory.Clear();
  2516.                 }
  2517.                 else
  2518.                 {
  2519.                     DropItemContainer(itemContainerEntity.inventory, itemContainerEntity.GetDropPosition(), itemContainerEntity.Transform.rotation);
  2520.                 }
  2521.             }
  2522.         }
  2523.  
  2524.         private static bool DoRemove(BaseEntity entity, BaseNetworkable.DestroyMode destroyMode)
  2525.         {
  2526.             if (entity != null && !entity.IsDestroyed)
  2527.             {
  2528.                 entity.Kill(destroyMode);
  2529.                 return true;
  2530.             }
  2531.             return false;
  2532.         }
  2533.  
  2534.         private static void DoNormalRemove(BasePlayer player, BaseEntity entity, bool gibs = true)
  2535.         {
  2536.             if (entity != null && !entity.IsDestroyed)
  2537.             {
  2538.                 _instance.PrintDebug($"{player.displayName}({player.userID}) has removed {entity.ShortPrefabName}({entity.OwnerID} | {entity.transform.position})", true);
  2539.                 Interface.CallHook("OnNormalRemovedEntity", player, entity);
  2540.                 entity.Kill(gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None);
  2541.             }
  2542.         }
  2543.  
  2544.         #endregion RemoveEntity Helpers
  2545.  
  2546.         #endregion RemoveEntity
  2547.  
  2548.         #region API
  2549.  
  2550.         public struct CurrencyInfo
  2551.         {
  2552.             [JsonProperty(PropertyName = "amount")]
  2553.             public int Amount { get; set; }
  2554.  
  2555.             [JsonProperty(PropertyName = "skinId")]
  2556.             public long SkinId { get; set; }
  2557.  
  2558.             public CurrencyInfo(int amount, long skinId = -1)
  2559.             {
  2560.                 Amount = amount;
  2561.                 SkinId = skinId;
  2562.             }
  2563.         }
  2564.  
  2565.         private struct ValueCache<T>
  2566.         {
  2567.             private T _value;
  2568.             private bool _flag;
  2569.             private readonly string _key;
  2570.             private readonly Dictionary<string, object> _dictionary;
  2571.  
  2572.             public ValueCache(string key, Dictionary<string, object> dictionary, T defaultValue)
  2573.             {
  2574.                 _flag = false;
  2575.                 _key = key;
  2576.                 _value = defaultValue;
  2577.                 _dictionary = dictionary;
  2578.             }
  2579.  
  2580.             public T Value
  2581.             {
  2582.                 get
  2583.                 {
  2584.                     if (!_flag)
  2585.                     {
  2586.                         _flag = true;
  2587.                         object value;
  2588.                         if (_dictionary.TryGetValue(_key, out value))
  2589.                         {
  2590.                             try
  2591.                             {
  2592.                                 _value = (T)value;
  2593.                             }
  2594.                             catch (Exception)
  2595.                             {
  2596.                                 _instance.PrintError($"Incorrect type for {_key}( {typeof(T)})");
  2597.                             }
  2598.                         }
  2599.                     }
  2600.  
  2601.                     return _value;
  2602.                 }
  2603.             }
  2604.         }
  2605.  
  2606.         private struct CurrencyCache
  2607.         {
  2608.             private bool _name2InfoFlag;
  2609.             private bool _name2AmountFlag;
  2610.             private Dictionary<string, CurrencyInfo> _valueName2Currency;
  2611.             private Dictionary<string, ExternalItemInfo> _valueName2Info;
  2612.             private ValueCache<Dictionary<string, object>> _dictionary;
  2613.  
  2614.             public CurrencyCache(string key, Dictionary<string, object> dictionary)
  2615.             {
  2616.                 _name2InfoFlag = false;
  2617.                 _name2AmountFlag = false;
  2618.                 _valueName2Info = null;
  2619.                 _valueName2Currency = null;
  2620.                 _dictionary = new ValueCache<Dictionary<string, object>>(key, dictionary, default(Dictionary<string, object>));
  2621.             }
  2622.  
  2623.             public ExternalItemInfo? this[string key]
  2624.             {
  2625.                 get
  2626.                 {
  2627.                     if (ValueName2Info == null)
  2628.                     {
  2629.                         return null;
  2630.                     }
  2631.  
  2632.                     ExternalItemInfo externalItemInfo;
  2633.                     if (!ValueName2Info.TryGetValue(key, out externalItemInfo))
  2634.                     {
  2635.                         return null;
  2636.                     }
  2637.                     return externalItemInfo;
  2638.                 }
  2639.             }
  2640.  
  2641.             public Dictionary<string, CurrencyInfo> ValueName2Currency
  2642.             {
  2643.                 get
  2644.                 {
  2645.                     if (!_name2AmountFlag)
  2646.                     {
  2647.                         _name2AmountFlag = true;
  2648.                         if (ValueName2Info == null)
  2649.                         {
  2650.                             return null;
  2651.                         }
  2652.  
  2653.                         _valueName2Currency = new Dictionary<string, CurrencyInfo>();
  2654.                         foreach (var entry in ValueName2Info)
  2655.                         {
  2656.                             _valueName2Currency.Add(entry.Key, new CurrencyInfo(entry.Value.Amount.Value, entry.Value.SkinId.Value));
  2657.                         }
  2658.                     }
  2659.  
  2660.                     return _valueName2Currency;
  2661.                 }
  2662.             }
  2663.  
  2664.             private Dictionary<string, ExternalItemInfo> ValueName2Info
  2665.             {
  2666.                 get
  2667.                 {
  2668.                     if (!_name2InfoFlag)
  2669.                     {
  2670.                         _name2InfoFlag = true;
  2671.                         if (_dictionary.Value == null)
  2672.                         {
  2673.                             return null;
  2674.                         }
  2675.  
  2676.                         _valueName2Info = new Dictionary<string, ExternalItemInfo>();
  2677.                         foreach (var entry in _dictionary.Value)
  2678.                         {
  2679.                             _valueName2Info.Add(entry.Key, new ExternalItemInfo(entry.Value as Dictionary<string, object>));
  2680.                         }
  2681.                     }
  2682.  
  2683.                     return _valueName2Info;
  2684.                 }
  2685.             }
  2686.         }
  2687.  
  2688.         private struct ExternalItemInfo
  2689.         {
  2690.             public ExternalItemInfo(Dictionary<string, object> dictionary)
  2691.             {
  2692.                 Amount = new ValueCache<int>(nameof(Amount), dictionary, 0);
  2693.                 SkinId = new ValueCache<long>(nameof(SkinId), dictionary, -1);
  2694.                 ImageId = new ValueCache<string>(nameof(ImageId), dictionary, "");
  2695.                 DisplayName = new ValueCache<string>(nameof(DisplayName), dictionary, "");
  2696.             }
  2697.  
  2698.             public ValueCache<int> Amount { get; }
  2699.             public ValueCache<long> SkinId { get; }
  2700.             public ValueCache<string> ImageId { get; }
  2701.             public ValueCache<string> DisplayName { get; }
  2702.         }
  2703.  
  2704.         private struct RemovableEntityInfo
  2705.         {
  2706.             public RemovableEntityInfo(Dictionary<string, object> dictionary)
  2707.             {
  2708.                 ImageId = new ValueCache<string>(nameof(ImageId), dictionary, "");
  2709.                 DisplayName = new ValueCache<string>(nameof(DisplayName), dictionary, "");
  2710.                 Price = new CurrencyCache(nameof(Price), dictionary);
  2711.                 Refund = new CurrencyCache(nameof(Refund), dictionary);
  2712.             }
  2713.  
  2714.             public ValueCache<string> ImageId { get; }
  2715.  
  2716.             public ValueCache<string> DisplayName { get; }
  2717.  
  2718.             public CurrencyCache Price { get; }
  2719.  
  2720.             public CurrencyCache Refund { get; }
  2721.         }
  2722.  
  2723.         private static RemovableEntityInfo? GetRemovableEntityInfo(BaseEntity entity, BasePlayer player)
  2724.         {
  2725.             if (entity == null)
  2726.             {
  2727.                 return null;
  2728.             }
  2729.             var result = Interface.CallHook("OnRemovableEntityInfo", entity, player) as Dictionary<string, object>;
  2730.             if (result != null)
  2731.             {
  2732.                 return new RemovableEntityInfo(result);
  2733.             }
  2734.  
  2735.             return null;
  2736.         }
  2737.  
  2738.         private bool IsToolRemover(BasePlayer player)
  2739.         {
  2740.             return player != null && player.GetComponent<ToolRemover>() != null;
  2741.         }
  2742.  
  2743.         private string GetPlayerRemoveType(BasePlayer player)
  2744.         {
  2745.             return player != null ? player.GetComponent<ToolRemover>()?.RemoveType.ToString() : null;
  2746.         }
  2747.  
  2748.         #endregion API
  2749.  
  2750.         #region Commands
  2751.  
  2752.         private void CmdRemove(BasePlayer player, string command, string[] args)
  2753.         {
  2754.             if (args == null || args.Length == 0)
  2755.             {
  2756.                 var sourceRemover = player.GetComponent<ToolRemover>();
  2757.                 if (sourceRemover != null)
  2758.                 {
  2759.                     sourceRemover.DisableTool();
  2760.                     return;
  2761.                 }
  2762.             }
  2763.             if (_removeOverride && !permission.UserHasPermission(player.UserIDString, PERMISSION_OVERRIDE))
  2764.             {
  2765.                 Print(player, Lang("CurrentlyDisabled", player.UserIDString));
  2766.                 return;
  2767.             }
  2768.             var removeType = RemoveType.Normal;
  2769.             var time = _configData.removeType[removeType].defaultTime;
  2770.             if (args != null && args.Length > 0)
  2771.             {
  2772.                 switch (args[0].ToLower())
  2773.                 {
  2774.                     case "n":
  2775.                     case "normal":
  2776.                         break;
  2777.  
  2778.                     case "a":
  2779.                     case "admin":
  2780.                         removeType = RemoveType.Admin;
  2781.                         time = _configData.removeType[removeType].defaultTime;
  2782.                         if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN))
  2783.                         {
  2784.                             Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_ADMIN));
  2785.                             return;
  2786.                         }
  2787.                         break;
  2788.  
  2789.                     case "all":
  2790.                         removeType = RemoveType.All;
  2791.                         time = _configData.removeType[removeType].defaultTime;
  2792.                         if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ALL))
  2793.                         {
  2794.                             Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_ALL));
  2795.                             return;
  2796.                         }
  2797.                         break;
  2798.  
  2799.                     case "s":
  2800.                     case "structure":
  2801.                         removeType = RemoveType.Structure;
  2802.                         time = _configData.removeType[removeType].defaultTime;
  2803.                         if (!permission.UserHasPermission(player.UserIDString, PERMISSION_STRUCTURE))
  2804.                         {
  2805.                             Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_STRUCTURE));
  2806.                             return;
  2807.                         }
  2808.                         break;
  2809.  
  2810.                     case "e":
  2811.                     case "external":
  2812.                         removeType = RemoveType.External;
  2813.                         time = _configData.removeType[removeType].defaultTime;
  2814.                         if (!permission.UserHasPermission(player.UserIDString, PERMISSION_EXTERNAL))
  2815.                         {
  2816.                             Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_EXTERNAL));
  2817.                             return;
  2818.                         }
  2819.                         break;
  2820.  
  2821.                     case "h":
  2822.                     case "help":
  2823.                         var stringBuilder = Pool.Get<StringBuilder>();
  2824.                         stringBuilder.Clear();
  2825.                         stringBuilder.AppendLine(Lang("Syntax", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.Normal)));
  2826.                         stringBuilder.AppendLine(Lang("Syntax1", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.Admin)));
  2827.                         stringBuilder.AppendLine(Lang("Syntax2", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.All)));
  2828.                         stringBuilder.AppendLine(Lang("Syntax3", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.Structure)));
  2829.                         stringBuilder.AppendLine(Lang("Syntax4", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.External)));
  2830.                         Print(player, stringBuilder.ToString());
  2831.                         stringBuilder.Clear();
  2832.                         Pool.FreeUnmanaged(ref stringBuilder);
  2833.                         return;
  2834.  
  2835.                     default:
  2836.                         if (int.TryParse(args[0], out time))
  2837.                         {
  2838.                             break;
  2839.                         }
  2840.                         Print(player, Lang("SyntaxError", player.UserIDString, _configData.chat.command));
  2841.                         return;
  2842.                 }
  2843.             }
  2844.             if (args != null && args.Length > 1)
  2845.             {
  2846.                 int.TryParse(args[1], out time);
  2847.             }
  2848.             ToggleRemove(player, removeType, time);
  2849.         }
  2850.  
  2851.         private bool ToggleRemove(BasePlayer player, RemoveType removeType, int time = 0)
  2852.         {
  2853.             if (removeType == RemoveType.Normal && !permission.UserHasPermission(player.UserIDString, PERMISSION_NORMAL))
  2854.             {
  2855.                 Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_NORMAL));
  2856.                 return false;
  2857.             }
  2858.  
  2859.             var maxRemovable = 0;
  2860.             bool pay = false, refund = false;
  2861.             var removeTypeS = _configData.removeType[removeType];
  2862.             var distance = removeTypeS.distance;
  2863.             var maxTime = removeTypeS.maxTime;
  2864.             var resetTime = removeTypeS.resetTime;
  2865.             var interval = _configData.global.removeInterval;
  2866.             if (removeType == RemoveType.Normal)
  2867.             {
  2868.                 var permissionS = GetPermissionSettings(player);
  2869.                 var cooldown = permissionS.cooldown;
  2870.                 if (cooldown > 0 && !(_configData.global.cooldownExclude && player.IsAdmin))
  2871.                 {
  2872.                     float lastUse;
  2873.                     if (_cooldownTimes.TryGetValue(player.userID, out lastUse))
  2874.                     {
  2875.                         var timeLeft = cooldown - (Time.realtimeSinceStartup - lastUse);
  2876.                         if (timeLeft > 0)
  2877.                         {
  2878.                             var timeRemaining = timeLeft > 300f ? TimeSpan.FromSeconds(timeLeft).ToShortString() : Mathf.CeilToInt(timeLeft).ToString();
  2879.                             Print(player, Lang("Cooldown", player.UserIDString, timeRemaining));
  2880.                             return false;
  2881.                         }
  2882.                     }
  2883.                 }
  2884.                 if (_removeMode == RemoveMode.MeleeHit && _configData.removerMode.meleeHitRequires)
  2885.                 {
  2886.                     if (!IsMeleeTool(player))
  2887.                     {
  2888.                         var meleeToolDisplayName = GetDisplayNameByCurrencyName(lang.GetLanguage(player.UserIDString), _configData.removerMode.meleeHitItemShortname, _configData.removerMode.meleeHitModeSkin);
  2889.                         Print(player, Lang("MeleeToolNotHeld", player.UserIDString, meleeToolDisplayName));
  2890.                         return false;
  2891.                     }
  2892.                 }
  2893.                 if (_removeMode == RemoveMode.SpecificTool && _configData.removerMode.specificToolRequires)
  2894.                 {
  2895.                     if (!IsSpecificTool(player))
  2896.                     {
  2897.                         var specificToolDisplayName = GetDisplayNameByCurrencyName(lang.GetLanguage(player.UserIDString), _configData.removerMode.specificToolShortName, _configData.removerMode.specificToolSkin);
  2898.                         Print(player, Lang("SpecificToolNotHeld", player.UserIDString, specificToolDisplayName));
  2899.                         return false;
  2900.                     }
  2901.                 }
  2902.  
  2903.                 interval = permissionS.removeInterval;
  2904.                 resetTime = permissionS.resetTime;
  2905.                 maxTime = permissionS.maxTime;
  2906.                 maxRemovable = permissionS.maxRemovable;
  2907.                 if (_configData.global.maxRemovableExclude && player.IsAdmin)
  2908.                 {
  2909.                     maxRemovable = 0;
  2910.                 }
  2911.                 distance = permissionS.distance;
  2912.                 pay = permissionS.pay;
  2913.                 refund = permissionS.refund;
  2914.             }
  2915.             if (time == 0)
  2916.             {
  2917.                 time = _configData.removeType[removeType].defaultTime;
  2918.             }
  2919.             if (time > maxTime)
  2920.             {
  2921.                 time = maxTime;
  2922.             }
  2923.             var toolRemover = player.GetOrAddComponent<ToolRemover>();
  2924.             if (toolRemover.RemoveType == RemoveType.Normal)
  2925.             {
  2926.                 if (!_configData.global.startCooldownOnRemoved)
  2927.                 {
  2928.                     _cooldownTimes[player.userID] = Time.realtimeSinceStartup;
  2929.                 }
  2930.             }
  2931.             toolRemover.Init(removeType, time, maxRemovable, distance, interval, pay, refund, resetTime, true);
  2932.             if (_configData.chat.showMessageWhenEnabledOrDisabled)
  2933.             {
  2934.                 Print(player, Lang("ToolEnabled", player.UserIDString, time, maxRemovable == 0 ? Lang("Unlimit", player.UserIDString) : maxRemovable.ToString(), GetRemoveTypeName(removeType)));
  2935.             }
  2936.             return true;
  2937.         }
  2938.  
  2939.         [ConsoleCommand("remove.toggle")]
  2940.         private void CCmdRemoveToggle(ConsoleSystem.Arg arg)
  2941.         {
  2942.             var player = arg.Player();
  2943.             if (player == null)
  2944.             {
  2945.                 Print(arg, "Syntax error!!! Please type the commands in the F1 console");
  2946.                 return;
  2947.             }
  2948.             CmdRemove(player, null, arg.Args);
  2949.         }
  2950.  
  2951.         [ConsoleCommand("remove.target")]
  2952.         private void CCmdRemoveTarget(ConsoleSystem.Arg arg)
  2953.         {
  2954.             if (arg.Args == null || arg.Args.Length <= 1)
  2955.             {
  2956.                 var stringBuilder = Pool.Get<StringBuilder>();
  2957.                 stringBuilder.Clear();
  2958.                 stringBuilder.AppendLine("Syntax error of target command");
  2959.                 stringBuilder.AppendLine("remove.target <disable | d> <player (name or id)> - Disable remover tool for player");
  2960.                 stringBuilder.AppendLine("remove.target <normal | n> <player (name or id)> [time (seconds)] [max removable objects (integer)] - Enable remover tool for player (Normal)");
  2961.                 stringBuilder.AppendLine("remove.target <admin | a> <player (name or id)> [time (seconds)] - Enable remover tool for player (Admin)");
  2962.                 stringBuilder.AppendLine("remove.target <all> <player (name or id)> [time (seconds)] - Enable remover tool for player (All)");
  2963.                 stringBuilder.AppendLine("remove.target <structure | s> <player (name or id)> [time (seconds)] - Enable remover tool for player (Structure)");
  2964.                 stringBuilder.AppendLine("remove.target <external | e> <player (name or id)> [time (seconds)] - Enable remover tool for player (External)");
  2965.                 Print(arg, stringBuilder.ToString());
  2966.                 stringBuilder.Clear();
  2967.                 Pool.FreeUnmanaged(ref stringBuilder);
  2968.                 return;
  2969.             }
  2970.             var player = arg.Player();
  2971.             if (player != null && !permission.UserHasPermission(player.UserIDString, PERMISSION_TARGET))
  2972.             {
  2973.                 Print(arg, Lang("NotAllowed", player.UserIDString, PERMISSION_TARGET));
  2974.                 return;
  2975.             }
  2976.             var target = RustCore.FindPlayer(arg.Args[1]);
  2977.             if (target == null || !target.IsConnected)
  2978.             {
  2979.                 Print(arg, target == null ? $"'{arg.Args[0]}' cannot be found." : $"'{target}' is offline.");
  2980.                 return;
  2981.             }
  2982.             var removeType = RemoveType.Normal;
  2983.             switch (arg.Args[0].ToLower())
  2984.             {
  2985.                 case "n":
  2986.                 case "normal":
  2987.                     break;
  2988.  
  2989.                 case "a":
  2990.                 case "admin":
  2991.                     removeType = RemoveType.Admin;
  2992.                     break;
  2993.  
  2994.                 case "all":
  2995.                     removeType = RemoveType.All;
  2996.                     break;
  2997.  
  2998.                 case "s":
  2999.                 case "structure":
  3000.                     removeType = RemoveType.Structure;
  3001.                     break;
  3002.  
  3003.                 case "e":
  3004.                 case "external":
  3005.                     removeType = RemoveType.External;
  3006.                     break;
  3007.  
  3008.                 case "d":
  3009.                 case "disable":
  3010.                     var toolRemover = target.GetComponent<ToolRemover>();
  3011.                     if (toolRemover != null)
  3012.                     {
  3013.                         toolRemover.DisableTool();
  3014.                         Print(arg, $"{target}'s remover tool is disabled");
  3015.                     }
  3016.                     else
  3017.                     {
  3018.                         Print(arg, $"{target} did not enable the remover tool");
  3019.                     }
  3020.                     return;
  3021.  
  3022.                 default:
  3023.                     var stringBuilder = Pool.Get<StringBuilder>();
  3024.                     stringBuilder.Clear();
  3025.                     stringBuilder.AppendLine("Syntax error of target command");
  3026.                     stringBuilder.AppendLine("remove.target <disable | d> <player (name or id)> - Disable remover tool for player");
  3027.                     stringBuilder.AppendLine("remove.target <normal | n> <player (name or id)> [time (seconds)] [max removable objects (integer)] - Enable remover tool for player (Normal)");
  3028.                     stringBuilder.AppendLine("remove.target <admin | a> <player (name or id)> [time (seconds)] - Enable remover tool for player (Admin)");
  3029.                     stringBuilder.AppendLine("remove.target <all> <player (name or id)> [time (seconds)] - Enable remover tool for player (All)");
  3030.                     stringBuilder.AppendLine("remove.target <structure | s> <player (name or id)> [time (seconds)] - Enable remover tool for player (Structure)");
  3031.                     stringBuilder.AppendLine("remove.target <external | e> <player (name or id)> [time (seconds)] - Enable remover tool for player (External)");
  3032.                     Print(arg, stringBuilder.ToString());
  3033.                     stringBuilder.Clear();
  3034.                     Pool.FreeUnmanaged(ref stringBuilder);
  3035.                     return;
  3036.             }
  3037.             var maxRemovable = 0;
  3038.             var time = _configData.removeType[removeType].defaultTime;
  3039.             if (arg.Args.Length > 2)
  3040.             {
  3041.                 int.TryParse(arg.Args[2], out time);
  3042.             }
  3043.             if (arg.Args.Length > 3 && removeType == RemoveType.Normal)
  3044.             {
  3045.                 int.TryParse(arg.Args[3], out maxRemovable);
  3046.             }
  3047.             var permissionS = _configData.permission[PERMISSION_NORMAL];
  3048.             var targetRemover = target.GetOrAddComponent<ToolRemover>();
  3049.             targetRemover.Init(removeType, time, maxRemovable, _configData.removeType[removeType].distance, permissionS.removeInterval, permissionS.pay, permissionS.refund, permissionS.resetTime, false);
  3050.             Print(arg, Lang("TargetEnabled", player?.UserIDString, target, time, maxRemovable, GetRemoveTypeName(removeType)));
  3051.         }
  3052.  
  3053.         [ConsoleCommand("remove.building")]
  3054.         private void CCmdConstruction(ConsoleSystem.Arg arg)
  3055.         {
  3056.             if (arg.Args == null || arg.Args.Length <= 1 || !arg.IsAdmin)
  3057.             {
  3058.                 Print(arg, "Syntax error, Please type 'remove.building <price / refund / priceP / refundP> <percentage>', e.g.'remove.building price 60'");
  3059.                 return;
  3060.             }
  3061.             float value;
  3062.             switch (arg.Args[0].ToLower())
  3063.             {
  3064.                 case "price":
  3065.                     if (!float.TryParse(arg.Args[1], out value))
  3066.                     {
  3067.                         value = 50f;
  3068.                     }
  3069.                     foreach (var construction in _constructions)
  3070.                     {
  3071.                         BuildingBlocksSettings buildingBlocksSettings;
  3072.                         if (_configData.remove.buildingBlock.TryGetValue(construction.info.name.english, out buildingBlocksSettings))
  3073.                         {
  3074.                             foreach (var entry in buildingBlocksSettings.buildingGrade)
  3075.                             {
  3076.                                 var grade = construction.GetGrade(entry.Key, 0);
  3077.                                 var costToBuild = grade.CostToBuild();
  3078.                                 entry.Value.price = costToBuild.ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(value <= 0 ? 0 : Mathf.RoundToInt(y.amount * value / 100)));
  3079.                             }
  3080.                         }
  3081.                     }
  3082.                     Print(arg, $"Successfully modified all building prices to {value}% of the initial cost.");
  3083.                     SaveConfig();
  3084.                     LoadConfig();
  3085.                     return;
  3086.  
  3087.                 case "refund":
  3088.                     if (!float.TryParse(arg.Args[1], out value))
  3089.                     {
  3090.                         value = 40f;
  3091.                     }
  3092.                     foreach (var construction in _constructions)
  3093.                     {
  3094.                         BuildingBlocksSettings buildingBlocksSettings;
  3095.                         if (_configData.remove.buildingBlock.TryGetValue(construction.info.name.english, out buildingBlocksSettings))
  3096.                         {
  3097.                             foreach (var entry in buildingBlocksSettings.buildingGrade)
  3098.                             {
  3099.                                 var grade = construction.GetGrade(entry.Key, 0);
  3100.                                 var costToBuild = grade.CostToBuild();
  3101.                                 entry.Value.refund = costToBuild.ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(value <= 0 ? 0 : Mathf.RoundToInt(y.amount * value / 100)));
  3102.                             }
  3103.                         }
  3104.                     }
  3105.                     Print(arg, $"Successfully modified all building refunds to {value}% of the initial cost.");
  3106.                     SaveConfig();
  3107.                     LoadConfig();
  3108.                     return;
  3109.  
  3110.                 case "pricep":
  3111.                     if (!float.TryParse(arg.Args[1], out value))
  3112.                     {
  3113.                         value = 40f;
  3114.                     }
  3115.                     foreach (var buildingBlockS in _configData.remove.buildingBlock.Values)
  3116.                     {
  3117.                         foreach (var data in buildingBlockS.buildingGrade.Values)
  3118.                         {
  3119.                             data.price = value <= 0 ? 0 : value;
  3120.                         }
  3121.                     }
  3122.  
  3123.                     Print(arg, $"Successfully modified all building prices to {value}% of the initial cost.");
  3124.                     SaveConfig();
  3125.                     LoadConfig();
  3126.                     return;
  3127.  
  3128.                 case "refundp":
  3129.                     if (!float.TryParse(arg.Args[1], out value))
  3130.                     {
  3131.                         value = 50f;
  3132.                     }
  3133.                     foreach (var buildingBlockS in _configData.remove.buildingBlock.Values)
  3134.                     {
  3135.                         foreach (var data in buildingBlockS.buildingGrade.Values)
  3136.                         {
  3137.                             data.refund = value <= 0 ? 0 : value;
  3138.                         }
  3139.                     }
  3140.  
  3141.                     Print(arg, $"Successfully modified all building refunds to {value}% of the initial cost.");
  3142.                     SaveConfig();
  3143.                     LoadConfig();
  3144.                     return;
  3145.  
  3146.                 default:
  3147.                     Print(arg, "Syntax error, Please type 'remove.building <price / refund / priceP / refundP> <percentage>', e.g.'remove.building price 60'");
  3148.                     return;
  3149.             }
  3150.         }
  3151.  
  3152.         [ConsoleCommand("remove.allow")]
  3153.         private void CCmdRemoveAllow(ConsoleSystem.Arg arg)
  3154.         {
  3155.             if (arg.Args == null || arg.Args.Length == 0)
  3156.             {
  3157.                 Print(arg, "Syntax error, Please type 'remove.allow <true | false>'");
  3158.                 return;
  3159.             }
  3160.             var player = arg.Player();
  3161.             if (player != null && !permission.UserHasPermission(player.UserIDString, PERMISSION_OVERRIDE))
  3162.             {
  3163.                 Print(arg, Lang("NotAllowed", player.UserIDString, PERMISSION_OVERRIDE));
  3164.                 return;
  3165.             }
  3166.             switch (arg.Args[0].ToLower())
  3167.             {
  3168.                 case "true":
  3169.                 case "1":
  3170.                     _removeOverride = false;
  3171.                     Print(arg, "Remove is now allowed depending on your settings.");
  3172.                     return;
  3173.  
  3174.                 case "false":
  3175.                 case "0":
  3176.                     _removeOverride = true;
  3177.                     Print(arg, "Remove is now restricted for all players (exept admins)");
  3178.                     foreach (var p in BasePlayer.activePlayerList)
  3179.                     {
  3180.                         var toolRemover = p.GetComponent<ToolRemover>();
  3181.                         if (toolRemover == null)
  3182.                         {
  3183.                             continue;
  3184.                         }
  3185.                         if (toolRemover.RemoveType == RemoveType.Normal && toolRemover.CanOverride)
  3186.                         {
  3187.                             Print(toolRemover.Player, "The remover tool has been disabled by the admin");
  3188.                             toolRemover.DisableTool(false);
  3189.                         }
  3190.                     }
  3191.                     return;
  3192.  
  3193.                 default:
  3194.                     Print(arg, "This is not a valid argument");
  3195.                     return;
  3196.             }
  3197.         }
  3198.  
  3199.         [ConsoleCommand("remove.playerentity")]
  3200.         private void CCmdRemoveEntity(ConsoleSystem.Arg arg)
  3201.         {
  3202.             if (arg.Args == null || arg.Args.Length <= 1 || !arg.IsAdmin)
  3203.             {
  3204.                 var stringBuilder = Pool.Get<StringBuilder>();
  3205.                 stringBuilder.Clear();
  3206.                 stringBuilder.AppendLine("Syntax error of remove.playerentity command");
  3207.                 stringBuilder.AppendLine("remove.playerentity <all | a> <player id> - Remove all entities of the player");
  3208.                 stringBuilder.AppendLine("remove.playerentity <building | b> <player id> - Remove all buildings of the player");
  3209.                 stringBuilder.AppendLine("remove.playerentity <cupboard | c> <player id> - Remove buildings of the player owned cupboard");
  3210.                 Print(arg, stringBuilder.ToString());
  3211.                 stringBuilder.Clear();
  3212.                 Pool.FreeUnmanaged(ref stringBuilder);
  3213.                 return;
  3214.             }
  3215.             if (_removePlayerEntityCoroutine != null)
  3216.             {
  3217.                 Print(arg, "There is already a RemovePlayerEntity running, please wait.");
  3218.                 return;
  3219.             }
  3220.             ulong targetID;
  3221.             if (!ulong.TryParse(arg.Args[1], out targetID) || !targetID.IsSteamId())
  3222.             {
  3223.                 Print(arg, "Please enter the player's steamID.");
  3224.                 return;
  3225.             }
  3226.             PlayerEntityRemoveType playerEntityRemoveType;
  3227.             switch (arg.Args[0].ToLower())
  3228.             {
  3229.                 case "a":
  3230.                 case "all":
  3231.                     playerEntityRemoveType = PlayerEntityRemoveType.All;
  3232.                     break;
  3233.  
  3234.                 case "b":
  3235.                 case "building":
  3236.                     playerEntityRemoveType = PlayerEntityRemoveType.Building;
  3237.                     break;
  3238.  
  3239.                 case "c":
  3240.                 case "cupboard":
  3241.                     playerEntityRemoveType = PlayerEntityRemoveType.Cupboard;
  3242.                     break;
  3243.  
  3244.                 default:
  3245.                     Print(arg, "This is not a valid argument");
  3246.                     return;
  3247.             }
  3248.             _removePlayerEntityCoroutine = ServerMgr.Instance.StartCoroutine(RemovePlayerEntity(arg, targetID, playerEntityRemoveType));
  3249.             Print(arg, "Start running RemovePlayerEntity, please wait.");
  3250.         }
  3251.  
  3252.         #endregion Commands
  3253.  
  3254.         #region Debug
  3255.  
  3256.         private void PrintDebug(string message, bool warning = false)
  3257.         {
  3258.             if (_configData.global.debugEnabled)
  3259.             {
  3260.                 if (warning)
  3261.                 {
  3262.                     PrintWarning(message);
  3263.                 }
  3264.                 else
  3265.                 {
  3266.                     Puts(message);
  3267.                 }
  3268.             }
  3269.             if (_configData.global.logToFile)
  3270.             {
  3271.                 _debugStringBuilder.AppendLine($"[{DateTime.Now.ToString(CultureInfo.InstalledUICulture)}] | {message}");
  3272.             }
  3273.         }
  3274.  
  3275.         private void SaveDebug()
  3276.         {
  3277.             if (!_configData.global.logToFile)
  3278.             {
  3279.                 return;
  3280.             }
  3281.             var debugText = _debugStringBuilder.ToString().Trim();
  3282.             _debugStringBuilder.Clear();
  3283.             if (!string.IsNullOrEmpty(debugText))
  3284.             {
  3285.                 LogToFile("debug", debugText, this);
  3286.             }
  3287.         }
  3288.  
  3289.         #endregion Debug
  3290.  
  3291.         #region RustTranslationAPI
  3292.  
  3293.         private string GetItemTranslationByShortName(string language, string itemShortName)
  3294.         {
  3295.             return (string)RustTranslationAPI.Call("GetItemTranslationByShortName", language, itemShortName);
  3296.         }
  3297.  
  3298.         private string GetConstructionTranslation(string language, string prefabName)
  3299.         {
  3300.             return (string)RustTranslationAPI.Call("GetConstructionTranslation", language, prefabName);
  3301.         }
  3302.  
  3303.         private string GetDeployableTranslation(string language, string deployable)
  3304.         {
  3305.             return (string)RustTranslationAPI.Call("GetDeployableTranslation", language, deployable);
  3306.         }
  3307.  
  3308.         private string GetItemDisplayName(string language, string itemShortName)
  3309.         {
  3310.             if (RustTranslationAPI != null)
  3311.             {
  3312.                 return GetItemTranslationByShortName(language, itemShortName);
  3313.             }
  3314.             return null;
  3315.         }
  3316.  
  3317.         private string GetConstructionDisplayName(BasePlayer player, string shortPrefabName, string displayName)
  3318.         {
  3319.             if (RustTranslationAPI != null)
  3320.             {
  3321.                 var displayName1 = GetConstructionTranslation(lang.GetLanguage(player.UserIDString), shortPrefabName);
  3322.                 if (!string.IsNullOrEmpty(displayName1))
  3323.                 {
  3324.                     return displayName1;
  3325.                 }
  3326.             }
  3327.             return displayName;
  3328.         }
  3329.  
  3330.         private string GetDeployableDisplayName(BasePlayer player, string deployable, string displayName)
  3331.         {
  3332.             if (RustTranslationAPI != null)
  3333.             {
  3334.                 var displayName1 = GetDeployableTranslation(lang.GetLanguage(player.UserIDString), deployable);
  3335.                 if (!string.IsNullOrEmpty(displayName1))
  3336.                 {
  3337.                     return displayName1;
  3338.                 }
  3339.             }
  3340.             return displayName;
  3341.         }
  3342.  
  3343.         #endregion RustTranslationAPI
  3344.  
  3345.         #region ConfigurationFile
  3346.  
  3347.         private void UpdateConfig()
  3348.         {
  3349.             var buildingGrades = new[]
  3350.             {
  3351.                 BuildingGrade.Enum.Twigs, BuildingGrade.Enum.Wood,
  3352.                 BuildingGrade.Enum.Stone, BuildingGrade.Enum.Metal, BuildingGrade.Enum.TopTier
  3353.             };
  3354.  
  3355.             foreach (var value in buildingGrades)
  3356.             {
  3357.                 if (!_configData.remove.validConstruction.ContainsKey(value))
  3358.                 {
  3359.                     _configData.remove.validConstruction.Add(value, true);
  3360.                 }
  3361.             }
  3362.  
  3363.             var newBuildingBlocks = new Dictionary<string, BuildingBlocksSettings>();
  3364.             foreach (var construction in _constructions)
  3365.             {
  3366.                 BuildingBlocksSettings buildingBlocksSettings;
  3367.                 if (!_configData.remove.buildingBlock.TryGetValue(construction.info.name.english, out buildingBlocksSettings))
  3368.                 {
  3369.                     var buildingGrade = new Dictionary<BuildingGrade.Enum,
  3370.                             BuildingGradeSettings>();
  3371.                     foreach (var value in buildingGrades)
  3372.                     {
  3373.                         var grade = construction.GetGrade(value, 0);
  3374.  
  3375.                         if (grade == null)
  3376.                         {
  3377.                             continue;
  3378.                         }
  3379.  
  3380.                         var costToBuild = grade.CostToBuild();
  3381.                        
  3382.                         buildingGrade.Add(value, new BuildingGradeSettings
  3383.                         {
  3384.                             refund = costToBuild.ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(Mathf.RoundToInt(y.amount * 0.4f))),
  3385.                             price = costToBuild.ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(Mathf.RoundToInt(y.amount * 0.6f)))
  3386.                         });
  3387.                     }
  3388.                     buildingBlocksSettings = new BuildingBlocksSettings { displayName = construction.info.name.english, buildingGrade = buildingGrade };
  3389.                 }
  3390.                 newBuildingBlocks.Add(construction.info.name.english, buildingBlocksSettings);
  3391.             }
  3392.             _configData.remove.buildingBlock = newBuildingBlocks;
  3393.             foreach (var entry in _shortPrefabNameToDeployable)
  3394.             {
  3395.                 EntitySettings entitySettings;
  3396.                 if (!_configData.remove.entity.TryGetValue(entry.Key, out entitySettings))
  3397.                 {
  3398.                     var itemDefinition = ItemManager.FindItemDefinition(entry.Value);
  3399.                     entitySettings = new EntitySettings
  3400.                     {
  3401.                         enabled = _configData.global.defaultEntity.removeAllowed && itemDefinition.category != ItemCategory.Food,
  3402.                         displayName = itemDefinition.displayName.english,
  3403.                         refund = new Dictionary<string, CurrencyInfo>
  3404.                         {
  3405.                             [entry.Value] = new CurrencyInfo(1)
  3406.                         },
  3407.                         price = new Dictionary<string, CurrencyInfo>()
  3408.                     };
  3409.                     _configData.remove.entity.Add(entry.Key, entitySettings);
  3410.                 }
  3411.             }
  3412.             SaveConfig();
  3413.         }
  3414.  
  3415.         private static ConfigData _configData;
  3416.  
  3417.         private class ConfigData
  3418.         {
  3419.             [JsonProperty(PropertyName = "Settings")]
  3420.             public readonly GlobalSettings global = new GlobalSettings();
  3421.  
  3422.             [JsonProperty(PropertyName = "Container Settings")]
  3423.             public readonly ContainerSettings container = new ContainerSettings();
  3424.  
  3425.             [JsonProperty(PropertyName = "Remove Damaged Entities")]
  3426.             public readonly DamagedEntitySettings damagedEntity = new DamagedEntitySettings();
  3427.  
  3428.             [JsonProperty(PropertyName = "Chat Settings")]
  3429.             public readonly ChatSettings chat = new ChatSettings();
  3430.  
  3431.             [JsonProperty(PropertyName = "Permission Settings (Just for normal type)")]
  3432.             public readonly Dictionary<string, PermissionSettings> permission = new Dictionary<string, PermissionSettings>
  3433.             {
  3434.                 [PERMISSION_NORMAL] = new PermissionSettings { priority = 0, distance = 3, cooldown = 60, maxTime = 300, maxRemovable = 50, removeInterval = 0.8f, pay = true, refund = true, resetTime = false }
  3435.             };
  3436.  
  3437.             [JsonProperty(PropertyName = "Remove Type Settings")]
  3438.             public readonly Dictionary<RemoveType, RemoveTypeSettings> removeType = new Dictionary<RemoveType, RemoveTypeSettings>
  3439.             {
  3440.                 [RemoveType.Normal] = new RemoveTypeSettings { displayName = RemoveType.Normal.ToString(), distance = 3, gibs = true, defaultTime = 60, maxTime = 300, resetTime = false },
  3441.                 [RemoveType.Structure] = new RemoveTypeSettings { displayName = RemoveType.Structure.ToString(), distance = 100, gibs = false, defaultTime = 300, maxTime = 600, resetTime = true },
  3442.                 [RemoveType.All] = new RemoveTypeSettings { displayName = RemoveType.All.ToString(), distance = 50, gibs = false, defaultTime = 300, maxTime = 600, resetTime = true },
  3443.                 [RemoveType.Admin] = new RemoveTypeSettings { displayName = RemoveType.Admin.ToString(), distance = 20, gibs = true, defaultTime = 300, maxTime = 600, resetTime = true },
  3444.                 [RemoveType.External] = new RemoveTypeSettings { displayName = RemoveType.External.ToString(), distance = 20, gibs = true, defaultTime = 300, maxTime = 600, resetTime = true }
  3445.             };
  3446.  
  3447.             [JsonProperty(PropertyName = "Remove Mode Settings (Only one model works)")]
  3448.             public readonly RemoverModeSettings removerMode = new RemoverModeSettings();
  3449.  
  3450.             [JsonProperty(PropertyName = "NoEscape Settings")]
  3451.             public readonly NoEscapeSettings noEscape = new NoEscapeSettings();
  3452.  
  3453.             [JsonProperty(PropertyName = "Image Urls (Used to UI image)")]
  3454.             public readonly Dictionary<string, string> imageUrls = new Dictionary<string, string>
  3455.             {
  3456.                 [ECONOMICS_KEY] = "https://i.imgur.com/znPwdcv.png",
  3457.                 [SERVER_REWARDS_KEY] = "https://i.imgur.com/04rJsV3.png"
  3458.             };
  3459.  
  3460.             [JsonProperty(PropertyName = "GUI")]
  3461.             public readonly UiSettings ui = new UiSettings();
  3462.  
  3463.             [JsonProperty(PropertyName = "Remove Info (Refund & Price)")]
  3464.             public readonly RemoveSettings remove = new RemoveSettings();
  3465.  
  3466.             [JsonProperty(PropertyName = "Version")]
  3467.             public VersionNumber version;
  3468.         }
  3469.  
  3470.         public class GlobalSettings
  3471.         {
  3472.             [JsonProperty(PropertyName = "Enable Debug Mode")]
  3473.             public bool debugEnabled;
  3474.  
  3475.             [JsonProperty(PropertyName = "Log Debug To File")]
  3476.             public bool logToFile;
  3477.  
  3478.             [JsonProperty(PropertyName = "Use Teams")]
  3479.             public bool useTeams = false;
  3480.  
  3481.             [JsonProperty(PropertyName = "Use Clans")]
  3482.             public bool useClans = true;
  3483.  
  3484.             [JsonProperty(PropertyName = "Use Friends")]
  3485.             public bool useFriends = true;
  3486.  
  3487.             [JsonProperty(PropertyName = "Use Entity Owners")]
  3488.             public bool useEntityOwners = true;
  3489.  
  3490.             [JsonProperty(PropertyName = "Use Building Locks")]
  3491.             public bool useBuildingLocks = false;
  3492.  
  3493.             [JsonProperty(PropertyName = "Use Tool Cupboards (Strongly unrecommended)")]
  3494.             public bool useToolCupboards = false;
  3495.  
  3496.             [JsonProperty(PropertyName = "Use Building Owners (You will need BuildingOwners plugin)")]
  3497.             public bool useBuildingOwners = false;
  3498.  
  3499.             //[JsonProperty(PropertyName = "Exclude Twigs (Used for \"Use Tool Cupboards\" and \"Use Entity Owners\")")]
  3500.             //public bool excludeTwigs;
  3501.  
  3502.             [JsonProperty(PropertyName = "Remove Button")]
  3503.             public string removeButton = BUTTON.FIRE_PRIMARY.ToString();
  3504.  
  3505.             [JsonProperty(PropertyName = "Remove Interval (Min = 0.2)")]
  3506.             public float removeInterval = 0.5f;
  3507.  
  3508.             [JsonProperty(PropertyName = "Only start cooldown when an entity is removed")]
  3509.             public bool startCooldownOnRemoved;
  3510.  
  3511.             [JsonProperty(PropertyName = "RemoveType - All/Structure - Remove per frame")]
  3512.             public int removePerFrame = 15;
  3513.  
  3514.             [JsonProperty(PropertyName = "RemoveType - All/Structure - No item container dropped")]
  3515.             public bool noItemContainerDrop = true;
  3516.  
  3517.             [JsonProperty(PropertyName = "RemoveType - Normal - Max Removable Objects - Exclude admins")]
  3518.             public bool maxRemovableExclude = true;
  3519.  
  3520.             [JsonProperty(PropertyName = "RemoveType - Normal - Cooldown - Exclude admins")]
  3521.             public bool cooldownExclude = true;
  3522.  
  3523.             [JsonProperty(PropertyName = "RemoveType - Normal - Entity Spawned Time Limit - Enabled")]
  3524.             public bool entityTimeLimit = false;
  3525.  
  3526.             [JsonProperty(PropertyName = "RemoveType - Normal - Entity Spawned Time Limit - Cannot be removed when entity spawned time more than it")]
  3527.             public float limitTime = 300f;
  3528.  
  3529.             [JsonProperty(PropertyName = "Default Entity Settings (When automatically adding new entities to 'Other Entity Settings')")]
  3530.             public DefaultEntitySettings defaultEntity = new DefaultEntitySettings();
  3531.  
  3532.             public class DefaultEntitySettings
  3533.             {
  3534.                 [JsonProperty(PropertyName = "Default Remove Allowed")]
  3535.                 public bool removeAllowed = true;
  3536.             }
  3537.         }
  3538.  
  3539.         public class ContainerSettings
  3540.         {
  3541.             [JsonProperty(PropertyName = "Storage Container - Enable remove of not empty storages")]
  3542.             public bool removeNotEmptyStorage = true;
  3543.  
  3544.             [JsonProperty(PropertyName = "Storage Container - Drop items from container")]
  3545.             public bool dropItemsStorage = false;
  3546.  
  3547.             [JsonProperty(PropertyName = "Storage Container - Drop a item container from container")]
  3548.             public bool dropContainerStorage = true;
  3549.  
  3550.             [JsonProperty(PropertyName = "IOEntity Container - Enable remove of not empty storages")]
  3551.             public bool removeNotEmptyIoEntity = true;
  3552.  
  3553.             [JsonProperty(PropertyName = "IOEntity Container - Drop items from container")]
  3554.             public bool dropItemsIoEntity = false;
  3555.  
  3556.             [JsonProperty(PropertyName = "IOEntity Container - Drop a item container from container")]
  3557.             public bool dropContainerIoEntity = true;
  3558.         }
  3559.  
  3560.         public class DamagedEntitySettings
  3561.         {
  3562.             [JsonProperty(PropertyName = "Enabled")]
  3563.             public bool enabled = false;
  3564.  
  3565.             [JsonProperty(PropertyName = "Exclude Quarries")]
  3566.             public bool excludeQuarries = true;
  3567.  
  3568.             [JsonProperty(PropertyName = "Exclude Building Blocks")]
  3569.             public bool excludeBuildingBlocks = true;
  3570.  
  3571.             [JsonProperty(PropertyName = "Percentage (Can be removed when (health / max health * 100) is not less than it)")]
  3572.             public float percentage = 90f;
  3573.         }
  3574.  
  3575.         public class ChatSettings
  3576.         {
  3577.             [JsonProperty(PropertyName = "Chat Command")]
  3578.             public string command = "remove";
  3579.  
  3580.             [JsonProperty(PropertyName = "Chat Prefix")]
  3581.             public string prefix = "<color=#00FFFF>[RemoverTool]</color>: ";
  3582.  
  3583.             [JsonProperty(PropertyName = "Chat SteamID Icon")]
  3584.             public ulong steamIDIcon = 0;
  3585.  
  3586.             [JsonProperty(PropertyName = "Show Message When Enabled/Disabled")]
  3587.             public bool showMessageWhenEnabledOrDisabled = true;
  3588.         }
  3589.  
  3590.         public class PermissionSettings
  3591.         {
  3592.             [JsonProperty(PropertyName = "Priority")]
  3593.             public int priority;
  3594.  
  3595.             [JsonProperty(PropertyName = "Distance")]
  3596.             public float distance;
  3597.  
  3598.             [JsonProperty(PropertyName = "Cooldown")]
  3599.             public float cooldown;
  3600.  
  3601.             [JsonProperty(PropertyName = "Max Time")]
  3602.             public int maxTime;
  3603.  
  3604.             [JsonProperty(PropertyName = "Remove Interval (Min = 0.2)")]
  3605.             public float removeInterval;
  3606.  
  3607.             [JsonProperty(PropertyName = "Max Removable Objects (0 = Unlimited)")]
  3608.             public int maxRemovable;
  3609.  
  3610.             [JsonProperty(PropertyName = "Pay")]
  3611.             public bool pay;
  3612.  
  3613.             [JsonProperty(PropertyName = "Refund")]
  3614.             public bool refund;
  3615.  
  3616.             [JsonProperty(PropertyName = "Reset the time after removing an entity")]
  3617.             public bool resetTime;
  3618.         }
  3619.  
  3620.         public class RemoveTypeSettings
  3621.         {
  3622.             [JsonProperty(PropertyName = "Display Name")]
  3623.             public string displayName;
  3624.  
  3625.             [JsonProperty(PropertyName = "Distance")]
  3626.             public float distance;
  3627.  
  3628.             [JsonProperty(PropertyName = "Default Time")]
  3629.             public int defaultTime;
  3630.  
  3631.             [JsonProperty(PropertyName = "Max Time")]
  3632.             public int maxTime;
  3633.  
  3634.             [JsonProperty(PropertyName = "Gibs")]
  3635.             public bool gibs;
  3636.  
  3637.             [JsonProperty(PropertyName = "Reset the time after removing an entity")]
  3638.             public bool resetTime;
  3639.         }
  3640.  
  3641.         public class RemoverModeSettings
  3642.         {
  3643.             [JsonProperty(PropertyName = "No Held Item Mode")]
  3644.             public bool noHeldMode = true;
  3645.  
  3646.             [JsonProperty(PropertyName = "No Held Item Mode - Disable remover tool when you have any item in hand")]
  3647.             public bool noHeldDisableInHand = true;
  3648.  
  3649.             [JsonProperty(PropertyName = "Melee Tool Hit Mode")]
  3650.             public bool meleeHitMode;
  3651.  
  3652.             [JsonProperty(PropertyName = "Melee Tool Hit Mode - Item shortname")]
  3653.             public string meleeHitItemShortname = "hammer";
  3654.  
  3655.             [JsonProperty(PropertyName = "Melee Tool Hit Mode - Item skin (-1 = All skins)")]
  3656.             public long meleeHitModeSkin = -1;
  3657.  
  3658.             [JsonProperty(PropertyName = "Melee Tool Hit Mode - Auto enable remover tool when you hold a melee tool")]
  3659.             public bool meleeHitEnableInHand = false;
  3660.  
  3661.             [JsonProperty(PropertyName = "Melee Tool Hit Mode - Requires a melee tool in your hand when remover tool is enabled")]
  3662.             public bool meleeHitRequires;
  3663.  
  3664.             [JsonProperty(PropertyName = "Melee Tool Hit Mode - Disable remover tool when you are not holding a melee tool")]
  3665.             public bool meleeHitDisableInHand;
  3666.  
  3667.             [JsonProperty(PropertyName = "Specific Tool Mode")]
  3668.             public bool specificToolMode = false;
  3669.  
  3670.             [JsonProperty(PropertyName = "Specific Tool Mode - Item shortname")]
  3671.             public string specificToolShortName = "hammer";
  3672.  
  3673.             [JsonProperty(PropertyName = "Specific Tool Mode - Item skin (-1 = All skins)")]
  3674.             public long specificToolSkin = -1;
  3675.  
  3676.             [JsonProperty(PropertyName = "Specific Tool Mode - Auto enable remover tool when you hold a specific tool")]
  3677.             public bool specificToolEnableInHand = false;
  3678.  
  3679.             [JsonProperty(PropertyName = "Specific Tool Mode - Requires a specific tool in your hand when remover tool is enabled")]
  3680.             public bool specificToolRequires = false;
  3681.  
  3682.             [JsonProperty(PropertyName = "Specific Tool Mode - Disable remover tool when you are not holding a specific tool")]
  3683.             public bool specificToolDisableInHand = false;
  3684.         }
  3685.  
  3686.         public class NoEscapeSettings
  3687.         {
  3688.             [JsonProperty(PropertyName = "Use Raid Blocker")]
  3689.             public bool useRaidBlocker;
  3690.  
  3691.             [JsonProperty(PropertyName = "Use Combat Blocker")]
  3692.             public bool useCombatBlocker;
  3693.         }
  3694.  
  3695.         public class UiSettings
  3696.         {
  3697.             [JsonProperty(PropertyName = "Enabled")]
  3698.             public bool enabled = true;
  3699.  
  3700.             [JsonProperty(PropertyName = "Main Box - Min Anchor (in Rust Window)")]
  3701.             public string removerToolAnchorMin = "0 1";
  3702.  
  3703.             [JsonProperty(PropertyName = "Main Box - Max Anchor (in Rust Window)")]
  3704.             public string removerToolAnchorMax = "0 1";
  3705.  
  3706.             [JsonProperty(PropertyName = "Main Box - Min Offset (in Rust Window)")]
  3707.             public string removerToolOffsetMin = "30 -330";
  3708.  
  3709.             [JsonProperty(PropertyName = "Main Box - Max Offset (in Rust Window)")]
  3710.             public string removerToolOffsetMax = "470 -40";
  3711.  
  3712.             [JsonProperty(PropertyName = "Main Box - Background Color")]
  3713.             public string removerToolBackgroundColor = "0 0 0 0";
  3714.  
  3715.             [JsonProperty(PropertyName = "Remove Title - Box - Min Anchor (in Main Box)")]
  3716.             public string removeAnchorMin = "0 0.84";
  3717.  
  3718.             [JsonProperty(PropertyName = "Remove Title - Box - Max Anchor (in Main Box)")]
  3719.             public string removeAnchorMax = "0.996 1";
  3720.  
  3721.             [JsonProperty(PropertyName = "Remove Title - Box - Background Color")]
  3722.             public string removeBackgroundColor = "0.31 0.88 0.71 1";
  3723.  
  3724.             [JsonProperty(PropertyName = "Remove Title - Text - Min Anchor (in Main Box)")]
  3725.             public string removeTextAnchorMin = "0.05 0.84";
  3726.  
  3727.             [JsonProperty(PropertyName = "Remove Title - Text - Max Anchor (in Main Box)")]
  3728.             public string removeTextAnchorMax = "0.6 1";
  3729.  
  3730.             [JsonProperty(PropertyName = "Remove Title - Text - Text Color")]
  3731.             public string removeTextColor = "1 0.1 0.1 1";
  3732.  
  3733.             [JsonProperty(PropertyName = "Remove Title - Text - Text Size")]
  3734.             public int removeTextSize = 18;
  3735.  
  3736.             [JsonProperty(PropertyName = "Timeleft - Box - Min Anchor (in Main Box)")]
  3737.             public string timeLeftAnchorMin = "0.6 0.84";
  3738.  
  3739.             [JsonProperty(PropertyName = "Timeleft - Box - Max Anchor (in Main Box)")]
  3740.             public string timeLeftAnchorMax = "1 1";
  3741.  
  3742.             [JsonProperty(PropertyName = "Timeleft - Box - Background Color")]
  3743.             public string timeLeftBackgroundColor = "0 0 0 0";
  3744.  
  3745.             [JsonProperty(PropertyName = "Timeleft - Text - Min Anchor (in Timeleft Box)")]
  3746.             public string timeLeftTextAnchorMin = "0 0";
  3747.  
  3748.             [JsonProperty(PropertyName = "Timeleft - Text - Max Anchor (in Timeleft Box)")]
  3749.             public string timeLeftTextAnchorMax = "0.9 1";
  3750.  
  3751.             [JsonProperty(PropertyName = "Timeleft - Text - Text Color")]
  3752.             public string timeLeftTextColor = "0 0 0 0.9";
  3753.  
  3754.             [JsonProperty(PropertyName = "Timeleft - Text - Text Size")]
  3755.             public int timeLeftTextSize = 15;
  3756.  
  3757.             [JsonProperty(PropertyName = "Entity - Box - Min Anchor (in Main Box)")]
  3758.             public string entityAnchorMin = "0 0.68";
  3759.  
  3760.             [JsonProperty(PropertyName = "Entity - Box - Max Anchor (in Main Box)")]
  3761.             public string entityAnchorMax = "1 0.84";
  3762.  
  3763.             [JsonProperty(PropertyName = "Entity - Box - Background Color")]
  3764.             public string entityBackgroundColor = "0.82 0.58 0.30 1";
  3765.  
  3766.             [JsonProperty(PropertyName = "Entity - Text - Min Anchor (in Entity Box)")]
  3767.             public string entityTextAnchorMin = "0.05 0";
  3768.  
  3769.             [JsonProperty(PropertyName = "Entity - Text - Max Anchor (in Entity Box)")]
  3770.             public string entityTextAnchorMax = "1 1";
  3771.  
  3772.             [JsonProperty(PropertyName = "Entity - Text - Text Color")]
  3773.             public string entityTextColor = "1 1 1 1";
  3774.  
  3775.             [JsonProperty(PropertyName = "Entity - Text - Text Size")]
  3776.             public int entityTextSize = 16;
  3777.  
  3778.             [JsonProperty(PropertyName = "Entity - Image - Enabled")]
  3779.             public bool entityImageEnabled = true;
  3780.  
  3781.             [JsonProperty(PropertyName = "Entity - Image - Min Anchor (in Entity Box)")]
  3782.             public string entityImageAnchorMin = "0.795 0.01";
  3783.  
  3784.             [JsonProperty(PropertyName = "Entity - Image - Max Anchor (in Entity Box)")]
  3785.             public string entityImageAnchorMax = "0.9 0.99";
  3786.  
  3787.             [JsonProperty(PropertyName = "Authorization Check Enabled")]
  3788.             public bool authorizationEnabled = true;
  3789.  
  3790.             [JsonProperty(PropertyName = "Authorization Check - Box - Min Anchor (in Main Box)")]
  3791.             public string authorizationsAnchorMin = "0 0.6";
  3792.  
  3793.             [JsonProperty(PropertyName = "Authorization Check - Box - Max Anchor (in Main Box)")]
  3794.             public string authorizationsAnchorMax = "1 0.68";
  3795.  
  3796.             [JsonProperty(PropertyName = "Authorization Check - Box - Allowed Background Color")]
  3797.             public string allowedBackgroundColor = "0.22 0.78 0.27 1";
  3798.  
  3799.             [JsonProperty(PropertyName = "Authorization Check - Box - Refused Background Color")]
  3800.             public string refusedBackgroundColor = "0.78 0.22 0.27 1";
  3801.  
  3802.             [JsonProperty(PropertyName = "Authorization Check - Text - Min Anchor (in Authorization Check Box)")]
  3803.             public string authorizationsTextAnchorMin = "0.05 0";
  3804.  
  3805.             [JsonProperty(PropertyName = "Authorization Check - Text - Max Anchor (in Authorization Check Box)")]
  3806.             public string authorizationsTextAnchorMax = "1 1";
  3807.  
  3808.             [JsonProperty(PropertyName = "Authorization Check - Text - Text Color")]
  3809.             public string authorizationsTextColor = "1 1 1 0.9";
  3810.  
  3811.             [JsonProperty(PropertyName = "Authorization Check Box - Text - Text Size")]
  3812.             public int authorizationsTextSize = 14;
  3813.  
  3814.             [JsonProperty(PropertyName = "Price & Refund - Image Enabled")]
  3815.             public bool imageEnabled = true;
  3816.  
  3817.             [JsonProperty(PropertyName = "Price & Refund - Image Scale")]
  3818.             public float imageScale = 0.18f;
  3819.  
  3820.             [JsonProperty(PropertyName = "Price & Refund - Distance of image from right border")]
  3821.             public float rightDistance = 0.05f;
  3822.  
  3823.             [JsonProperty(PropertyName = "Price Enabled")]
  3824.             public bool priceEnabled = true;
  3825.  
  3826.             [JsonProperty(PropertyName = "Price - Box - Min Anchor (in Main Box)")]
  3827.             public string priceAnchorMin = "0 0.3";
  3828.  
  3829.             [JsonProperty(PropertyName = "Price - Box - Max Anchor (in Main Box)")]
  3830.             public string priceAnchorMax = "1 0.6";
  3831.  
  3832.             [JsonProperty(PropertyName = "Price - Box - Background Color")]
  3833.             public string priceBackgroundColor = "0 0 0 0.8";
  3834.  
  3835.             [JsonProperty(PropertyName = "Price - Text - Min Anchor (in Price Box)")]
  3836.             public string priceTextAnchorMin = "0.05 0";
  3837.  
  3838.             [JsonProperty(PropertyName = "Price - Text - Max Anchor (in Price Box)")]
  3839.             public string priceTextAnchorMax = "0.25 1";
  3840.  
  3841.             [JsonProperty(PropertyName = "Price - Text - Text Color")]
  3842.             public string priceTextColor = "1 1 1 0.9";
  3843.  
  3844.             [JsonProperty(PropertyName = "Price - Text - Text Size")]
  3845.             public int priceTextSize = 18;
  3846.  
  3847.             [JsonProperty(PropertyName = "Price - Text2 - Min Anchor (in Price Box)")]
  3848.             public string price2TextAnchorMin = "0.3 0";
  3849.  
  3850.             [JsonProperty(PropertyName = "Price - Text2 - Max Anchor (in Price Box)")]
  3851.             public string price2TextAnchorMax = "1 1";
  3852.  
  3853.             [JsonProperty(PropertyName = "Price - Text2 - Text Color")]
  3854.             public string price2TextColor = "1 1 1 0.9";
  3855.  
  3856.             [JsonProperty(PropertyName = "Price - Text2 - Text Size")]
  3857.             public int price2TextSize = 16;
  3858.  
  3859.             [JsonProperty(PropertyName = "Refund Enabled")]
  3860.             public bool refundEnabled = true;
  3861.  
  3862.             [JsonProperty(PropertyName = "Refund - Box - Min Anchor (in Main Box)")]
  3863.             public string refundAnchorMin = "0 0";
  3864.  
  3865.             [JsonProperty(PropertyName = "Refund - Box - Max Anchor (in Main Box)")]
  3866.             public string refundAnchorMax = "1 0.3";
  3867.  
  3868.             [JsonProperty(PropertyName = "Refund - Box - Background Color")]
  3869.             public string refundBackgroundColor = "0 0 0 0.8";
  3870.  
  3871.             [JsonProperty(PropertyName = "Refund - Text - Min Anchor (in Refund Box)")]
  3872.             public string refundTextAnchorMin = "0.05 0";
  3873.  
  3874.             [JsonProperty(PropertyName = "Refund - Text - Max Anchor (in Refund Box)")]
  3875.             public string refundTextAnchorMax = "0.25 1";
  3876.  
  3877.             [JsonProperty(PropertyName = "Refund - Text - Text Color")]
  3878.             public string refundTextColor = "1 1 1 0.9";
  3879.  
  3880.             [JsonProperty(PropertyName = "Refund - Text - Text Size")]
  3881.             public int refundTextSize = 18;
  3882.  
  3883.             [JsonProperty(PropertyName = "Refund - Text2 - Min Anchor (in Refund Box)")]
  3884.             public string refund2TextAnchorMin = "0.3 0";
  3885.  
  3886.             [JsonProperty(PropertyName = "Refund - Text2 - Max Anchor (in Refund Box)")]
  3887.             public string refund2TextAnchorMax = "1 1";
  3888.  
  3889.             [JsonProperty(PropertyName = "Refund - Text2 - Text Color")]
  3890.             public string refund2TextColor = "1 1 1 0.9";
  3891.  
  3892.             [JsonProperty(PropertyName = "Refund - Text2 - Text Size")]
  3893.             public int refund2TextSize = 16;
  3894.  
  3895.             [JsonProperty(PropertyName = "Crosshair - Enabled")]
  3896.             public bool showCrosshair = true;
  3897.  
  3898.             [JsonProperty(PropertyName = "Crosshair - Image Url")]
  3899.             public string crosshairImageUrl = "https://i.imgur.com/SqLCJaQ.png";
  3900.  
  3901.             [JsonProperty(PropertyName = "Crosshair - Box - Min Anchor (in Rust Window)")]
  3902.             public string crosshairAnchorMin = "0.5 0.5";
  3903.  
  3904.             [JsonProperty(PropertyName = "Crosshair - Box - Max Anchor (in Rust Window)")]
  3905.             public string crosshairAnchorMax = "0.5 0.5";
  3906.  
  3907.             [JsonProperty(PropertyName = "Crosshair - Box - Min Offset (in Rust Window)")]
  3908.             public string crosshairOffsetMin = "-15 -15";
  3909.  
  3910.             [JsonProperty(PropertyName = "Crosshair - Box - Max Offset (in Rust Window)")]
  3911.             public string crosshairOffsetMax = "15 15";
  3912.  
  3913.             [JsonProperty(PropertyName = "Crosshair - Box - Image Color")]
  3914.             public string crosshairColor = "1 0 0 1";
  3915.  
  3916.             [JsonIgnore]
  3917.             public Vector2 Price2TextAnchorMin, Price2TextAnchorMax, Refund2TextAnchorMin, Refund2TextAnchorMax;
  3918.         }
  3919.  
  3920.         public class RemoveSettings
  3921.         {
  3922.             [JsonProperty(PropertyName = "Price Enabled")]
  3923.             public bool priceEnabled = true;
  3924.  
  3925.             [JsonProperty(PropertyName = "Refund Enabled")]
  3926.             public bool refundEnabled = true;
  3927.  
  3928.             [JsonProperty(PropertyName = "Refund Items In Entity Slot")]
  3929.             public bool refundSlot = true;
  3930.  
  3931.             [JsonProperty(PropertyName = "Allowed Building Grade")]
  3932.             public Dictionary<BuildingGrade.Enum, bool> validConstruction = new Dictionary<BuildingGrade.Enum, bool>();
  3933.  
  3934.             [JsonProperty(PropertyName = "Display Names (Refund & Price)")]
  3935.             public readonly Dictionary<string, string> displayNames = new Dictionary<string, string>();
  3936.  
  3937.             [JsonProperty(PropertyName = "Building Blocks Settings")]
  3938.             public Dictionary<string, BuildingBlocksSettings> buildingBlock = new Dictionary<string, BuildingBlocksSettings>();
  3939.  
  3940.             [JsonProperty(PropertyName = "Other Entity Settings")]
  3941.             public Dictionary<string, EntitySettings> entity = new Dictionary<string, EntitySettings>();
  3942.         }
  3943.  
  3944.         public class BuildingBlocksSettings
  3945.         {
  3946.             [JsonProperty(PropertyName = "Display Name")]
  3947.             public string displayName;
  3948.  
  3949.             [JsonProperty(PropertyName = "Building Grade")]
  3950.             public Dictionary<BuildingGrade.Enum, BuildingGradeSettings> buildingGrade = new Dictionary<BuildingGrade.Enum, BuildingGradeSettings>();
  3951.         }
  3952.  
  3953.         public class BuildingGradeSettings
  3954.         {
  3955.             [JsonProperty(PropertyName = "Price")]
  3956.             public object price;
  3957.  
  3958.             [JsonProperty(PropertyName = "Refund")]
  3959.             public object refund;
  3960.  
  3961.             [JsonIgnore]
  3962.             public float pricePercentage = -1, refundPercentage = -1;
  3963.  
  3964.             [JsonIgnore]
  3965.             public Dictionary<string, CurrencyInfo> priceDict, refundDict;
  3966.         }
  3967.  
  3968.         public class EntitySettings
  3969.         {
  3970.             [JsonProperty(PropertyName = "Remove Allowed")]
  3971.             public bool enabled;
  3972.  
  3973.             [JsonProperty(PropertyName = "Display Name")]
  3974.             public string displayName = string.Empty;
  3975.  
  3976.             [JsonProperty(PropertyName = "Price")]
  3977.             public object price;
  3978.  
  3979.             [JsonProperty(PropertyName = "Refund")]
  3980.             public object refund;
  3981.  
  3982.             [JsonIgnore]
  3983.             public Dictionary<string, CurrencyInfo> priceDict, refundDict;
  3984.         }
  3985.  
  3986.         protected override void LoadConfig()
  3987.         {
  3988.             base.LoadConfig();
  3989.             try
  3990.             {
  3991.                 PreprocessOldConfig();
  3992.                 _configData = Config.ReadObject<ConfigData>();
  3993.                 if (_configData == null)
  3994.                 {
  3995.                     LoadDefaultConfig();
  3996.                 }
  3997.                 else
  3998.                 {
  3999.                     UpdateConfigValues();
  4000.                 }
  4001.             }
  4002.             catch (Exception ex)
  4003.             {
  4004.                 PrintError($"The configuration file is corrupted. \n{ex}");
  4005.  
  4006.                 LoadDefaultConfig();
  4007.             }
  4008.             SaveConfig();
  4009.             PreprocessConfigValues();
  4010.         }
  4011.  
  4012.         protected override void LoadDefaultConfig()
  4013.         {
  4014.             PrintWarning("Creating a new configuration file");
  4015.             _configData = new ConfigData();
  4016.             _configData.version = Version;
  4017.         }
  4018.  
  4019.         protected override void SaveConfig()
  4020.         {
  4021.             Config.WriteObject(_configData);
  4022.         }
  4023.  
  4024.         private void PreprocessConfigValues()
  4025.         {
  4026.             _configData.ui.Price2TextAnchorMin = GetAnchor(_configData.ui.price2TextAnchorMin);
  4027.             _configData.ui.Price2TextAnchorMax = GetAnchor(_configData.ui.price2TextAnchorMax);
  4028.             _configData.ui.Refund2TextAnchorMin = GetAnchor(_configData.ui.refund2TextAnchorMin);
  4029.             _configData.ui.Refund2TextAnchorMax = GetAnchor(_configData.ui.refund2TextAnchorMax);
  4030.  
  4031.             foreach (var entry in _configData.remove.buildingBlock)
  4032.             {
  4033.                 foreach (var gradeEntry in entry.Value.buildingGrade)
  4034.                 {
  4035.                     var price = gradeEntry.Value.price;
  4036.                     float pricePercentage;
  4037.                     if (float.TryParse(price.ToString(), out pricePercentage))
  4038.                     {
  4039.                         gradeEntry.Value.pricePercentage = pricePercentage;
  4040.                     }
  4041.                     else
  4042.                     {
  4043.                         var priceDic = price as Dictionary<string, CurrencyInfo>;
  4044.                         if (priceDic != null)
  4045.                         {
  4046.                             gradeEntry.Value.priceDict = priceDic;
  4047.                         }
  4048.                         else
  4049.                         {
  4050.                             try
  4051.                             {
  4052.                                 gradeEntry.Value.priceDict = JsonConvert.DeserializeObject<Dictionary<string, CurrencyInfo>>(price.ToString());
  4053.                             }
  4054.                             catch (Exception e)
  4055.                             {
  4056.                                 gradeEntry.Value.priceDict = null;
  4057.                                 PrintError($"Wrong price format for '{gradeEntry.Key}' of '{entry.Key}' in 'Building Blocks Settings'. Error Message: {e.Message}");
  4058.                             }
  4059.                         }
  4060.                     }
  4061.  
  4062.                     var refund = gradeEntry.Value.refund;
  4063.                     float refundPercentage;
  4064.                     if (float.TryParse(refund.ToString(), out refundPercentage))
  4065.                     {
  4066.                         gradeEntry.Value.refundPercentage = refundPercentage;
  4067.                     }
  4068.                     else
  4069.                     {
  4070.                         var refundDic = refund as Dictionary<string, CurrencyInfo>;
  4071.                         if (refundDic != null)
  4072.                         {
  4073.                             gradeEntry.Value.refundDict = refundDic;
  4074.                         }
  4075.                         else
  4076.                         {
  4077.                             try
  4078.                             {
  4079.                                 gradeEntry.Value.refundDict = JsonConvert.DeserializeObject<Dictionary<string, CurrencyInfo>>(refund.ToString());
  4080.                             }
  4081.                             catch (Exception e)
  4082.                             {
  4083.                                 gradeEntry.Value.refundDict = null;
  4084.                                 PrintError($"Wrong refund format for '{gradeEntry.Key}' of '{entry.Key}' in 'Building Blocks Settings'. Error Message: {e.Message}");
  4085.                             }
  4086.                         }
  4087.                     }
  4088.                 }
  4089.             }
  4090.             foreach (var entry in _configData.remove.entity)
  4091.             {
  4092.                 var price = entry.Value.price;
  4093.                 var priceDic = price as Dictionary<string, CurrencyInfo>;
  4094.                 if (priceDic != null)
  4095.                 {
  4096.                     entry.Value.priceDict = priceDic;
  4097.                 }
  4098.                 else
  4099.                 {
  4100.                     try
  4101.                     {
  4102.                         entry.Value.priceDict = JsonConvert.DeserializeObject<Dictionary<string, CurrencyInfo>>(price.ToString());
  4103.                     }
  4104.                     catch (Exception e)
  4105.                     {
  4106.                         entry.Value.priceDict = null;
  4107.                         PrintError($"Wrong price format for '{entry.Key}' of '{entry.Key}' in 'Other Entity Settings'. Error Message: {e.Message}");
  4108.                     }
  4109.                 }
  4110.  
  4111.                 var refund = entry.Value.refund;
  4112.                 var refundDic = refund as Dictionary<string, CurrencyInfo>;
  4113.                 if (refundDic != null)
  4114.                 {
  4115.                     entry.Value.refundDict = refundDic;
  4116.                 }
  4117.                 else
  4118.                 {
  4119.                     try
  4120.                     {
  4121.                         entry.Value.refundDict = JsonConvert.DeserializeObject<Dictionary<string, CurrencyInfo>>(refund.ToString());
  4122.                     }
  4123.                     catch (Exception e)
  4124.                     {
  4125.                         entry.Value.refundDict = null;
  4126.                         PrintError($"Wrong refund format for '{entry.Key}' of '{entry.Key}' in 'Other Entity Settings'. Error Message: {e.Message}");
  4127.                     }
  4128.                 }
  4129.             }
  4130.         }
  4131.  
  4132.         private void UpdateConfigValues()
  4133.         {
  4134.             if (_configData.version < Version)
  4135.             {
  4136.                 if (_configData.version <= default(VersionNumber))
  4137.                 {
  4138.                     string prefix, prefixColor;
  4139.                     if (GetConfigValue(out prefix, "Chat Settings", "Chat Prefix") && GetConfigValue(out prefixColor, "Chat Settings", "Chat Prefix Color"))
  4140.                     {
  4141.                         _configData.chat.prefix = $"<color={prefixColor}>{prefix}</color>: ";
  4142.                     }
  4143.  
  4144.                     if (_configData.ui.removerToolAnchorMin == "0.1 0.55")
  4145.                     {
  4146.                         _configData.ui.removerToolAnchorMin = "0.04 0.55";
  4147.                     }
  4148.  
  4149.                     if (_configData.ui.removerToolAnchorMax == "0.4 0.95")
  4150.                     {
  4151.                         _configData.ui.removerToolAnchorMax = "0.37 0.95";
  4152.                     }
  4153.                 }
  4154.  
  4155.                 if (_configData.version <= new VersionNumber(4, 3, 22))
  4156.                 {
  4157.                     bool enabled;
  4158.                     if (GetConfigValue(out enabled, "Remove Mode Settings (Only one model works)", "Hammer Hit Mode"))
  4159.                     {
  4160.                         _configData.removerMode.meleeHitMode = true;
  4161.                         _configData.removerMode.meleeHitItemShortname = "hammer";
  4162.                     }
  4163.                     if (GetConfigValue(out enabled, "Remove Mode Settings (Only one model works)", "Hammer Hit Mode - Requires a hammer in your hand when remover tool is enabled"))
  4164.                     {
  4165.                         _configData.removerMode.meleeHitRequires = true;
  4166.                     }
  4167.                     if (GetConfigValue(out enabled, "Remove Mode Settings (Only one model works)", "Hammer Hit Mode - Disable remover tool when you are not holding a hammer"))
  4168.                     {
  4169.                         _configData.removerMode.meleeHitDisableInHand = true;
  4170.                     }
  4171.                 }
  4172.  
  4173.                 if (_configData.version <= new VersionNumber(4, 3, 23))
  4174.                 {
  4175.                     string value;
  4176.                     if (GetConfigValue(out value, "GUI", "Authorization Check - Box - Allowed Background"))
  4177.                     {
  4178.                         _configData.ui.allowedBackgroundColor = value == "0 1 0 0.8" ? "0.22 0.78 0.27 1" : value;
  4179.                     }
  4180.                     if (GetConfigValue(out value, "GUI", "Authorization Check - Box - Refused Background"))
  4181.                     {
  4182.                         _configData.ui.refusedBackgroundColor = value == "1 0 0 0.8" ? "0.78 0.22 0.27 1" : value;
  4183.                     }
  4184.                     if (_configData.ui.removeBackgroundColor == "0.42 0.88 0.88 1")
  4185.                     {
  4186.                         _configData.ui.removeBackgroundColor = "0.31 0.88 0.71 1";
  4187.                     }
  4188.                     if (_configData.ui.entityBackgroundColor == "0 0 0 0.8")
  4189.                     {
  4190.                         _configData.ui.entityBackgroundColor = "0.82 0.58 0.30 1";
  4191.                     }
  4192.                 }
  4193.                 _configData.version = Version;
  4194.             }
  4195.         }
  4196.  
  4197.         private bool GetConfigValue<T>(out T value, params string[] path)
  4198.         {
  4199.             var configValue = Config.Get(path);
  4200.             if (configValue != null)
  4201.             {
  4202.                 if (configValue is T)
  4203.                 {
  4204.                     value = (T)configValue;
  4205.                     return true;
  4206.                 }
  4207.                 try
  4208.                 {
  4209.                     value = Config.ConvertValue<T>(configValue);
  4210.                     return true;
  4211.                 }
  4212.                 catch (Exception ex)
  4213.                 {
  4214.                     PrintError($"GetConfigValue ERROR: path: {string.Join("\\", path)}\n{ex}");
  4215.                 }
  4216.             }
  4217.  
  4218.             value = default(T);
  4219.             return false;
  4220.         }
  4221.  
  4222.         private void SetConfigValue(params object[] pathAndTrailingValue)
  4223.         {
  4224.             Config.Set(pathAndTrailingValue);
  4225.         }
  4226.  
  4227.         #region Preprocess Old Config
  4228.  
  4229.         private void PreprocessOldConfig()
  4230.         {
  4231.             var config = Config.ReadObject<JObject>();
  4232.             if (config == null)
  4233.             {
  4234.                 return;
  4235.             }
  4236.             //Interface.Oxide.DataFileSystem.WriteObject(Name + "_old", jObject);
  4237.             VersionNumber oldVersion;
  4238.             if (GetConfigVersionPre(config, out oldVersion))
  4239.             {
  4240.                 if (oldVersion < Version)
  4241.                 {
  4242.                     //Fixed typos
  4243.                     if (oldVersion <= new VersionNumber(4, 3, 23))
  4244.                     {
  4245.                         foreach (RemoveType value in Enum.GetValues(typeof(RemoveType)))
  4246.                         {
  4247.                             if (value == RemoveType.None)
  4248.                             {
  4249.                                 continue;
  4250.                             }
  4251.                             bool enabled;
  4252.                             if (GetConfigValuePre(config, out enabled, "Remove Type Settings", value.ToString(), "Reset the time after removing a entity"))
  4253.                             {
  4254.                                 SetConfigValuePre(config, enabled, "Remove Type Settings", value.ToString(), "Reset the time after removing an entity");
  4255.                             }
  4256.                         }
  4257.                         Dictionary<string, object> values;
  4258.                         if (GetConfigValuePre(config, out values, "Permission Settings (Just for normal type)"))
  4259.                         {
  4260.                             foreach (var entry in values)
  4261.                             {
  4262.                                 object value;
  4263.                                 if (GetConfigValuePre(config, out value, "Permission Settings (Just for normal type)", entry.Key, "Reset the time after removing a entity"))
  4264.                                 {
  4265.                                     SetConfigValuePre(config, value, "Permission Settings (Just for normal type)", entry.Key, "Reset the time after removing an entity");
  4266.                                 }
  4267.                             }
  4268.                         }
  4269.                     }
  4270.  
  4271.                     if (oldVersion <= new VersionNumber(4, 3, 25))
  4272.                     {
  4273.                         bool enabled;
  4274.                         if (GetConfigValuePre(config, out enabled, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Show Crosshair"))
  4275.                         {
  4276.                             SetConfigValuePre(config, enabled, "GUI", "Crosshair - Enabled");
  4277.                         }
  4278.                         object value;
  4279.                         if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Image Url"))
  4280.                         {
  4281.                             SetConfigValuePre(config, value, "GUI", "Crosshair - Image Url");
  4282.                         }
  4283.                         if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Min Anchor (in Rust Window)"))
  4284.                         {
  4285.                             SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Min Anchor (in Rust Window)");
  4286.                         }
  4287.                         if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Max Anchor (in Rust Window)"))
  4288.                         {
  4289.                             SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Max Anchor (in Rust Window)");
  4290.                         }
  4291.                         if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Min Offset (in Rust Window)"))
  4292.                         {
  4293.                             SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Min Offset (in Rust Window)");
  4294.                         }
  4295.                         if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Max Offset (in Rust Window)"))
  4296.                         {
  4297.                             SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Max Offset (in Rust Window)");
  4298.                         }
  4299.                         if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Image Color"))
  4300.                         {
  4301.                             SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Image Color");
  4302.                         }
  4303.                     }
  4304.                     if (oldVersion <= new VersionNumber(4, 3, 32))
  4305.                     {
  4306.                         try
  4307.                         {
  4308.                             var permissions = GetConfigValue(config, "Permission Settings (Just for normal type)");
  4309.                             if (permissions != null)
  4310.                             {
  4311.                                 foreach (var perm in permissions)
  4312.                                 {
  4313.                                     var maxRemovables = perm.Value.Value<int>("Max Removable Objects (0 = Unlimit)");
  4314.                                     perm.Value["Max Removable Objects (0 = Unlimited)"] = JToken.FromObject(maxRemovables);
  4315.                                 }
  4316.                             }
  4317.                             var entities = GetConfigValue(config, "Remove Info (Refund & Price)", "Other Entity Settings");
  4318.                             if (entities != null)
  4319.                             {
  4320.                                 foreach (var entity in entities)
  4321.                                 {
  4322.                                     var price = entity.Value.Value<JObject>("Price");
  4323.                                     var priceDict = price?.ToObject<Dictionary<string, int>>();
  4324.                                     if (priceDict != null)
  4325.                                     {
  4326.                                         entity.Value["Price"] = JToken.FromObject(priceDict.ToDictionary(x => x.Key, y => new CurrencyInfo(y.Value)));
  4327.                                     }
  4328.                                     var refund = entity.Value.Value<JObject>("Refund");
  4329.                                     var refundDict = refund?.ToObject<Dictionary<string, int>>();
  4330.                                     if (refundDict != null)
  4331.                                     {
  4332.                                         entity.Value["Refund"] = JToken.FromObject(refundDict.ToDictionary(x => x.Key, y => new CurrencyInfo(y.Value)));
  4333.                                     }
  4334.                                 }
  4335.                             }
  4336.  
  4337.                             var buildingBlocks = GetConfigValue(config, "Remove Info (Refund & Price)", "Building Blocks Settings");
  4338.                             if (buildingBlocks != null)
  4339.                             {
  4340.                                 foreach (var item in buildingBlocks)
  4341.                                 {
  4342.                                     var buildingGrades = item.Value.Value<JObject>("Building Grade");
  4343.                                     foreach (var buildingGrade in buildingGrades)
  4344.                                     {
  4345.                                         float percentage;
  4346.                                         var price = buildingGrade.Value.Value<object>("Price");
  4347.                                         if (price != null && !float.TryParse(price.ToString(), out percentage))
  4348.                                         {
  4349.                                             var target = buildingGrade.Value["Price"] as JObject;
  4350.                                             var dict = target?.ToObject<Dictionary<string, int>>();
  4351.                                             if (dict != null)
  4352.                                             {
  4353.                                                 buildingGrade.Value["Price"] = JToken.FromObject(dict.ToDictionary(x => x.Key, y => new CurrencyInfo(y.Value)));
  4354.                                             }
  4355.                                         }
  4356.                                         var refund = buildingGrade.Value.Value<object>("Refund");
  4357.                                         if (refund != null && !float.TryParse(refund.ToString(), out percentage))
  4358.                                         {
  4359.                                             var target = buildingGrade.Value["Refund"] as JObject;
  4360.                                             var dict = target?.ToObject<Dictionary<string, int>>();
  4361.                                             if (dict != null)
  4362.                                             {
  4363.                                                 buildingGrade.Value["Refund"] = JToken.FromObject(dict.ToDictionary(x => x.Key, y => new CurrencyInfo(y.Value)));
  4364.                                             }
  4365.                                         }
  4366.                                     }
  4367.                                 }
  4368.                             }
  4369.                         }
  4370.                         catch
  4371.                         {
  4372.                             // ignored
  4373.                         }
  4374.                     }
  4375.                     Config.WriteObject(config);
  4376.                     // Interface.Oxide.DataFileSystem.WriteObject(Name + "_new", jObject);
  4377.                 }
  4378.             }
  4379.         }
  4380.  
  4381.         private JObject GetConfigValue(JObject config, params string[] path)
  4382.         {
  4383.             if (path.Length < 1)
  4384.             {
  4385.                 throw new ArgumentException("path is empty");
  4386.             }
  4387.  
  4388.             try
  4389.             {
  4390.                 JToken jToken;
  4391.                 if (!config.TryGetValue(path[0], out jToken))
  4392.                 {
  4393.                     return null;
  4394.                 }
  4395.  
  4396.                 for (var i = 1; i < path.Length; i++)
  4397.                 {
  4398.                     var jObject = jToken as JObject;
  4399.                     if (jObject == null || !jObject.TryGetValue(path[i], out jToken))
  4400.                     {
  4401.                         return null;
  4402.                     }
  4403.                 }
  4404.                 return jToken as JObject;
  4405.             }
  4406.             catch (Exception ex)
  4407.             {
  4408.                 PrintError($"GetConfigValue ERROR: path: {string.Join("\\", path)}\n{ex}");
  4409.             }
  4410.             return null;
  4411.         }
  4412.  
  4413.         private bool GetConfigValuePre<T>(JObject config, out T value, params string[] path)
  4414.         {
  4415.             if (path.Length < 1)
  4416.             {
  4417.                 throw new ArgumentException("path is empty");
  4418.             }
  4419.  
  4420.             try
  4421.             {
  4422.                 JToken jToken;
  4423.                 if (!config.TryGetValue(path[0], out jToken))
  4424.                 {
  4425.                     value = default(T);
  4426.                     return false;
  4427.                 }
  4428.  
  4429.                 for (var i = 1; i < path.Length; i++)
  4430.                 {
  4431.                     var jObject = jToken.ToObject<JObject>();
  4432.                     if (jObject == null || !jObject.TryGetValue(path[i], out jToken))
  4433.                     {
  4434.                         value = default(T);
  4435.                         return false;
  4436.                     }
  4437.                 }
  4438.                 value = jToken.ToObject<T>();
  4439.                 return true;
  4440.             }
  4441.             catch (Exception ex)
  4442.             {
  4443.                 PrintError($"GetConfigValuePre ERROR: path: {string.Join("\\", path)}\n{ex}");
  4444.             }
  4445.             value = default(T);
  4446.             return false;
  4447.         }
  4448.  
  4449.         private void SetConfigValuePre(JObject config, object value, params string[] path)
  4450.         {
  4451.             if (path.Length < 1)
  4452.             {
  4453.                 throw new ArgumentException("path is empty");
  4454.             }
  4455.  
  4456.             try
  4457.             {
  4458.                 JToken jToken;
  4459.                 if (!config.TryGetValue(path[0], out jToken))
  4460.                 {
  4461.                     if (path.Length == 1)
  4462.                     {
  4463.                         jToken = JToken.FromObject(value);
  4464.                         config.Add(path[0], jToken);
  4465.                         return;
  4466.                     }
  4467.                     jToken = new JObject();
  4468.                     config.Add(path[0], jToken);
  4469.                 }
  4470.  
  4471.                 for (var i = 1; i < path.Length - 1; i++)
  4472.                 {
  4473.                     var jObject = jToken as JObject;
  4474.                     if (jObject == null || !jObject.TryGetValue(path[i], out jToken))
  4475.                     {
  4476.                         jToken = new JObject();
  4477.                         jObject?.Add(path[i], jToken);
  4478.                     }
  4479.                 }
  4480.                 var targetToken = jToken as JObject;
  4481.                 if (targetToken != null)
  4482.                 {
  4483.                     targetToken[path[path.Length - 1]] = JToken.FromObject(value);
  4484.                 }
  4485.                 // (jToken as JObject)?.TryAdd(path[path.Length - 1], JToken.FromObject(value));
  4486.             }
  4487.             catch (Exception ex)
  4488.             {
  4489.                 PrintError($"SetConfigValuePre ERROR: value: {value} path: {string.Join("\\", path)}\n{ex}");
  4490.             }
  4491.         }
  4492.  
  4493.         private bool GetConfigVersionPre(JObject config, out VersionNumber version)
  4494.         {
  4495.             try
  4496.             {
  4497.                 JToken jToken;
  4498.                 if (config.TryGetValue("Version", out jToken))
  4499.                 {
  4500.                     version = jToken.ToObject<VersionNumber>();
  4501.                     return true;
  4502.                 }
  4503.             }
  4504.             catch
  4505.             {
  4506.                 // ignored
  4507.             }
  4508.             version = default(VersionNumber);
  4509.             return false;
  4510.         }
  4511.  
  4512.         #endregion Preprocess Old Config
  4513.  
  4514.         #endregion ConfigurationFile
  4515.  
  4516.         #region LanguageFile
  4517.  
  4518.         private void Print(BasePlayer player, string message)
  4519.         {
  4520.             Player.Message(player, message, _configData.chat.prefix, _configData.chat.steamIDIcon);
  4521.         }
  4522.  
  4523.         private void Print(ConsoleSystem.Arg arg, string message)
  4524.         {
  4525.             //SendReply(arg, message);
  4526.             var player = arg.Player();
  4527.             if (player == null)
  4528.             {
  4529.                 Puts(message);
  4530.             }
  4531.             else
  4532.             {
  4533.                 PrintToConsole(player, message);
  4534.             }
  4535.         }
  4536.  
  4537.         private string Lang(string key, string id = null, params object[] args)
  4538.         {
  4539.             try
  4540.             {
  4541.                 return string.Format(lang.GetMessage(key, this, id), args);
  4542.             }
  4543.             catch (Exception)
  4544.             {
  4545.                 PrintError($"Error in the language formatting of '{key}'. (userid: {id}. lang: {lang.GetLanguage(id)}. args: {string.Join(" ,", args)})");
  4546.                 throw;
  4547.             }
  4548.         }
  4549.  
  4550.         protected override void LoadDefaultMessages()
  4551.         {
  4552.             lang.RegisterMessages(new Dictionary<string, string>
  4553.             {
  4554.                 ["NotAllowed"] = "You don't have '{0}' permission to use this command.",
  4555.                 ["TargetDisabled"] = "{0}'s Remover Tool has been disabled.",
  4556.                 ["TargetEnabled"] = "{0} is now using Remover Tool; Enabled for {1} seconds (Max Removable Objects: {2}, Remove Type: {3}).",
  4557.                 ["ToolDisabled"] = "Remover Tool has been disabled.",
  4558.                 ["ToolEnabled"] = "Remover Tool enabled for {0} seconds (Max Removable Objects: {1}, Remove Type: {2}).",
  4559.                 ["Cooldown"] = "You need to wait {0} seconds before using Remover Tool again.",
  4560.                 ["CurrentlyDisabled"] = "Remover Tool is currently disabled.",
  4561.                 ["EntityLimit"] = "Entity limit reached, you have removed {0} entities, Remover Tool was automatically disabled.",
  4562.                 ["MeleeToolNotHeld"] = "You need to be holding a {0} in order to use the Remover Tool",
  4563.                 ["SpecificToolNotHeld"] = "You need to be holding a {0} in order to use the Remover Tool.",
  4564.  
  4565.                 ["StartRemoveAll"] = "Start running RemoveAll, please wait.",
  4566.                 ["StartRemoveStructure"] = "Start running RemoveStructure, please wait.",
  4567.                 ["StartRemoveExternal"] = "Start running RemoveExternal, please wait.",
  4568.                 ["AlreadyRemoveAll"] = "There is already a RemoveAll running, please wait.",
  4569.                 ["AlreadyRemoveStructure"] = "There is already a RemoveStructure running, please wait.",
  4570.                 ["AlreadyRemoveExternal"] = "There is already a RemoveExternal running, please wait.",
  4571.                 ["CompletedRemoveAll"] = "You've successfully removed {0} entities using RemoveAll.",
  4572.                 ["CompletedRemoveStructure"] = "You've successfully removed {0} entities using RemoveStructure.",
  4573.                 ["CompletedRemoveExternal"] = "You've successfully removed {0} entities using RemoveExternal.",
  4574.  
  4575.                 ["CanRemove"] = "You can remove this entity.",
  4576.                 ["NotEnoughCost"] = "Can't remove: You don't have enough resources.",
  4577.                 ["EntityDisabled"] = "Can't remove: Server has disabled the entity from being removed.",
  4578.                 ["DamagedEntity"] = "Can't remove: Server has disabled damaged objects from being removed.",
  4579.                 ["BeBlocked"] = "Can't remove: An external plugin blocked the usage.",
  4580.                 ["InvalidEntity"] = "Can't remove: No valid entity targeted.",
  4581.                 ["NotFoundOrFar"] = "Can't remove: The entity is not found or too far away.",
  4582.                 ["StorageNotEmpty"] = "Can't remove: The entity storage is not empty.",
  4583.                 ["RaidBlocked"] = "Can't remove: Raid blocked.",
  4584.                 ["CombatBlocked"] = "Can't remove: Combat blocked.",
  4585.                 ["NotRemoveAccess"] = "Can't remove: You don't have any rights to remove this.",
  4586.                 ["NotStructure"] = "Can't remove: The entity is not a structure.",
  4587.                 ["NotExternalWall"] = "Can't remove: The entity is not an external wall.",
  4588.                 ["EntityTimeLimit"] = "Can't remove: The entity was built more than {0} seconds ago.",
  4589.                 //["Can'tOpenAllLocks"] = "Can't remove: There is a lock in the building that you cannot open.",
  4590.                 ["CantPay"] = "Can't remove: Paying system crashed! Contact an administrator with the time and date to help him understand what happened.",
  4591.                 //["UsageOfRemove"] = "You have to hold a hammer in your hand and press the left mouse button.",
  4592.  
  4593.                 ["Refund"] = "Refund:",
  4594.                 ["Nothing"] = "Nothing",
  4595.                 ["Price"] = "Price:",
  4596.                 ["Free"] = "Free",
  4597.                 ["TimeLeft"] = "Timeleft: {0}s\nRemoved: {1}",
  4598.                 ["RemoverToolType"] = "Remover Tool ({0})",
  4599.                 ["Unlimit"] = "∞",
  4600.  
  4601.                 ["SyntaxError"] = "Syntax error, please type '<color=#ce422b>/{0} <help | h></color>' to view help",
  4602.                 ["Syntax"] = "<color=#ce422b>/{0} [time (seconds)]</color> - Enable RemoverTool ({1})",
  4603.                 ["Syntax1"] = "<color=#ce422b>/{0} <admin | a> [time (seconds)]</color> - Enable RemoverTool ({1})",
  4604.                 ["Syntax2"] = "<color=#ce422b>/{0} <all> [time (seconds)]</color> - Enable RemoverTool ({1})",
  4605.                 ["Syntax3"] = "<color=#ce422b>/{0} <structure | s> [time (seconds)]</color> - Enable RemoverTool ({1})",
  4606.                 ["Syntax4"] = "<color=#ce422b>/{0} <external | e> [time (seconds)]</color> - Enable RemoverTool ({1})"
  4607.             }, this);
  4608.  
  4609.             lang.RegisterMessages(new Dictionary<string, string>
  4610.             {
  4611.                 ["NotAllowed"] = "您没有 '{0}' 权限来使用该命令",
  4612.                 ["TargetDisabled"] = "'{0}' 的拆除工具已禁用",
  4613.                 ["TargetEnabled"] = "'{0}' 的拆除工具已启用 {1} 秒 (可拆除数: {2}, 拆除模式: {3}).",
  4614.                 ["ToolDisabled"] = "您的拆除工具已禁用",
  4615.                 ["ToolEnabled"] = "您的拆除工具已启用 {0} 秒 (可拆除数: {1}, 拆除模式: {2}).",
  4616.                 ["Cooldown"] = "您需要等待 {0} 秒才可以再次使用拆除工具",
  4617.                 ["CurrentlyDisabled"] = "服务器当前已禁用了拆除工具",
  4618.                 ["EntityLimit"] = "您已经拆除了 '{0}' 个实体,拆除工具已自动禁用",
  4619.                 ["MeleeToolNotHeld"] = "您必须拿着{0}才可以使用拆除工具",
  4620.                 ["SpecificToolNotHeld"] = "您必须拿着{0}才可以使用拆除工具",
  4621.  
  4622.                 ["StartRemoveAll"] = "开始运行 '所有拆除',请稍等片刻",
  4623.                 ["StartRemoveStructure"] = "开始运行 '建筑拆除',请稍等片刻",
  4624.                 ["StartRemoveExternal"] = "开始运行 '外墙拆除',请稍等片刻",
  4625.                 ["AlreadyRemoveAll"] = "已经有一个 '所有拆除' 正在运行,请稍等片刻",
  4626.                 ["AlreadyRemoveStructure"] = "已经有一个 '建筑拆除' 正在运行,请稍等片刻",
  4627.                 ["AlreadyRemoveExternal"] = "已经有一个 '外墙拆除' 正在运行,请稍等片刻",
  4628.                 ["CompletedRemoveAll"] = "您使用 '所有拆除' 成功拆除了 {0} 个实体",
  4629.                 ["CompletedRemoveStructure"] = "您使用 '建筑拆除' 成功拆除了 {0} 个实体",
  4630.                 ["CompletedRemoveExternal"] = "您使用 '外墙拆除' 成功拆除了 {0} 个实体",
  4631.  
  4632.                 ["CanRemove"] = "您可以拆除该实体",
  4633.                 ["NotEnoughCost"] = "无法拆除: 拆除所需资源不足",
  4634.                 ["EntityDisabled"] = "无法拆除: 服务器已禁用拆除这种实体",
  4635.                 ["DamagedEntity"] = "无法拆除: 服务器已禁用拆除已损坏的实体",
  4636.                 ["BeBlocked"] = "无法拆除: 其他插件阻止您拆除该实体",
  4637.                 ["InvalidEntity"] = "无法拆除: 无效的实体",
  4638.                 ["NotFoundOrFar"] = "无法拆除: 没有找到实体或者距离太远",
  4639.                 ["StorageNotEmpty"] = "无法拆除: 该实体内含有物品",
  4640.                 ["RaidBlocked"] = "无法拆除: 拆除工具被突袭阻止了",
  4641.                 ["CombatBlocked"] = "无法拆除: 拆除工具被战斗阻止了",
  4642.                 ["NotRemoveAccess"] = "无法拆除: 您无权拆除该实体",
  4643.                 ["NotStructure"] = "无法拆除: 该实体不是建筑物",
  4644.                 ["NotExternalWall"] = "无法拆除: 该实体不是外高墙",
  4645.                 ["EntityTimeLimit"] = "无法拆除: 该实体的存活时间大于 {0} 秒",
  4646.                 //["Can'tOpenAllLocks"] = "无法拆除: 该建筑中有您无法打开的锁",
  4647.                 ["CantPay"] = "无法拆除: 支付失败,请联系管理员,告诉他详情",
  4648.  
  4649.                 ["Refund"] = "退还:",
  4650.                 ["Nothing"] = "没有",
  4651.                 ["Price"] = "价格:",
  4652.                 ["Free"] = "免费",
  4653.                 ["TimeLeft"] = "剩余时间: {0}s\n已拆除数: {1} ",
  4654.                 ["RemoverToolType"] = "拆除工具 ({0})",
  4655.                 ["Unlimit"] = "∞",
  4656.  
  4657.                 ["SyntaxError"] = "语法错误,输入 '<color=#ce422b>/{0} <help | h></color>' 查看帮助",
  4658.                 ["Syntax"] = "<color=#ce422b>/{0} [time (seconds)]</color> - 启用拆除工具 ({1})",
  4659.                 ["Syntax1"] = "<color=#ce422b>/{0} <admin | a> [time (seconds)]</color> - 启用拆除工具 ({1})",
  4660.                 ["Syntax2"] = "<color=#ce422b>/{0} <all> [time (seconds)]</color> - 启用拆除工具 ({1})",
  4661.                 ["Syntax3"] = "<color=#ce422b>/{0} <structure | s> [time (seconds)]</color> - 启用拆除工具 ({1})",
  4662.                 ["Syntax4"] = "<color=#ce422b>/{0} <external | e> [time (seconds)]</color> - 启用拆除工具 ({1})"
  4663.             }, this, "zh-CN");
  4664.             lang.RegisterMessages(new Dictionary<string, string>
  4665.             {
  4666.                 ["NotAllowed"] = "У вас нет разрешения '{0}' чтобы использовать эту команду.",
  4667.                 ["TargetDisabled"] = "{0}'s Remover Tool отключен.",
  4668.                 ["TargetEnabled"] = "{0} теперь использует Remover Tool; Включено на {1} секунд (Макс. объектов для удаления: {2}, Тип удаления: {3}).",
  4669.                 ["ToolDisabled"] = "Remover Tool отключен.",
  4670.                 ["ToolEnabled"] = "Remover Tool включен на {0} секунд (Макс. объектов для удаления: {1}, Тип удаления: {2}).",
  4671.                 ["Cooldown"] = "Необходимо подождать {0} секунд, прежде чем использовать Remover Tool снова.",
  4672.                 ["CurrentlyDisabled"] = "Remover Tool в данный момент отключен.",
  4673.                 ["EntityLimit"] = "Достигнут предел, удалено {0} объектов, Remover Tool автоматически отключен.",
  4674.                 ["MeleeToolNotHeld"] = "Вы должны держать {0}, чтобы использовать инструмент для удаления.",
  4675.                 ["SpecificToolNotHeld"] = "Вы должны держать {0}, чтобы использовать инструмент для удаления.",
  4676.  
  4677.                 ["StartRemoveAll"] = "Запускается RemoveAll, пожалуйста, подождите.",
  4678.                 ["StartRemoveStructure"] = "Запускается RemoveStructure, пожалуйста, подождите.",
  4679.                 ["StartRemoveExternal"] = "Запускается RemoveExternal, пожалуйста, подождите.",
  4680.                 ["AlreadyRemoveAll"] = "RemoveAll уже выполняется, пожалуйста, подождите.",
  4681.                 ["AlreadyRemoveStructure"] = "RemoveStructure уже выполняется, пожалуйста, подождите.",
  4682.                 ["AlreadyRemoveExternal"] = "RemoveExternal уже выполняется, пожалуйста, подождите.",
  4683.                 ["CompletedRemoveAll"] = "Вы успешно удалили {0} объектов используя RemoveAll.",
  4684.                 ["CompletedRemoveStructure"] = "Вы успешно удалили {0} объектов используя RemoveStructure.",
  4685.                 ["CompletedRemoveExternal"] = "Вы успешно удалили {0} объектов используя RemoveExternal.",
  4686.  
  4687.                 ["CanRemove"] = "Вы можете удалить этот объект.",
  4688.                 ["NotEnoughCost"] = "Нельзя удалить: У вас не достаточно ресурсов.",
  4689.                 ["EntityDisabled"] = "Нельзя удалить: Сервер отключил возможность удаления этого объекта.",
  4690.                 ["DamagedEntity"] = "Нельзя удалить: Сервер отключил возможность удалять повреждённые объекты.",
  4691.                 ["BeBlocked"] = "Нельзя удалить: Внешний plugin блокирует использование.",
  4692.                 ["InvalidEntity"] = "Нельзя удалить: Неверный объект.",
  4693.                 ["NotFoundOrFar"] = "Нельзя удалить: Объект не найден, либо слишком далеко.",
  4694.                 ["StorageNotEmpty"] = "Нельзя удалить: Хранилище объекта не пусто.",
  4695.                 ["RaidBlocked"] = "Нельзя удалить: Рейды остановки.",
  4696.                 ["CombatBlocked"] = "Нельзя удалить: боевые остановки",
  4697.                 ["NotRemoveAccess"] = "Нельзя удалить: У вас нет прав удалять это.",
  4698.                 ["NotStructure"] = "Нельзя удалить: Объект не конструкция.",
  4699.                 ["NotExternalWall"] = "Нельзя удалить: Объект не внешняя стена.",
  4700.                 ["EntityTimeLimit"] = "Нельзя удалить: Объект был построен более {0} секунд назад.",
  4701.                 //["Can'tOpenAllLocks"] = "Нельзя удалить: в здании есть замок, который вы не можете открыть",
  4702.                 ["CantPay"] = "Нельзя удалить: Система оплаты дала сбой! Свяжитесь с админом указав дату и время, чтобы помочь ему понять что случилось.",
  4703.  
  4704.                 ["Refund"] = "Возврат:",
  4705.                 ["Nothing"] = "Ничего",
  4706.                 ["Price"] = "Цена:",
  4707.                 ["Free"] = "Бесплатно",
  4708.                 ["TimeLeft"] = "Осталось времени: {0}s\nУдалено: {1}",
  4709.                 ["RemoverToolType"] = "Remover Tool ({0})",
  4710.                 ["Unlimit"] = "∞",
  4711.  
  4712.                 ["SyntaxError"] = "Синтаксическая ошибка! Пожалуйста, введите '<color=#ce422b>/{0} <help | h></color>' для отображения помощи",
  4713.                 ["Syntax"] = "<color=#ce422b>/{0} [время (секунд)]</color> - Включить RemoverTool ({1})",
  4714.                 ["Syntax1"] = "<color=#ce422b>/{0} <admin | a> [время (секунд)]</color> - Включить RemoverTool ({1})",
  4715.                 ["Syntax2"] = "<color=#ce422b>/{0} <all> [время (секунд)]</color> - Включить RemoverTool ({1})",
  4716.                 ["Syntax3"] = "<color=#ce422b>/{0} <structure | s> [время (секунд)]</color> - Включить RemoverTool ({1})",
  4717.                 ["Syntax4"] = "<color=#ce422b>/{0} <external | e> [время (секунд)]</color> - Включить RemoverTool ({1})"
  4718.             }, this, "ru");
  4719.  
  4720.             lang.RegisterMessages(new Dictionary<string, string>
  4721.             {
  4722.                 ["NotAllowed"] = "No tienes permiso '{0}' para usar este comando.",
  4723.                 ["TargetDisabled"] = "La herramienta de eliminación de {0} ha sido desactivada.",
  4724.                 ["TargetEnabled"] = "{0} ahora está usando la herramienta de eliminación; habilitada durante {1} segundos (Máximo de objetos removibles: {2}, Tipo de eliminación: {3}).",
  4725.                 ["ToolDisabled"] = "La herramienta de eliminación ha sido desactivada.",
  4726.                 ["ToolEnabled"] = "Herramienta de eliminación habilitada durante {0} segundos (Máximo de objetos removibles: {1}, Tipo de eliminación: {2}).",
  4727.                 ["Cooldown"] = "Debes esperar {0} segundos antes de usar la herramienta de eliminación nuevamente.",
  4728.                 ["CurrentlyDisabled"] = "La herramienta de eliminación está actualmente desactivada.",
  4729.                 ["EntityLimit"] = "Límite de entidades alcanzado, has eliminado {0} entidades, la herramienta de eliminación se ha desactivado automáticamente.",
  4730.                 ["MeleeToolNotHeld"] = "Debes estar sosteniendo un(a) {0} para usar la herramienta de eliminación",
  4731.                 ["SpecificToolNotHeld"] = "Debes estar sosteniendo un(a) {0} para usar la herramienta de eliminación.",
  4732.  
  4733.                 ["StartRemoveAll"] = "Iniciando eliminación total, por favor espera.",
  4734.                 ["StartRemoveStructure"] = "Iniciando eliminación de estructura, por favor espera.",
  4735.                 ["StartRemoveExternal"] = "Iniciando eliminación de externos, por favor espera.",
  4736.                 ["AlreadyRemoveAll"] = "Ya hay una eliminación total en curso, por favor espera.",
  4737.                 ["AlreadyRemoveStructure"] = "Ya hay una eliminación de estructura en curso, por favor espera.",
  4738.                 ["AlreadyRemoveExternal"] = "Ya hay una eliminación de externos en curso, por favor espera.",
  4739.                 ["CompletedRemoveAll"] = "Has eliminado exitosamente {0} entidades utilizando la eliminación total.",
  4740.                 ["CompletedRemoveStructure"] = "Has eliminado exitosamente {0} entidades utilizando la eliminación de estructura.",
  4741.                 ["CompletedRemoveExternal"] = "Has eliminado exitosamente {0} entidades utilizando la eliminación de externos.",
  4742.  
  4743.                 ["CanRemove"] = "Puedes eliminar esta entidad.",
  4744.                 ["NotEnoughCost"] = "No se puede eliminar: no tienes suficientes recursos.",
  4745.                 ["EntityDisabled"] = "No se puede eliminar: el servidor ha desactivado la eliminación de esta entidad.",
  4746.                 ["DamagedEntity"] = "No se puede eliminar: el servidor ha desactivado la eliminación de objetos dañados.",
  4747.                 ["BeBlocked"] = "No se puede eliminar: un complemento externo bloqueó su uso.",
  4748.                 ["InvalidEntity"] = "No se puede eliminar: no se ha seleccionado una entidad válida.",
  4749.                 ["NotFoundOrFar"] = "No se puede eliminar: la entidad no se encuentra o está demasiado lejos.",
  4750.                 ["StorageNotEmpty"] = "No se puede eliminar: el almacenamiento de la entidad no está vacío.",
  4751.                 ["RaidBlocked"] = "No se puede eliminar: bloqueado durante un asalto.",
  4752.                 ["CombatBlocked"] = "No se puede eliminar: bloqueado durante el combate.",
  4753.                 ["NotRemoveAccess"] = "No se puede eliminar: no tienes permisos para eliminar esto.",
  4754.                 ["NotStructure"] = "No se puede eliminar: la entidad no es una estructura.",
  4755.                 ["NotExternalWall"] = "No se puede eliminar: la entidad no es una pared externa.",
  4756.                 ["EntityTimeLimit"] = "No se puede eliminar: la entidad fue construida hace más de {0} segundos.",
  4757.                 //["Can'tOpenAllLocks"] = "No se puede eliminar: hay una cerradura en el edificio que no puedes abrir.",
  4758.                 ["CantPay"] = "No se puede eliminar: ¡el sistema de pago ha fallado! Contacta a un administrador con la fecha y hora para ayudarlo a entender lo que sucedió.",
  4759.                 //["UsageOfRemove"] = "Debes sostener un martillo en tu mano y presionar el botón izquierdo del mouse.",
  4760.  
  4761.                 ["Refund"] = "Reembolso:",
  4762.                 ["Nothing"] = "Nada",
  4763.                 ["Price"] = "Precio:",
  4764.                 ["Free"] = "Gratis",
  4765.                 ["TimeLeft"] = "Tiempo restante: {0}s\nEliminado: {1}",
  4766.                 ["RemoverToolType"] = "Herramienta de eliminación ({0})",
  4767.                 ["Unlimit"] = "∞",
  4768.  
  4769.                 ["SyntaxError"] = "Error de sintaxis, por favor escribe '<color=#ce422b>/{0} <ayuda | h></color>' para ver la ayuda",
  4770.                 ["Syntax"] = "<color=#ce422b>/{0} [tiempo (segundos)]</color> - Habilitar la herramienta de eliminación ({1})",
  4771.                 ["Syntax1"] = "<color=#ce422b>/{0} <admin | a> [tiempo (segundos)]</color> - Habilitar la herramienta de eliminación ({1})",
  4772.                 ["Syntax2"] = "<color=#ce422b>/{0} <todos> [tiempo (segundos)]</color> - Habilitar la herramienta de eliminación ({1})",
  4773.                 ["Syntax3"] = "<color=#ce422b>/{0} <estructura | s> [tiempo (segundos)]</color> - Habilitar la herramienta de eliminación ({1})",
  4774.                 ["Syntax4"] = "<color=#ce422b>/{0} <externo | e> [tiempo (segundos)]</color> - Habilitar la herramienta de eliminación ({1})"
  4775.             }, this, "es");
  4776.         }
  4777.  
  4778.         #endregion LanguageFile
  4779.     }
  4780. }
Add Comment
Please, Sign In to add comment