Guest User

RemoverTool.cs fix v2

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