using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using Facepunch; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Oxide.Core; using Oxide.Core.Plugins; using Oxide.Game.Rust; using Oxide.Game.Rust.Cui; using UnityEngine; namespace Oxide.Plugins { [Info("Remover Tool", "Reneb/Fuji/Arainrr", "4.3.24", ResourceId = 651)] [Description("Building and entity removal tool")] public class RemoverTool : RustPlugin { #region Fields [PluginReference] private readonly Plugin Friends, ServerRewards, Clans, Economics, ImageLibrary, BuildingOwners; private const string PERMISSION_ALL = "removertool.all"; private const string PERMISSION_ADMIN = "removertool.admin"; private const string PERMISSION_NORMAL = "removertool.normal"; private const string PERMISSION_TARGET = "removertool.target"; private const string PERMISSION_EXTERNAL = "removertool.external"; private const string PERMISSION_OVERRIDE = "removertool.override"; private const string PERMISSION_STRUCTURE = "removertool.structure"; private const string PREFAB_ITEM_DROP = "assets/prefabs/misc/item drop/item_drop.prefab"; private const int LAYER_ALL = 1 << 0 | 1 << 8 | 1 << 21; private const int LAYER_TARGET = ~(1 << 2 | 1 << 3 | 1 << 4 | 1 << 10 | 1 << 18 | 1 << 28 | 1 << 29); private static RemoverTool rt; private static BUTTON removeButton; private static RemoveMode removeMode; private bool removeOverride; private Coroutine removeAllCoroutine; private Coroutine removeStructureCoroutine; private Coroutine removeExternalCoroutine; private Coroutine removePlayerEntityCoroutine; private Hash entitySpawnedTimes; private Hash lastBlockedPlayers; private Hash lastAttackedBuildings; private readonly Hash cooldownTimes = new Hash(); //Reduce boxing private static readonly object True = true, False = false, Null = null; private enum RemoveMode { None, NoHeld, MeleeHit, SpecificTool } private enum RemoveType { None, All, Admin, Normal, External, Structure } private enum RemovePlayerEntityType { All, Cupboard, Building, } #endregion Fields #region Oxide Hooks private void Init() { rt = this; LoadDefaultMessages(); permission.RegisterPermission(PERMISSION_ALL, this); permission.RegisterPermission(PERMISSION_ADMIN, this); permission.RegisterPermission(PERMISSION_NORMAL, this); permission.RegisterPermission(PERMISSION_TARGET, this); permission.RegisterPermission(PERMISSION_OVERRIDE, this); permission.RegisterPermission(PERMISSION_EXTERNAL, this); permission.RegisterPermission(PERMISSION_STRUCTURE, this); foreach (var perm in configData.permS.Keys) { if (!permission.PermissionExists(perm, this)) permission.RegisterPermission(perm, this); } cmd.AddChatCommand(configData.chatS.command, this, nameof(CmdRemove)); Unsubscribe(nameof(OnEntityDeath)); Unsubscribe(nameof(OnHammerHit)); Unsubscribe(nameof(OnEntitySpawned)); Unsubscribe(nameof(OnEntityKill)); Unsubscribe(nameof(OnPlayerAttack)); Unsubscribe(nameof(OnActiveItemChanged)); } private void OnServerInitialized() { Initialize(); UpdateConfig(); removeMode = RemoveMode.None; if (configData.removerModeS.noHeldMode) removeMode = RemoveMode.NoHeld; if (configData.removerModeS.meleeHitMode) removeMode = RemoveMode.MeleeHit; if (configData.removerModeS.specificToolMode) removeMode = RemoveMode.SpecificTool; if (removeMode == RemoveMode.MeleeHit) { BaseMelee beseMelee; ItemDefinition itemDefinition; if (string.IsNullOrEmpty(configData.removerModeS.meleeHitItemShortname) || (itemDefinition = ItemManager.FindItemDefinition(configData.removerModeS.meleeHitItemShortname)) == null || (beseMelee = itemDefinition.GetComponent()?.entityPrefab.Get()?.GetComponent()) == null) { PrintError($"{configData.removerModeS.meleeHitItemShortname} is not an item shortname for a melee tool"); removeMode = RemoveMode.None; } else { Subscribe(beseMelee is Hammer ? nameof(OnHammerHit) : nameof(OnPlayerAttack)); } } if (configData.raidS.enabled) { lastBlockedPlayers = new Hash(); lastAttackedBuildings = new Hash(); Subscribe(nameof(OnEntityDeath)); } if (configData.globalS.entityTimeLimit) { entitySpawnedTimes = new Hash(); Subscribe(nameof(OnEntitySpawned)); Subscribe(nameof(OnEntityKill)); } if (removeMode == RemoveMode.MeleeHit && configData.removerModeS.meleeHitEnableInHand || removeMode == RemoveMode.SpecificTool && configData.removerModeS.specificToolEnableInHand) { Subscribe(nameof(OnActiveItemChanged)); } if (!Enum.TryParse(configData.globalS.removeButton, true, out removeButton)) { PrintError($"{configData.globalS.removeButton} is an invalid button. The remove button has been changed to 'FIRE_PRIMARY'."); removeButton = BUTTON.FIRE_PRIMARY; configData.globalS.removeButton = removeButton.ToString(); SaveConfig(); } if (ImageLibrary != null) { foreach (var image in configData.imageUrls) { AddImageToLibrary(image.Value, image.Key); } if (configData.removerModeS.showCrosshair) { AddImageToLibrary(configData.removerModeS.crosshairImageUrl, UINAME_CROSSHAIR); } } } private void Unload() { if (removeAllCoroutine != null) ServerMgr.Instance.StopCoroutine(removeAllCoroutine); if (removeStructureCoroutine != null) ServerMgr.Instance.StopCoroutine(removeStructureCoroutine); if (removeExternalCoroutine != null) ServerMgr.Instance.StopCoroutine(removeExternalCoroutine); if (removePlayerEntityCoroutine != null) ServerMgr.Instance.StopCoroutine(removePlayerEntityCoroutine); foreach (var player in BasePlayer.activePlayerList) { DestroyAllUI(player); player.GetComponent()?.DisableTool(); } rt = null; configData = null; } private void OnEntityDeath(BuildingBlock buildingBlock, HitInfo info) { if (buildingBlock == null || info == null) return; var attacker = info.InitiatorPlayer; if (attacker != null && attacker.userID.IsSteamId() && HasAccess(attacker, buildingBlock)) return; BlockRemove(buildingBlock); } private void OnEntitySpawned(BaseEntity entity) { if (entity == null || entity.net == null) return; if (!CanEntityBeSaved(entity)) return; entitySpawnedTimes[entity.net.ID] = Time.realtimeSinceStartup; } private void OnEntityKill(BaseEntity entity) { if (entity == null || entity.net == null) return; entitySpawnedTimes.Remove(entity.net.ID); } private object OnPlayerAttack(BasePlayer player, HitInfo info) => OnHammerHit(player, info); private object OnHammerHit(BasePlayer player, HitInfo info) { if (player == null || info.HitEntity == null) return Null; var toolRemover = player.GetComponent(); if (toolRemover == null) return Null; if (!IsMeleeTool(player)) return Null; toolRemover.hitEntity = info.HitEntity; return False; } private void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem) { if (newItem == null) return; if (player == null || !player.userID.IsSteamId()) return; if (IsToolRemover(player)) return; if (removeMode == RemoveMode.MeleeHit && IsMeleeTool(newItem)) { ToggleRemove(player, RemoveType.Normal); return; } if (removeMode == RemoveMode.SpecificTool && IsSpecificTool(newItem)) { ToggleRemove(player, RemoveType.Normal); return; } } #endregion Oxide Hooks #region Initializing private readonly Dictionary shorPrefabNameToDeployable = new Dictionary(); private readonly Dictionary prefabNameToStructure = new Dictionary(); private readonly Dictionary itemShortNameToItemID = new Dictionary(); private readonly HashSet constructions = new HashSet(); private void Initialize() { foreach (var itemDefinition in ItemManager.GetItemDefinitions()) { if (!itemShortNameToItemID.ContainsKey(itemDefinition.shortname)) itemShortNameToItemID.Add(itemDefinition.shortname, itemDefinition.itemid); var deployablePrefab = itemDefinition.GetComponent()?.entityPrefab?.resourcePath; if (string.IsNullOrEmpty(deployablePrefab)) continue; var shortPrefabName = GameManager.server.FindPrefab(deployablePrefab)?.GetComponent()?.ShortPrefabName; if (!string.IsNullOrEmpty(shortPrefabName) && !shorPrefabNameToDeployable.ContainsKey(shortPrefabName)) shorPrefabNameToDeployable.Add(shortPrefabName, itemDefinition.shortname); } foreach (var entry in PrefabAttribute.server.prefabs) { var construction = entry.Value.Find().FirstOrDefault(); if (construction != null && construction.deployable == null && !string.IsNullOrEmpty(construction.info.name.english)) { constructions.Add(construction); if (!prefabNameToStructure.ContainsKey(construction.fullName)) prefabNameToStructure.Add(construction.fullName, construction.info.name.english); } } } #endregion Initializing #region Methods private static string GetRemoveTypeName(RemoveType removeType) => configData.removeTypeS[removeType].displayName; private static void DropItemContainer(ItemContainer itemContainer, Vector3 position, Quaternion rotation) => itemContainer?.Drop(PREFAB_ITEM_DROP, position, rotation); private static bool IsExternalWall(StabilityEntity stabilityEntity) => stabilityEntity.ShortPrefabName.Contains("external"); private static bool IsRemovableEntity(BaseEntity entity) => rt.shorPrefabNameToDeployable.ContainsKey(entity.ShortPrefabName) || rt.prefabNameToStructure.ContainsKey(entity.PrefabName) || configData.removeS.entityS.ContainsKey(entity.ShortPrefabName); private static bool CanEntityBeDisplayed(BaseEntity entity, BasePlayer player) { if (entity == null) return false; var stash = entity as StashContainer; return stash == null || !stash.IsHidden() || stash.PlayerInRange(player); } private static bool CanEntityBeSaved(BaseEntity entity) { if (entity is BuildingBlock) return true; ConfigData.RemoveSettings.EntityS entityS; return configData.removeS.entityS.TryGetValue(entity.ShortPrefabName, out entityS) && entityS.enabled; } private static bool HasEntityEnabled(BaseEntity entity) { bool valid; var buildingBlock = entity as BuildingBlock; if (buildingBlock != null && configData.removeS.validConstruction.TryGetValue(buildingBlock.grade, out valid) && valid) return true; ConfigData.RemoveSettings.EntityS entityS; return configData.removeS.entityS.TryGetValue(entity.ShortPrefabName, out entityS) && entityS.enabled; } private static string GetEntityName(BaseEntity entity) { string entityName; if (rt.shorPrefabNameToDeployable.TryGetValue(entity.ShortPrefabName, out entityName)) return entityName; if (rt.prefabNameToStructure.TryGetValue(entity.PrefabName, out entityName)) return entityName; if (configData.removeS.entityS.ContainsKey(entity.ShortPrefabName)) return entity.ShortPrefabName; return string.Empty; } private static string GetEntityImage(string name) { if (configData.imageUrls.ContainsKey(name)) return GetImageFromLibrary(name); if (rt.itemShortNameToItemID.ContainsKey(name)) return GetImageFromLibrary(name); return string.Empty; } private static string GetItemImage(string shortname) { switch (shortname.ToLower()) { case "economics": return GetImageFromLibrary("Economics"); case "serverrewards": return GetImageFromLibrary("ServerRewards"); } return GetEntityImage(shortname); } private static string GetDisplayName(string name) { var shortPrefabName = rt.shorPrefabNameToDeployable.FirstOrDefault(x => x.Value == name).Key; if (string.IsNullOrEmpty(shortPrefabName)) shortPrefabName = name; ConfigData.RemoveSettings.EntityS entityS; if (configData.removeS.entityS.TryGetValue(shortPrefabName, out entityS)) return entityS.displayName; ConfigData.RemoveSettings.BuildingBlocksS buildingBlockS; if (configData.removeS.buildingBlockS.TryGetValue(name, out buildingBlockS)) return buildingBlockS.displayName; if (configData.displayNames.TryGetValue(name, out shortPrefabName)) return shortPrefabName; var itemDefinition = ItemManager.FindItemDefinition(name); if (itemDefinition != null) { configData.displayNames.Add(name, itemDefinition.displayName.english); name = itemDefinition.displayName.english; } else configData.displayNames.Add(name, name); rt.SaveConfig(); return name; } private static ConfigData.PermSettings GetPermissionS(BasePlayer player) { int priority = 0; ConfigData.PermSettings permissionS = null; foreach (var entry in configData.permS) { if (entry.Value.priority >= priority && rt.permission.UserHasPermission(player.UserIDString, entry.Key)) { priority = entry.Value.priority; permissionS = entry.Value; } } return permissionS ?? new ConfigData.PermSettings(); } private static Vector2 GetAnchor(string anchor) { var array = anchor.Split(' '); return new Vector2(float.Parse(array[0]), float.Parse(array[1])); } private static bool AddImageToLibrary(string url, string shortname, ulong skin = 0) => (bool)rt.ImageLibrary.Call("AddImage", url, shortname.ToLower(), skin); private static string GetImageFromLibrary(string shortname, ulong skin = 0, bool returnUrl = false) => string.IsNullOrEmpty(shortname) ? string.Empty : (string)rt.ImageLibrary.Call("GetImage", shortname.ToLower(), skin, returnUrl); #endregion Methods #region UI private class UI { public static CuiElementContainer CreateElementContainer(string parent, string panelName, string backgroundColor, string anchorMin, string anchorMax, string offsetMin = "", string offsetMax = "", bool cursor = false) { return new CuiElementContainer() { { new CuiPanel { Image = { Color = backgroundColor }, RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax ,OffsetMin = offsetMin,OffsetMax = offsetMax}, CursorEnabled = cursor }, parent, panelName } }; } public static void CreatePanel(ref CuiElementContainer container, string panelName, string backgroundColor, string anchorMin, string anchorMax, bool cursor = false) { container.Add(new CuiPanel { Image = { Color = backgroundColor }, RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax }, CursorEnabled = cursor }, panelName, CuiHelper.GetGuid()); } 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) { container.Add(new CuiLabel { Text = { Color = textColor, FontSize = fontSize, Align = align, Text = text, FadeIn = fadeIn }, RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax } }, panelName, CuiHelper.GetGuid()); } public static void CreateImage(ref CuiElementContainer container, string panelName, string image, string anchorMin, string anchorMax, string color = "1 1 1 1") { container.Add(new CuiElement { Name = CuiHelper.GetGuid(), Parent = panelName, Components = { new CuiRawImageComponent { Sprite = "assets/content/textures/generic/fulltransparent.tga", Color = color, Png = image}, new CuiRectTransformComponent { AnchorMin = anchorMin, AnchorMax = anchorMax } } }); } } private const string UINAME_MAIN = "RemoverToolUI_Main"; private const string UINAME_TIMELEFT = "RemoverToolUI_TimeLeft"; private const string UINAME_ENTITY = "RemoverToolUI_Entity"; private const string UINAME_PRICE = "RemoverToolUI_Price"; private const string UINAME_REFUND = "RemoverToolUI_Refund"; private const string UINAME_AUTH = "RemoverToolUI_Auth"; private const string UINAME_CROSSHAIR = "RemoverToolUI_Crosshair"; private static void CreateCrosshairUI(BasePlayer player) { if (rt.ImageLibrary == null) return; var image = GetImageFromLibrary(UINAME_CROSSHAIR); if (string.IsNullOrEmpty(image)) return; var container = UI.CreateElementContainer("Hud", UINAME_CROSSHAIR, "0 0 0 0", configData.removerModeS.crosshairAnchorMin, configData.removerModeS.crosshairAnchorMax, configData.removerModeS.crosshairOffsetMin, configData.removerModeS.crosshairOffsetMax); UI.CreateImage(ref container, UINAME_CROSSHAIR, image, "0 0", "1 1", configData.removerModeS.crosshairColor); CuiHelper.DestroyUi(player, UINAME_CROSSHAIR); CuiHelper.AddUi(player, container); } private static void CreateToolUI(BasePlayer player, RemoveType removeType) { var container = UI.CreateElementContainer("Hud", UINAME_MAIN, configData.uiS.removerToolBackgroundColor, configData.uiS.removerToolAnchorMin, configData.uiS.removerToolAnchorMax, configData.uiS.removerToolOffsetMin, configData.uiS.removerToolOffsetMax); UI.CreatePanel(ref container, UINAME_MAIN, configData.uiS.removeBackgroundColor, configData.uiS.removeAnchorMin, configData.uiS.removeAnchorMax); UI.CreateLabel(ref container, UINAME_MAIN, configData.uiS.removeTextColor, rt.Lang("RemoverToolType", player.UserIDString, GetRemoveTypeName(removeType)), configData.uiS.removeTextSize, configData.uiS.removeTextAnchorMin, configData.uiS.removeTextAnchorMax, TextAnchor.MiddleLeft); CuiHelper.DestroyUi(player, UINAME_MAIN); CuiHelper.AddUi(player, container); } private static void UpdateTimeLeftUI(BasePlayer player, RemoveType removeType, int timeLeft, int currentRemoved, int maxRemovable) { var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_TIMELEFT, configData.uiS.timeLeftBackgroundColor, configData.uiS.timeLeftAnchorMin, configData.uiS.timeLeftAnchorMax); UI.CreateLabel(ref container, UINAME_TIMELEFT, configData.uiS.timeLeftTextColor, rt.Lang("TimeLeft", player.UserIDString, timeLeft, removeType == RemoveType.Normal || removeType == RemoveType.Admin ? maxRemovable == 0 ? $"{currentRemoved} / {rt.Lang("Unlimit", player.UserIDString)}" : $"{currentRemoved} / {maxRemovable}" : currentRemoved.ToString()), configData.uiS.timeLeftTextSize, configData.uiS.timeLeftTextAnchorMin, configData.uiS.timeLeftTextAnchorMax, TextAnchor.MiddleLeft); CuiHelper.DestroyUi(player, UINAME_TIMELEFT); CuiHelper.AddUi(player, container); } private static void UpdateEntityUI(BasePlayer player, BaseEntity targetEntity) { CuiHelper.DestroyUi(player, UINAME_ENTITY); if (!CanEntityBeDisplayed(targetEntity, player)) return; var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_ENTITY, configData.uiS.entityBackgroundColor, configData.uiS.entityAnchorMin, configData.uiS.entityAnchorMax); string name; var entityName = GetEntityName(targetEntity); if (string.IsNullOrEmpty(entityName)) { var target = targetEntity as BasePlayer; name = target != null ? $"{target.displayName} ({GetDisplayName(target.ShortPrefabName)})" : targetEntity.ShortPrefabName; } else name = GetDisplayName(entityName); UI.CreateLabel(ref container, UINAME_ENTITY, configData.uiS.entityTextColor, name, configData.uiS.entityTextSize, configData.uiS.entityTextAnchorMin, configData.uiS.entityTextAnchorMax, TextAnchor.MiddleLeft); if (configData.uiS.entityImageEnabled && !string.IsNullOrEmpty(entityName) && rt.ImageLibrary != null) { var image = GetEntityImage(entityName); if (!string.IsNullOrEmpty(image)) UI.CreateImage(ref container, UINAME_ENTITY, image, configData.uiS.entityImageAnchorMin, configData.uiS.entityImageAnchorMax); } CuiHelper.AddUi(player, container); } private static void UpdatePricesUI(BasePlayer player, bool usePrice, BaseEntity targetEntity) { CuiHelper.DestroyUi(player, UINAME_PRICE); if (!CanEntityBeDisplayed(targetEntity, player) || !HasEntityEnabled(targetEntity)) return; Dictionary price = null; if (usePrice) price = rt.GetPrice(targetEntity); var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_PRICE, configData.uiS.priceBackgroundColor, configData.uiS.priceAnchorMin, configData.uiS.priceAnchorMax); UI.CreateLabel(ref container, UINAME_PRICE, configData.uiS.priceTextColor, rt.Lang("Price", player.UserIDString), configData.uiS.priceTextSize, configData.uiS.priceTextAnchorMin, configData.uiS.priceTextAnchorMax, TextAnchor.MiddleLeft); if (price == null || price.Count == 0) UI.CreateLabel(ref container, UINAME_PRICE, configData.uiS.price2TextColor, rt.Lang("Free", player.UserIDString), configData.uiS.price2TextSize, configData.uiS.price2TextAnchorMin, configData.uiS.price2TextAnchorMax, TextAnchor.MiddleLeft); else { var anchorMin = configData.uiS.Price2TextAnchorMin; var anchorMax = configData.uiS.Price2TextAnchorMax; float x = (anchorMax.y - anchorMin.y) / price.Count; int textSize = configData.uiS.price2TextSize - price.Count; int i = 0; foreach (var p in price) { UI.CreateLabel(ref container, UINAME_PRICE, configData.uiS.price2TextColor, $"{GetDisplayName(p.Key)} x{p.Value}", textSize, $"{anchorMin.x} {anchorMin.y + i * x}", $"{anchorMax.x} {anchorMin.y + (i + 1) * x}", TextAnchor.MiddleLeft); if (configData.uiS.imageEnabled && rt.ImageLibrary != null) { var image = GetItemImage(p.Key); if (!string.IsNullOrEmpty(image)) UI.CreateImage(ref container, UINAME_PRICE, image, $"{anchorMax.x - configData.uiS.rightDistance - x * configData.uiS.imageScale} {anchorMin.y + i * x}", $"{anchorMax.x - configData.uiS.rightDistance} {anchorMin.y + (i + 1) * x}"); } i++; } } CuiHelper.AddUi(player, container); } private static void UpdateRefundUI(BasePlayer player, bool useRefund, BaseEntity targetEntity) { CuiHelper.DestroyUi(player, UINAME_REFUND); if (!CanEntityBeDisplayed(targetEntity, player) || !HasEntityEnabled(targetEntity)) return; Dictionary refund = null; if (useRefund) refund = rt.GetRefund(targetEntity); var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_REFUND, configData.uiS.refundBackgroundColor, configData.uiS.refundAnchorMin, configData.uiS.refundAnchorMax); UI.CreateLabel(ref container, UINAME_REFUND, configData.uiS.refundTextColor, rt.Lang("Refund", player.UserIDString), configData.uiS.refundTextSize, configData.uiS.refundTextAnchorMin, configData.uiS.refundTextAnchorMax, TextAnchor.MiddleLeft); if (refund == null || refund.Count == 0) UI.CreateLabel(ref container, UINAME_REFUND, configData.uiS.refund2TextColor, rt.Lang("Nothing", player.UserIDString), configData.uiS.refund2TextSize, configData.uiS.refund2TextAnchorMin, configData.uiS.refund2TextAnchorMax, TextAnchor.MiddleLeft); else { var anchorMin = configData.uiS.Refund2TextAnchorMin; var anchorMax = configData.uiS.Refund2TextAnchorMax; float x = (anchorMax.y - anchorMin.y) / refund.Count; int textSize = configData.uiS.refund2TextSize - refund.Count; int i = 0; foreach (var p in refund) { UI.CreateLabel(ref container, UINAME_REFUND, configData.uiS.refund2TextColor, $"{GetDisplayName(p.Key)} x{p.Value}", textSize, $"{anchorMin.x} {anchorMin.y + i * x}", $"{anchorMax.x} {anchorMin.y + (i + 1) * x}", TextAnchor.MiddleLeft); if (configData.uiS.imageEnabled && rt.ImageLibrary != null) { var image = GetItemImage(p.Key); if (!string.IsNullOrEmpty(image)) UI.CreateImage(ref container, UINAME_REFUND, image, $"{anchorMax.x - configData.uiS.rightDistance - x * configData.uiS.imageScale} {anchorMin.y + i * x}", $"{anchorMax.x - configData.uiS.rightDistance} {anchorMin.y + (i + 1) * x}"); } i++; } } CuiHelper.AddUi(player, container); } private static void UpdateAuthorizationUI(BasePlayer player, RemoveType removeType, BaseEntity targetEntity, bool shouldPay) { CuiHelper.DestroyUi(player, UINAME_AUTH); if (!CanEntityBeDisplayed(targetEntity, player)) return; string reason; string color = rt.CanRemoveEntity(player, removeType, targetEntity, shouldPay, out reason) ? configData.uiS.allowedBackgroundColor : configData.uiS.refusedBackgroundColor; var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_AUTH, color, configData.uiS.authorizationsAnchorMin, configData.uiS.authorizationsAnchorMax); UI.CreateLabel(ref container, UINAME_AUTH, configData.uiS.authorizationsTextColor, reason, configData.uiS.authorizationsTextSize, configData.uiS.authorizationsTextAnchorMin, configData.uiS.authorizationsTextAnchorMax, TextAnchor.MiddleLeft); CuiHelper.AddUi(player, container); } private static void DestroyAllUI(BasePlayer player) { CuiHelper.DestroyUi(player, UINAME_CROSSHAIR); CuiHelper.DestroyUi(player, UINAME_MAIN); } #endregion UI #region ToolRemover Component #region Tool Helpers private static bool IsSpecificTool(BasePlayer player) { var heldItem = player.GetActiveItem(); return IsSpecificTool(heldItem); } private static bool IsSpecificTool(Item heldItem) { if (heldItem != null && heldItem.info.shortname == configData.removerModeS.specificToolShortname) { if (configData.removerModeS.specificToolSkin < 0) return true; return heldItem.skin == (ulong)configData.removerModeS.specificToolSkin; } return false; } private static bool IsMeleeTool(BasePlayer player) { var heldItem = player.GetActiveItem(); return IsMeleeTool(heldItem); } private static bool IsMeleeTool(Item heldItem) { if (heldItem != null && heldItem.info.shortname == configData.removerModeS.meleeHitItemShortname) { if (configData.removerModeS.meleeHitModeSkin < 0) return true; return heldItem.skin == (ulong)configData.removerModeS.meleeHitModeSkin; } return false; } #endregion Tool Helpers private class ToolRemover : FacepunchBehaviour { public BasePlayer player; public RemoveType removeType; public bool canOverride; public BaseEntity hitEntity; public int currentRemoved; private int timeLeft; private float distance; private float lastRemove; private float removeInterval; private bool pay; private bool refund; private int maxRemovable; private RaycastHit raycastHit; private BaseEntity targetEntity; private uint currentItemID; private int removeTime; private bool resetTime; private Item lastHeldItem; private bool checkHeldItem; private void Awake() { player = GetComponent(); currentItemID = player.svActiveItemID; checkHeldItem = removeMode == RemoveMode.MeleeHit && configData.removerModeS.meleeHitDisableInHand || removeMode == RemoveMode.SpecificTool && configData.removerModeS.specificToolDisableInHand; if (checkHeldItem) { lastHeldItem = player.GetActiveItem(); } if (removeMode == RemoveMode.NoHeld) { UnEquip(); } } public void Init(RemoveType type, int time, int max, float dis, float interval, bool p, bool r, bool reset, bool c) { canOverride = c; removeTime = timeLeft = time; removeType = type; distance = dis; resetTime = reset; removeInterval = Mathf.Max(0.2f, interval); if (removeType == RemoveType.Normal) { maxRemovable = max; pay = p && configData.removeS.priceEnabled; refund = r && configData.removeS.refundEnabled; } else { maxRemovable = currentRemoved = 0; pay = refund = false; } DestroyAllUI(player); if (removeMode == RemoveMode.NoHeld && configData.removerModeS.showCrosshair) { CreateCrosshairUI(player); } if (configData.uiS.enabled) { CreateToolUI(player, removeType); } CancelInvoke(RemoveUpdate); InvokeRepeating(RemoveUpdate, 0f, 1f); } private void RemoveUpdate() { if (configData.uiS.enabled) { GetTargetEntity(); UpdateTimeLeftUI(player, removeType, timeLeft, currentRemoved, maxRemovable); UpdateEntityUI(player, targetEntity); if (removeType == RemoveType.Normal) { if (configData.uiS.authorizationEnabled) UpdateAuthorizationUI(player, removeType, targetEntity, pay); if (configData.uiS.priceEnabled) UpdatePricesUI(player, pay, targetEntity); if (configData.uiS.refundEnabled) UpdateRefundUI(player, refund, targetEntity); } } if (timeLeft-- <= 0) { DisableTool(); } } private void GetTargetEntity() { bool flag = Physics.Raycast(player.eyes.HeadRay(), out raycastHit, distance, LAYER_TARGET); targetEntity = flag ? raycastHit.GetEntity() : null; } private void FixedUpdate() { if (player == null || !player.IsConnected || !player.CanInteract()) { DisableTool(); return; } if (player.svActiveItemID != currentItemID) { if (checkHeldItem) { var heldItem = player.GetActiveItem(); if (removeMode == RemoveMode.MeleeHit && IsMeleeTool(lastHeldItem) && !IsMeleeTool(heldItem) || removeMode == RemoveMode.SpecificTool && IsSpecificTool(lastHeldItem) && !IsSpecificTool(heldItem)) { DisableTool(); return; } lastHeldItem = heldItem; } if (removeMode == RemoveMode.NoHeld) { if (player.svActiveItemID != 0) { if (configData.removerModeS.noHeldDisableInHand) { DisableTool(); return; } UnEquip(); } } currentItemID = player.svActiveItemID; } if (Time.realtimeSinceStartup - lastRemove >= removeInterval) { if (removeMode == RemoveMode.MeleeHit) { if (hitEntity == null) return; targetEntity = hitEntity; hitEntity = null; } else { if (!player.serverInput.IsDown(removeButton)) return; if (removeMode == RemoveMode.SpecificTool && !IsSpecificTool(player)) return; GetTargetEntity(); } if (rt.TryRemove(player, targetEntity, removeType, pay, refund)) { if (resetTime) timeLeft = removeTime; if (removeType == RemoveType.Normal || removeType == RemoveType.Admin) currentRemoved++; if (configData.globalS.startCooldownOnRemoved && removeType == RemoveType.Normal) { rt.cooldownTimes[player.userID] = Time.realtimeSinceStartup; } } lastRemove = Time.realtimeSinceStartup; } if (removeType == RemoveType.Normal && maxRemovable > 0 && currentRemoved >= maxRemovable) { rt.Print(player, rt.Lang("EntityLimit", player.UserIDString, maxRemovable)); DisableTool(false); }; } private void UnEquip() { //player.lastReceivedTick.activeItem = 0; var activeItem = player.GetActiveItem(); if (activeItem?.GetHeldEntity() is HeldEntity) { var slot = activeItem.position; activeItem.SetParent(null); player.Invoke(() => { if (activeItem == null || !activeItem.IsValid()) return; if (player.inventory.containerBelt.GetSlot(slot) == null) { activeItem.position = slot; activeItem.SetParent(player.inventory.containerBelt); } else player.GiveItem(activeItem); }, 0.2f); } } public void DisableTool(bool showMessage = true) { if (showMessage) { if (rt != null && player != null && player.IsConnected) { rt.Print(player, rt.Lang("ToolDisabled", player.UserIDString)); } } DestroyImmediate(this); } private void OnDestroy() { DestroyAllUI(player); if (rt != null && removeType == RemoveType.Normal) { if (configData != null && !configData.globalS.startCooldownOnRemoved) { rt.cooldownTimes[player.userID] = Time.realtimeSinceStartup; } } } } #endregion ToolRemover Component #region Pay private readonly List collect = new List(); private bool Pay(BasePlayer player, BaseEntity targetEntity) { var price = GetPrice(targetEntity); if (price == null) return true; try { foreach (var entry in price) { if (entry.Value <= 0) continue; int itemID; if (itemShortNameToItemID.TryGetValue(entry.Key, out itemID)) { collect.Clear(); player.inventory.Take(collect, itemID, entry.Value); player.Command("note.inv", itemID, -entry.Value); } else if (!CheckOrPay(entry.Key, entry.Value, player.userID)) { return false; } } } catch (Exception e) { PrintError($"{player} couldn't pay to remove entity. Error Message: {e.Message}"); return false; } finally { foreach (Item item in collect) item.Remove(); } return true; } private Dictionary GetPrice(BaseEntity targetEntity) { var buildingBlock = targetEntity as BuildingBlock; if (buildingBlock != null) { var entityName = prefabNameToStructure[buildingBlock.PrefabName]; ConfigData.RemoveSettings.BuildingBlocksS buildingBlockS; if (configData.removeS.buildingBlockS.TryGetValue(entityName, out buildingBlockS)) { ConfigData.RemoveSettings.BuildingBlocksS.BuildingGradeS buildingGradeS; if (buildingBlockS.buildingGradeS.TryGetValue(buildingBlock.grade, out buildingGradeS)) { if (buildingGradeS.priceDic != null) { return buildingGradeS.priceDic; } if (buildingGradeS.pricePercentage > 0f) { var currentGrade = buildingBlock.currentGrade; if (currentGrade != null) { var price = new Dictionary(); foreach (var itemAmount in currentGrade.costToBuild) { var amount = Mathf.RoundToInt(itemAmount.amount * buildingGradeS.pricePercentage / 100); if (amount <= 0) continue; price.Add(itemAmount.itemDef.shortname, amount); } return price; } } else if (buildingGradeS.pricePercentage < 0f) { var currentGrade = buildingBlock.currentGrade; if (currentGrade != null) { return currentGrade.costToBuild.ToDictionary(x => x.itemDef.shortname, y => Mathf.RoundToInt(y.amount)); } } } } } else { ConfigData.RemoveSettings.EntityS entityS; if (configData.removeS.entityS.TryGetValue(targetEntity.ShortPrefabName, out entityS)) { return entityS.price; } } return null; } private bool CanPay(BasePlayer player, BaseEntity targetEntity) { var price = GetPrice(targetEntity); if (price.Count <= 0) return true; foreach (var p in price) { if (p.Value <= 0) continue; int itemID; if (itemShortNameToItemID.TryGetValue(p.Key, out itemID)) { int c = player.inventory.GetAmount(itemID); if (c < p.Value) return false; } else if (!CheckOrPay(p.Key, p.Value, player.userID, true)) return false; } return true; } private bool CheckOrPay(string key, int price, ulong playerID, bool check = false) { if (price <= 0) return true; switch (key.ToLower()) { case "economics": if (Economics == null) return false; if (check) { var b = Economics.Call("Balance", playerID); if (b == null) return false; if ((double)b < price) return false; } else { var w = Economics.Call("Withdraw", playerID, (double)price); if (w == null || !(bool)w) return false; } return true; case "serverrewards": if (ServerRewards == null) return false; if (check) { var c = ServerRewards.Call("CheckPoints", playerID); if (c == null) return false; if ((int)c < price) return false; } else { var t = ServerRewards.Call("TakePoints", playerID, price); if (t == null || !(bool)t) return false; } return true; } return true; } #endregion Pay #region Refund private void GiveRefund(BasePlayer player, BaseEntity targetEntity) { var refund = GetRefund(targetEntity); if (refund == null) return; foreach (var entry in refund) { if (entry.Value <= 0) continue; int itemID; string shortname; shorPrefabNameToDeployable.TryGetValue(targetEntity.ShortPrefabName, out shortname); if (itemShortNameToItemID.TryGetValue(entry.Key, out itemID)) { var item = ItemManager.CreateByItemID(itemID, entry.Value, entry.Key == shortname ? targetEntity.skinID : 0); player.GiveItem(item); } else { switch (entry.Key.ToLower()) { case "economics": if (Economics == null) continue; Economics.Call("Deposit", player.userID, (double)entry.Value); continue; case "serverrewards": if (ServerRewards == null) continue; ServerRewards.Call("AddPoints", player.userID, entry.Value); continue; default: PrintError($"{player} didn't receive refund because {entry.Key} doesn't seem to be a valid item name"); continue; } } } } private Dictionary GetRefund(BaseEntity targetEntity) { var buildingBlock = targetEntity.GetComponent(); if (buildingBlock != null) { var entityName = prefabNameToStructure[buildingBlock.PrefabName]; ConfigData.RemoveSettings.BuildingBlocksS buildingBlockS; if (configData.removeS.buildingBlockS.TryGetValue(entityName, out buildingBlockS)) { ConfigData.RemoveSettings.BuildingBlocksS.BuildingGradeS buildingGradeS; if (buildingBlockS.buildingGradeS.TryGetValue(buildingBlock.grade, out buildingGradeS)) { if (buildingGradeS.refundDic != null) { return buildingGradeS.refundDic; } if (buildingGradeS.refundPercentage > 0f) { var currentGrade = buildingBlock.currentGrade; if (currentGrade != null) { var refund = new Dictionary(); foreach (var itemAmount in currentGrade.costToBuild) { var amount = Mathf.RoundToInt(itemAmount.amount * buildingGradeS.refundPercentage / 100); if (amount <= 0) continue; refund.Add(itemAmount.itemDef.shortname, amount); } return refund; } } else if (buildingGradeS.refundPercentage < 0f) { var currentGrade = buildingBlock.currentGrade; if (currentGrade != null) { return currentGrade.costToBuild.ToDictionary(x => x.itemDef.shortname, y => Mathf.RoundToInt(y.amount)); } } } } } else { ConfigData.RemoveSettings.EntityS entityS; if (configData.removeS.entityS.TryGetValue(targetEntity.ShortPrefabName, out entityS)) { if (configData.removeS.refundSlot) { var slots = GetSlots(targetEntity); if (slots.Any()) { var refund = new Dictionary(entityS.refund); foreach (var slotName in slots) { if (!refund.ContainsKey(slotName)) refund.Add(slotName, 0); refund[slotName]++; } return refund; } } return entityS.refund; } } return null; } private IEnumerable GetSlots(BaseEntity targetEntity) { foreach (BaseEntity.Slot slot in Enum.GetValues(typeof(BaseEntity.Slot))) { if (targetEntity.HasSlot(slot)) { var entity = targetEntity.GetSlot(slot); if (entity != null) { string slotName; if (shorPrefabNameToDeployable.TryGetValue(entity.ShortPrefabName, out slotName)) { yield return slotName; } } } } } #endregion Refund #region RaidBlocker private void BlockRemove(BuildingBlock buildingBlock) { if (configData.raidS.blockBuildingID) { var buildingID = buildingBlock.buildingID; lastAttackedBuildings[buildingID] = Time.realtimeSinceStartup; } if (configData.raidS.blockPlayers) { var players = Pool.GetList(); Vis.Entities(buildingBlock.transform.position, configData.raidS.blockRadius, players, Rust.Layers.Mask.Player_Server); foreach (var player in players) { if (player.userID.IsSteamId()) lastBlockedPlayers[player.userID] = Time.realtimeSinceStartup; } Pool.FreeList(ref players); } } private bool IsRaidBlocked(BasePlayer player, BaseEntity targetEntity, out float timeLeft) { if (configData.raidS.blockBuildingID) { var buildingBlock = targetEntity as BuildingBlock; if (buildingBlock != null) { float blockTime; if (lastAttackedBuildings.TryGetValue(buildingBlock.buildingID, out blockTime)) { timeLeft = configData.raidS.blockTime - (Time.realtimeSinceStartup - blockTime); if (timeLeft > 0) return true; } } } if (configData.raidS.blockPlayers) { float blockTime; if (lastBlockedPlayers.TryGetValue(player.userID, out blockTime)) { timeLeft = configData.raidS.blockTime - (Time.realtimeSinceStartup - blockTime); if (timeLeft > 0) return true; } } timeLeft = 0; return false; } #endregion RaidBlocker #region TryRemove private bool TryRemove(BasePlayer player, BaseEntity targetEntity, RemoveType removeType, bool shouldPay, bool shouldRefund) { if (!CanEntityBeDisplayed(targetEntity, player)) { Print(player, Lang("NotFoundOrFar", player.UserIDString)); return false; } if (removeType == RemoveType.Admin) { var target = targetEntity as BasePlayer; if (target != null) { if (target.userID.IsSteamId() && target.IsConnected) { target.Kick("From RemoverTool Plugin"); return true; } } DoRemove(targetEntity, configData.removeTypeS[RemoveType.Admin].gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None); return true; } string reason; if (!CanRemoveEntity(player, removeType, targetEntity, shouldPay, out reason)) { Print(player, reason); return false; } switch (removeType) { case RemoveType.All: { if (removeAllCoroutine != null) { Print(player, Lang("AlreadyRemoveAll", player.UserIDString)); return false; } removeAllCoroutine = ServerMgr.Instance.StartCoroutine(RemoveAll(targetEntity, player)); Print(player, Lang("StartRemoveAll", player.UserIDString)); return true; } case RemoveType.External: { var stabilityEntity = targetEntity as StabilityEntity; if (stabilityEntity == null || !IsExternalWall(stabilityEntity)) { Print(player, Lang("NotExternalWall", player.UserIDString)); return false; } if (removeExternalCoroutine != null) { Print(player, Lang("AlreadyRemoveExternal", player.UserIDString)); return false; } removeExternalCoroutine = ServerMgr.Instance.StartCoroutine(RemoveExternal(stabilityEntity, player)); Print(player, Lang("StartRemoveExternal", player.UserIDString)); return true; } case RemoveType.Structure: { var decayEntity = targetEntity as DecayEntity; if (decayEntity == null) { Print(player, Lang("NotStructure", player.UserIDString)); return false; } if (removeStructureCoroutine != null) { Print(player, Lang("AlreadyRemoveStructure", player.UserIDString)); return false; } removeStructureCoroutine = ServerMgr.Instance.StartCoroutine(RemoveStructure(decayEntity, player)); Print(player, Lang("StartRemoveStructure", player.UserIDString)); return true; } } var storageContainer = targetEntity as StorageContainer; if (storageContainer != null && storageContainer.inventory?.itemList?.Count > 0) { if (configData.containerS.dropContainerStorage || configData.containerS.dropItemsStorage) { if (Interface.CallHook("OnDropContainerEntity", storageContainer) == null) { if (configData.containerS.dropContainerStorage) { DropItemContainer(storageContainer.inventory, storageContainer.GetDropPosition(), storageContainer.transform.rotation); } else if (configData.containerS.dropItemsStorage) { storageContainer.DropItems(); //DropUtil.DropItems(storageContainer.inventory, storageContainer.GetDropPosition()); } } } } else { var containerIoEntity = targetEntity as ContainerIOEntity; if (containerIoEntity != null && containerIoEntity.inventory?.itemList?.Count > 0) { if (configData.containerS.dropContainerIoEntity || configData.containerS.dropItemsIoEntity) { if (Interface.CallHook("OnDropContainerEntity", containerIoEntity) == null) { if (configData.containerS.dropContainerIoEntity) { DropItemContainer(containerIoEntity.inventory, containerIoEntity.GetDropPosition(), containerIoEntity.transform.rotation); } else if (configData.containerS.dropItemsIoEntity) { containerIoEntity.DropItems(); //DropUtil.DropItems(containerIoEntity.inventory, containerIoEntity.GetDropPosition()); } } } } } if (shouldPay) { bool flag = Pay(player, targetEntity); if (!flag) { Print(player, Lang("CantPay", player.UserIDString)); return false; } } if (shouldRefund) GiveRefund(player, targetEntity); DoNormalRemove(player, targetEntity, configData.removeTypeS[RemoveType.Normal].gibs); return true; } private bool CanRemoveEntity(BasePlayer player, RemoveType removeType, BaseEntity targetEntity, bool shouldPay, out string reason) { if (targetEntity.IsDestroyed || !IsRemovableEntity(targetEntity) || (removeType == RemoveType.Normal && ((targetEntity as BaseCombatEntity)?.IsDead() ?? false))) { reason = Lang("InvalidEntity", player.UserIDString); return false; } if (removeType != RemoveType.Normal) { reason = string.Empty; return true; } if (!HasEntityEnabled(targetEntity)) { reason = Lang("EntityDisabled", player.UserIDString); return false; } var result = Interface.CallHook("canRemove", player, targetEntity); if (result != null) { reason = result is string ? (string)result : Lang("BeBlocked", player.UserIDString); return false; } if (!configData.damagedEntityS.enabled && IsDamagedEntity(targetEntity)) { reason = Lang("DamagedEntity", player.UserIDString); return false; } float timeLeft; if (configData.raidS.enabled && IsRaidBlocked(player, targetEntity, out timeLeft)) { reason = Lang("RaidBlocked", player.UserIDString, Math.Ceiling(timeLeft)); return false; } if (configData.globalS.entityTimeLimit && IsEntityTimeLimit(targetEntity)) { reason = Lang("EntityTimeLimit", player.UserIDString, configData.globalS.limitTime); return false; } if (shouldPay && !CanPay(player, targetEntity)) { reason = Lang("NotEnoughCost", player.UserIDString); return false; } if (!configData.containerS.removeNotEmptyStorage && targetEntity is StorageContainer) { if (((StorageContainer)targetEntity).inventory?.itemList?.Count > 0) { reason = Lang("StorageNotEmpty", player.UserIDString); return false; } } if (!configData.containerS.removeNotEmptyIoEntity && targetEntity is ContainerIOEntity) { if (((ContainerIOEntity)targetEntity).inventory?.itemList?.Count > 0) { reason = Lang("StorageNotEmpty", player.UserIDString); return false; } } if (!HasAccess(player, targetEntity)) { reason = Lang("NotRemoveAccess", player.UserIDString); return false; } if (configData.globalS.checkStash && HasStashUnderFoundation(targetEntity as BuildingBlock))//Prevent not access players from knowing that there has a stash { reason = Lang("HasStash", player.UserIDString); return false; } reason = Lang("CanRemove", player.UserIDString); return true; } private bool HasAccess(BasePlayer player, BaseEntity targetEntity) { //var excludeTwigs = configData.globalS.excludeTwigs && (targetEntity as BuildingBlock)?.grade == BuildingGrade.Enum.Twigs; if (configData.globalS.useEntityOwners) { if (AreFriends(targetEntity.OwnerID, player.userID)) { if (!configData.globalS.useToolCupboards) { return true; } if (HasTotalAccess(player, targetEntity)) { return true; } } } if (configData.globalS.useToolCupboards) { if (HasTotalAccess(player, targetEntity)) { return true; } } if (configData.globalS.useBuildingOwners && BuildingOwners != null) { var buildingBlock = targetEntity as BuildingBlock; if (buildingBlock != null) { var result = BuildingOwners?.Call("FindBlockData", buildingBlock) as string; if (result != null) { ulong ownerID = ulong.Parse(result); if (AreFriends(ownerID, player.userID)) { return true; } } } } return false; } private static bool HasTotalAccess(BasePlayer player, BaseEntity targetEntity) { if (player.IsBuildingBlocked(targetEntity.WorldSpaceBounds())) { return false; } if (configData.globalS.useBuildingLocks && !CanOpenAllLocks(player, targetEntity)) { //reason = Lang("Can'tOpenAllLocks", player.UserIDString); return false; } return true; } private static bool CanOpenAllLocks(BasePlayer player, BaseEntity targetEntity) { var decayEntities = Pool.GetList(); var building = targetEntity.GetBuildingPrivilege()?.GetBuilding() ?? (targetEntity as DecayEntity)?.GetBuilding(); if (building != null) { decayEntities.AddRange(building.decayEntities); } /*else//An entity placed outside { Vis.Entities(targetEntity.transform.position, 9f, decayEntities, Layers.Mask.Construction | Layers.Mask.Deployed); }*/ foreach (var decayEntity in decayEntities) { if ((decayEntity is Door || decayEntity is BoxStorage) && decayEntity.OwnerID.IsSteamId()) { var lockEntity = decayEntity.GetSlot(BaseEntity.Slot.Lock) as BaseLock; if (lockEntity != null && !OnTryToOpen(player, lockEntity)) { Pool.FreeList(ref decayEntities); return false; } } } Pool.FreeList(ref decayEntities); return true; } private static bool OnTryToOpen(BasePlayer player, BaseLock baseLock) { var codeLock = baseLock as CodeLock; if (codeLock != null) { var obj = Interface.CallHook("CanUseLockedEntity", player, codeLock); if (obj is bool) { return (bool)obj; } if (!codeLock.IsLocked()) { return true; } if (codeLock.whitelistPlayers.Contains(player.userID) || codeLock.guestPlayers.Contains(player.userID)) { return true; } return false; } var keyLock = baseLock as KeyLock; if (keyLock != null) { return keyLock.OnTryToOpen(player); } return false; } private static bool HasStashUnderFoundation(BuildingBlock buildingBlock) { if (buildingBlock == null) return false; if (buildingBlock.ShortPrefabName.Contains("foundation")) { return GamePhysics.CheckOBB(buildingBlock.WorldSpaceBounds()); //var stashes = Pool.GetList(); //Vis.Entities(buildingBlock.CenterPoint(), 2.5f, stashes, Rust.Layers.Mask.Default); //bool flag = stashes.Count > 0; //Pool.FreeList(ref stashes); //return flag; } return false; } private static bool IsDamagedEntity(BaseEntity entity) { if (configData.damagedEntityS.excludeBuildingBlocks && (entity is BuildingBlock || entity is SimpleBuildingBlock)) return false; var baseCombatEntity = entity as BaseCombatEntity; if (baseCombatEntity == null || !baseCombatEntity.repair.enabled) return false; if (!(entity is BuildingBlock) && (baseCombatEntity.repair.itemTarget == null || baseCombatEntity.repair.itemTarget.Blueprint == null))//Quarry return false; if (baseCombatEntity.Health() / baseCombatEntity.MaxHealth() * 100f >= configData.damagedEntityS.percentage) return false; return true; } private static bool IsEntityTimeLimit(BaseEntity entity) { if (entity.net == null) return true; float spawnedTime; if (rt.entitySpawnedTimes.TryGetValue(entity.net.ID, out spawnedTime)) return Time.realtimeSinceStartup - spawnedTime > configData.globalS.limitTime; return true; } #region AreFriends private bool AreFriends(ulong playerID, ulong friendID) { if (!playerID.IsSteamId()) return false; if (playerID == friendID) return true; if (configData.globalS.useTeams && SameTeam(playerID, friendID)) return true; if (configData.globalS.useFriends && HasFriend(playerID, friendID)) return true; if (configData.globalS.useClans && SameClan(playerID, friendID)) return true; return false; } private static bool SameTeam(ulong playerID, ulong friendID) { if (!RelationshipManager.TeamsEnabled()) return false; var playerTeam = RelationshipManager.Instance.FindPlayersTeam(playerID); if (playerTeam == null) return false; var friendTeam = RelationshipManager.Instance.FindPlayersTeam(friendID); if (friendTeam == null) return false; return playerTeam == friendTeam; } private bool HasFriend(ulong playerID, ulong friendID) { if (Friends == null) return false; return (bool)Friends.Call("HasFriend", playerID, friendID); } private bool SameClan(ulong playerID, ulong friendID) { if (Clans == null) return false; //Clans var isMember = Clans.Call("IsClanMember", playerID.ToString(), friendID.ToString()); if (isMember != null) return (bool)isMember; //Rust:IO Clans var playerClan = Clans.Call("GetClanOf", playerID); if (playerClan == null) return false; var friendClan = Clans.Call("GetClanOf", friendID); if (friendClan == null) return false; return (string)playerClan == (string)friendClan; } #endregion AreFriends #endregion TryRemove #region RemoveEntity private IEnumerator RemoveAll(BaseEntity sourceEntity, BasePlayer player) { var removeList = Pool.Get>(); yield return GetNearbyEntities(sourceEntity, removeList, LAYER_ALL); yield return ProcessContainer(removeList); yield return DelayRemove(removeList, player, RemoveType.All); Pool.Free(ref removeList); removeAllCoroutine = null; } private IEnumerator RemoveExternal(StabilityEntity sourceEntity, BasePlayer player) { var removeList = Pool.Get>(); yield return GetNearbyEntities(sourceEntity, removeList, Rust.Layers.Mask.Construction, IsExternalWall); yield return DelayRemove(removeList, player, RemoveType.External); Pool.Free(ref removeList); removeExternalCoroutine = null; } private IEnumerator RemoveStructure(DecayEntity sourceEntity, BasePlayer player) { var removeList = Pool.Get>(); yield return ProcessBuilding(sourceEntity, removeList); yield return DelayRemove(removeList, player, RemoveType.Structure); Pool.Free(ref removeList); removeStructureCoroutine = null; } private IEnumerator RemovePlayerEntity(ConsoleSystem.Arg arg, ulong targetID, RemovePlayerEntityType removePlayerEntityType) { int current = 0; var removeList = Pool.Get>(); switch (removePlayerEntityType) { case RemovePlayerEntityType.All: case RemovePlayerEntityType.Building: bool onlyBuilding = removePlayerEntityType == RemovePlayerEntityType.Building; foreach (var serverEntity in BaseNetworkable.serverEntities) { if (++current % 500 == 0) yield return CoroutineEx.waitForEndOfFrame; var entity = serverEntity as BaseEntity; if (entity == null || entity.OwnerID != targetID) continue; if (!onlyBuilding || entity is BuildingBlock) { removeList.Add(entity); } } foreach (var player in BasePlayer.allPlayerList) { if (player.userID == targetID) { if (player.IsConnected) player.Kick("From RemoverTool Plugin"); removeList.Add(player); break; } } break; case RemovePlayerEntityType.Cupboard: foreach (var serverEntity in BaseNetworkable.serverEntities) { if (++current % 500 == 0) yield return CoroutineEx.waitForEndOfFrame; var entity = serverEntity as BuildingPrivlidge; if (entity == null || entity.OwnerID != targetID) continue; yield return ProcessBuilding(entity, removeList); } break; } int removed = removeList.Count(x => x != null && !x.IsDestroyed); yield return DelayRemove(removeList); Pool.Free(ref removeList); Print(arg, $"You have successfully removed {removed} entities of player {targetID}."); removePlayerEntityCoroutine = null; } private IEnumerator DelayRemove(IEnumerable entities, BasePlayer player = null, RemoveType removeType = RemoveType.None) { int removed = 0; var destroyMode = removeType == RemoveType.None ? BaseNetworkable.DestroyMode.None : configData.removeTypeS[removeType].gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None; foreach (var entity in entities) { if (DoRemove(entity, destroyMode) && ++removed % configData.globalS.removePerFrame == 0) yield return CoroutineEx.waitForEndOfFrame; } if (removeType == RemoveType.None) yield break; var toolRemover = player?.GetComponent(); switch (removeType) { case RemoveType.All: if (toolRemover != null && toolRemover.removeType == removeType) toolRemover.currentRemoved += removed; if (player != null) Print(player, Lang("CompletedRemoveAll", player.UserIDString, removed)); yield break; case RemoveType.Structure: if (toolRemover != null && toolRemover.removeType == removeType) toolRemover.currentRemoved += removed; if (player != null) Print(player, Lang("CompletedRemoveStructure", player.UserIDString, removed)); yield break; case RemoveType.External: if (toolRemover != null && toolRemover.removeType == removeType) toolRemover.currentRemoved += removed; if (player != null) Print(player, Lang("CompletedRemoveExternal", player.UserIDString, removed)); yield break; } } #region RemoveEntity Helpers private static IEnumerator GetNearbyEntities(T sourceEntity, HashSet removeList, int layers, Func filter = null) where T : BaseEntity { int current = 0; var checkFrom = Pool.Get>(); var nearbyEntities = Pool.GetList(); checkFrom.Enqueue(sourceEntity.transform.position); while (checkFrom.Count > 0) { nearbyEntities.Clear(); var position = checkFrom.Dequeue(); Vis.Entities(position, 3f, nearbyEntities, layers); for (var i = 0; i < nearbyEntities.Count; i++) { var entity = nearbyEntities[i]; if (filter != null && !filter(entity)) continue; if (!removeList.Add(entity)) continue; checkFrom.Enqueue(entity.transform.position); } if (++current % configData.globalS.removePerFrame == 0) yield return CoroutineEx.waitForEndOfFrame; } Pool.Free(ref checkFrom); Pool.FreeList(ref nearbyEntities); } private static IEnumerator ProcessContainer(HashSet removeList) { foreach (var entity in removeList) { var storageContainer = entity as StorageContainer; if (storageContainer != null && storageContainer.inventory?.itemList?.Count > 0) { if (configData.globalS.noItemContainerDrop) storageContainer.inventory.Clear(); else DropItemContainer(storageContainer.inventory, storageContainer.GetDropPosition(), storageContainer.transform.rotation); continue; } var containerIoEntity = entity as ContainerIOEntity; if (containerIoEntity != null && containerIoEntity.inventory?.itemList?.Count > 0) { if (configData.globalS.noItemContainerDrop) containerIoEntity.inventory.Clear(); else DropItemContainer(containerIoEntity.inventory, containerIoEntity.GetDropPosition(), containerIoEntity.transform.rotation); } } if (configData.globalS.noItemContainerDrop) ItemManager.DoRemoves(); yield break; } private static IEnumerator ProcessBuilding(DecayEntity sourceEntity, HashSet removeList) { var building = sourceEntity.GetBuilding(); if (building != null) { foreach (var entity in building.decayEntities) { if (!removeList.Add(entity)) continue; var storageContainer = entity as StorageContainer; if (storageContainer != null && storageContainer.inventory?.itemList?.Count > 0) { if (configData.globalS.noItemContainerDrop) storageContainer.inventory.Clear(); else DropItemContainer(storageContainer.inventory, storageContainer.GetDropPosition(), storageContainer.transform.rotation); } } } else removeList.Add(sourceEntity); if (configData.globalS.noItemContainerDrop) ItemManager.DoRemoves(); yield break; } private static bool DoRemove(BaseEntity entity, BaseNetworkable.DestroyMode destroyMode) { if (entity != null && !entity.IsDestroyed) { entity.Kill(destroyMode); return true; } return false; } private static void DoNormalRemove(BasePlayer player, BaseEntity entity, bool gibs = true) { if (entity != null && !entity.IsDestroyed) { Interface.CallHook("OnNormalRemovedEntity", player, entity); entity.Kill(gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None); } } #endregion RemoveEntity Helpers #endregion RemoveEntity #region API private bool IsToolRemover(BasePlayer player) => player?.GetComponent() != null; private string GetPlayerRemoveType(BasePlayer player) => player?.GetComponent()?.removeType.ToString(); #endregion API #region Commands private void CmdRemove(BasePlayer player, string command, string[] args) { if (args == null || args.Length == 0) { var sourceRemover = player.GetComponent(); if (sourceRemover != null) { sourceRemover.DisableTool(); return; } } if (removeOverride && !permission.UserHasPermission(player.UserIDString, PERMISSION_OVERRIDE)) { Print(player, Lang("CurrentlyDisabled", player.UserIDString)); return; } RemoveType removeType = RemoveType.Normal; int time = configData.removeTypeS[removeType].defaultTime; if (args != null && args.Length > 0) { switch (args[0].ToLower()) { case "n": case "normal": break; case "a": case "admin": removeType = RemoveType.Admin; time = configData.removeTypeS[removeType].defaultTime; if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN)) { Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_ADMIN)); return; } break; case "all": removeType = RemoveType.All; time = configData.removeTypeS[removeType].defaultTime; if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ALL)) { Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_ALL)); return; } break; case "s": case "structure": removeType = RemoveType.Structure; time = configData.removeTypeS[removeType].defaultTime; if (!permission.UserHasPermission(player.UserIDString, PERMISSION_STRUCTURE)) { Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_STRUCTURE)); return; } break; case "e": case "external": removeType = RemoveType.External; time = configData.removeTypeS[removeType].defaultTime; if (!permission.UserHasPermission(player.UserIDString, PERMISSION_EXTERNAL)) { Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_EXTERNAL)); return; } break; case "h": case "help": StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(Lang("Syntax", player.UserIDString, configData.chatS.command, GetRemoveTypeName(RemoveType.Normal))); stringBuilder.AppendLine(Lang("Syntax1", player.UserIDString, configData.chatS.command, GetRemoveTypeName(RemoveType.Admin))); stringBuilder.AppendLine(Lang("Syntax2", player.UserIDString, configData.chatS.command, GetRemoveTypeName(RemoveType.All))); stringBuilder.AppendLine(Lang("Syntax3", player.UserIDString, configData.chatS.command, GetRemoveTypeName(RemoveType.Structure))); stringBuilder.AppendLine(Lang("Syntax4", player.UserIDString, configData.chatS.command, GetRemoveTypeName(RemoveType.External))); Print(player, stringBuilder.ToString()); return; default: if (int.TryParse(args[0], out time)) break; Print(player, Lang("SyntaxError", player.UserIDString, configData.chatS.command)); return; } } if (args != null && args.Length > 1) int.TryParse(args[1], out time); ToggleRemove(player, removeType, time); } private bool ToggleRemove(BasePlayer player, RemoveType removeType, int time = 0) { if (removeType == RemoveType.Normal && !permission.UserHasPermission(player.UserIDString, PERMISSION_NORMAL)) { Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_NORMAL)); return false; } int maxRemovable = 0; bool pay = false, refund = false; var removeTypeS = configData.removeTypeS[removeType]; float distance = removeTypeS.distance; int maxTime = removeTypeS.maxTime; bool resetTime = removeTypeS.resetTime; float interval = configData.globalS.removeInterval; if (removeType == RemoveType.Normal) { var permissionS = GetPermissionS(player); var cooldown = permissionS.cooldown; if (cooldown > 0 && !(configData.globalS.cooldownExclude && player.IsAdmin)) { float lastUse; if (cooldownTimes.TryGetValue(player.userID, out lastUse)) { var timeLeft = cooldown - (Time.realtimeSinceStartup - lastUse); if (timeLeft > 0) { Print(player, Lang("Cooldown", player.UserIDString, Math.Ceiling(timeLeft))); return false; } } } if (removeMode == RemoveMode.MeleeHit && configData.removerModeS.meleeHitRequires) { if (!IsMeleeTool(player)) { Print(player, Lang("MeleeToolNotHeld", player.UserIDString)); return false; } } if (removeMode == RemoveMode.SpecificTool && configData.removerModeS.specificToolRequires) { if (!IsSpecificTool(player)) { Print(player, Lang("SpecificToolNotHeld", player.UserIDString)); return false; } } interval = permissionS.removeInterval; resetTime = permissionS.resetTime; maxTime = permissionS.maxTime; maxRemovable = permissionS.maxRemovable; if (configData.globalS.maxRemovableExclude && player.IsAdmin) maxRemovable = 0; distance = permissionS.distance; pay = permissionS.pay; refund = permissionS.refund; } if (time == 0) time = configData.removeTypeS[removeType].defaultTime; if (time > maxTime) time = maxTime; var toolRemover = player.GetComponent(); if (toolRemover == null) { toolRemover = player.gameObject.AddComponent(); } else if (toolRemover.removeType == RemoveType.Normal) { if (!configData.globalS.startCooldownOnRemoved) { cooldownTimes[player.userID] = Time.realtimeSinceStartup; } } toolRemover.Init(removeType, time, maxRemovable, distance, interval, pay, refund, resetTime, true); Print(player, Lang("ToolEnabled", player.UserIDString, time, maxRemovable == 0 ? Lang("Unlimit", player.UserIDString) : maxRemovable.ToString(), GetRemoveTypeName(removeType))); return true; } [ConsoleCommand("remove.toggle")] private void CCmdRemoveToggle(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) { Print(arg, "Syntax error!!! Please type the commands in the F1 console"); return; } CmdRemove(player, string.Empty, arg.Args); } [ConsoleCommand("remove.target")] private void CCmdRemoveTarget(ConsoleSystem.Arg arg) { if (arg.Args == null || arg.Args.Length <= 1) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Syntax error of target command"); stringBuilder.AppendLine("remove.target - Disable remover tool for player"); stringBuilder.AppendLine("remove.target [time (seconds)] [max removable objects (integer)] - Enable remover tool for player (Normal)"); stringBuilder.AppendLine("remove.target [time (seconds)] - Enable remover tool for player (Admin)"); stringBuilder.AppendLine("remove.target [time (seconds)] - Enable remover tool for player (All)"); stringBuilder.AppendLine("remove.target [time (seconds)] - Enable remover tool for player (Structure)"); stringBuilder.AppendLine("remove.target [time (seconds)] - Enable remover tool for player (External)"); Print(arg, stringBuilder.ToString()); return; } var player = arg.Player(); if (player != null && !permission.UserHasPermission(player.UserIDString, PERMISSION_TARGET)) { Print(arg, Lang("NotAllowed", player.UserIDString, PERMISSION_TARGET)); return; } var target = RustCore.FindPlayer(arg.Args[1]); if (target == null || !target.IsConnected) { Print(arg, target == null ? $"'{arg.Args[0]}' cannot be found." : $"'{target}' is offline."); return; } RemoveType removeType = RemoveType.Normal; switch (arg.Args[0].ToLower()) { case "n": case "normal": break; case "a": case "admin": removeType = RemoveType.Admin; break; case "all": removeType = RemoveType.All; break; case "s": case "structure": removeType = RemoveType.Structure; break; case "e": case "external": removeType = RemoveType.External; break; case "d": case "disable": var toolRemover = target.GetComponent(); if (toolRemover != null) { toolRemover.DisableTool(); Print(arg, $"{target}'s remover tool is disabled"); } else Print(arg, $"{target} did not enable the remover tool"); return; default: StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Syntax error of target command"); stringBuilder.AppendLine("remove.target - Disable remover tool for player"); stringBuilder.AppendLine("remove.target [time (seconds)] [max removable objects (integer)] - Enable remover tool for player (Normal)"); stringBuilder.AppendLine("remove.target [time (seconds)] - Enable remover tool for player (Admin)"); stringBuilder.AppendLine("remove.target [time (seconds)] - Enable remover tool for player (All)"); stringBuilder.AppendLine("remove.target [time (seconds)] - Enable remover tool for player (Structure)"); stringBuilder.AppendLine("remove.target [time (seconds)] - Enable remover tool for player (External)"); Print(arg, stringBuilder.ToString()); return; } int maxRemovable = 0; int time = configData.removeTypeS[removeType].defaultTime; if (arg.Args.Length > 2) int.TryParse(arg.Args[2], out time); if (arg.Args.Length > 3 && removeType == RemoveType.Normal) int.TryParse(arg.Args[3], out maxRemovable); var targetRemover = target.GetComponent(); if (targetRemover == null) targetRemover = target.gameObject.AddComponent(); var permissionS = configData.permS[PERMISSION_NORMAL]; targetRemover.Init(removeType, time, maxRemovable, configData.removeTypeS[removeType].distance, permissionS.removeInterval, permissionS.pay, permissionS.refund, permissionS.resetTime, false); Print(arg, Lang("TargetEnabled", player?.UserIDString, target, time, maxRemovable, GetRemoveTypeName(removeType))); } [ConsoleCommand("remove.building")] private void CCmdConstruction(ConsoleSystem.Arg arg) { if (arg.Args == null || arg.Args.Length <= 1 || !arg.IsAdmin) { Print(arg, $"Syntax error, Please type 'remove.building ', e.g.'remove.building price 60'"); return; } float value; switch (arg.Args[0].ToLower()) { case "price": if (!float.TryParse(arg.Args[1], out value)) value = 50f; foreach (var construction in constructions) { ConfigData.RemoveSettings.BuildingBlocksS buildingBlocksS; if (configData.removeS.buildingBlockS.TryGetValue(construction.info.name.english, out buildingBlocksS)) { foreach (var entry in buildingBlocksS.buildingGradeS) { var grade = construction.grades[(int)entry.Key]; entry.Value.price = grade.costToBuild.ToDictionary(x => x.itemDef.shortname, y => value <= 0 ? 0 : Mathf.RoundToInt(y.amount * value / 100)); } } } Print(arg, $"Successfully modified all building prices to {value}% of the initial cost."); SaveConfig(); return; case "refund": if (!float.TryParse(arg.Args[1], out value)) value = 40f; foreach (var construction in constructions) { ConfigData.RemoveSettings.BuildingBlocksS buildingBlocksS; if (configData.removeS.buildingBlockS.TryGetValue(construction.info.name.english, out buildingBlocksS)) { foreach (var entry in buildingBlocksS.buildingGradeS) { var grade = construction.grades[(int)entry.Key]; entry.Value.refund = grade.costToBuild.ToDictionary(x => x.itemDef.shortname, y => value <= 0 ? 0 : Mathf.RoundToInt(y.amount * value / 100)); } } } Print(arg, $"Successfully modified all building refunds to {value}% of the initial cost."); SaveConfig(); return; case "pricep": if (!float.TryParse(arg.Args[1], out value)) value = 40f; foreach (var buildingBlockS in configData.removeS.buildingBlockS.Values) { foreach (var data in buildingBlockS.buildingGradeS.Values) data.price = value <= 0 ? 0 : value; } Print(arg, $"Successfully modified all building prices to {value}% of the initial cost."); SaveConfig(); return; case "refundp": if (!float.TryParse(arg.Args[1], out value)) value = 50f; foreach (var buildingBlockS in configData.removeS.buildingBlockS.Values) { foreach (var data in buildingBlockS.buildingGradeS.Values) data.refund = value <= 0 ? 0 : value; } Print(arg, $"Successfully modified all building refunds to {value}% of the initial cost."); SaveConfig(); return; default: Print(arg, $"Syntax error, Please type 'remove.building ', e.g.'remove.building price 60'"); return; } } [ConsoleCommand("remove.allow")] private void CCmdRemoveAllow(ConsoleSystem.Arg arg) { if (arg.Args == null || arg.Args.Length == 0) { Print(arg, "Syntax error, Please type 'remove.allow '"); return; } var player = arg.Player(); if (player != null && !permission.UserHasPermission(player.UserIDString, PERMISSION_OVERRIDE)) { Print(arg, Lang("NotAllowed", player.UserIDString, PERMISSION_OVERRIDE)); return; } switch (arg.Args[0].ToLower()) { case "true": case "1": removeOverride = false; Print(arg, "Remove is now allowed depending on your settings."); return; case "false": case "0": removeOverride = true; Print(arg, "Remove is now restricted for all players (exept admins)"); foreach (var p in BasePlayer.activePlayerList) { var toolRemover = p.GetComponent(); if (toolRemover == null) continue; if (toolRemover.removeType == RemoveType.Normal && toolRemover.canOverride) { Print(toolRemover.player, "The remover tool has been disabled by the admin"); toolRemover.DisableTool(false); } } return; default: Print(arg, "This is not a valid argument"); return; } } [ConsoleCommand("remove.playerentity")] private void CCmdRemoveEntity(ConsoleSystem.Arg arg) { if (arg.Args == null || arg.Args.Length <= 1 || !arg.IsAdmin) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Syntax error of remove.playerentity command"); stringBuilder.AppendLine("remove.playerentity - Remove all entities of the player"); stringBuilder.AppendLine("remove.playerentity - Remove all buildings of the player"); stringBuilder.AppendLine("remove.playerentity - Remove buildings of the player owned cupboard"); Print(arg, stringBuilder.ToString()); return; } if (removePlayerEntityCoroutine != null) { Print(arg, "There is already a RemovePlayerEntity running, please wait."); return; } ulong targetID; if (!ulong.TryParse(arg.Args[1], out targetID) || !targetID.IsSteamId()) { Print(arg, "Please enter the player's steamID."); return; } RemovePlayerEntityType removePlayerEntityType; switch (arg.Args[0].ToLower()) { case "a": case "all": removePlayerEntityType = RemovePlayerEntityType.All; break; case "b": case "building": removePlayerEntityType = RemovePlayerEntityType.Building; break; case "c": case "cupboard": removePlayerEntityType = RemovePlayerEntityType.Cupboard; break; default: Print(arg, "This is not a valid argument"); return; } removePlayerEntityCoroutine = ServerMgr.Instance.StartCoroutine(RemovePlayerEntity(arg, targetID, removePlayerEntityType)); Print(arg, "Start running RemovePlayerEntity, please wait."); } #endregion Commands #region ConfigurationFile private void UpdateConfig() { var buildingGrades = new[] { BuildingGrade.Enum.Twigs, BuildingGrade.Enum.Wood, BuildingGrade.Enum.Stone, BuildingGrade.Enum.Metal, BuildingGrade.Enum.TopTier }; foreach (var value in buildingGrades) { if (!configData.removeS.validConstruction.ContainsKey(value)) { configData.removeS.validConstruction.Add(value, true); } } var newBuildingBlocksS = new Dictionary(); foreach (var construction in constructions) { ConfigData.RemoveSettings.BuildingBlocksS buildingBlocksS; if (!configData.removeS.buildingBlockS.TryGetValue(construction.info.name.english, out buildingBlocksS)) { var buildingGrade = new Dictionary(); foreach (var value in buildingGrades) { var grade = construction.grades[(int)value]; buildingGrade.Add(value, new ConfigData.RemoveSettings.BuildingBlocksS.BuildingGradeS { refund = grade.costToBuild.ToDictionary(x => x.itemDef.shortname, y => Mathf.RoundToInt(y.amount * 0.4f)), price = grade.costToBuild.ToDictionary(x => x.itemDef.shortname, y => Mathf.RoundToInt(y.amount * 0.6f)) }); } buildingBlocksS = new ConfigData.RemoveSettings.BuildingBlocksS { displayName = construction.info.name.english, buildingGradeS = buildingGrade }; } newBuildingBlocksS.Add(construction.info.name.english, buildingBlocksS); } configData.removeS.buildingBlockS = newBuildingBlocksS; foreach (var entry in shorPrefabNameToDeployable) { ConfigData.RemoveSettings.EntityS entityS; if (!configData.removeS.entityS.TryGetValue(entry.Key, out entityS)) { var itemDefinition = ItemManager.FindItemDefinition(entry.Value); entityS = new ConfigData.RemoveSettings.EntityS { enabled = itemDefinition.category != ItemCategory.Food, displayName = itemDefinition.displayName.english, refund = new Dictionary { [entry.Value] = 1 }, price = new Dictionary() }; configData.removeS.entityS.Add(entry.Key, entityS); } } SaveConfig(); } private static ConfigData configData; private class ConfigData { [JsonProperty(PropertyName = "Settings")] public GlobalSettings globalS = new GlobalSettings(); public class GlobalSettings { [JsonProperty(PropertyName = "Use Teams")] public bool useTeams = false; [JsonProperty(PropertyName = "Use Clans")] public bool useClans = true; [JsonProperty(PropertyName = "Use Friends")] public bool useFriends = true; [JsonProperty(PropertyName = "Use Entity Owners")] public bool useEntityOwners = true; [JsonProperty(PropertyName = "Use Building Locks")] public bool useBuildingLocks = false; [JsonProperty(PropertyName = "Use Tool Cupboards (Strongly unrecommended)")] public bool useToolCupboards = false; [JsonProperty(PropertyName = "Use Building Owners (You will need BuildingOwners plugin)")] public bool useBuildingOwners = false; //[JsonProperty(PropertyName = "Exclude Twigs (Used for \"Use Tool Cupboards\" and \"Use Entity Owners\")")] //public bool excludeTwigs; [JsonProperty(PropertyName = "Remove Button")] public string removeButton = BUTTON.FIRE_PRIMARY.ToString(); [JsonProperty(PropertyName = "Remove Interval (Min = 0.2)")] public float removeInterval = 0.5f; [JsonProperty(PropertyName = "Only start cooldown when an entity is removed")] public bool startCooldownOnRemoved; [JsonProperty(PropertyName = "RemoveType - All/Structure - Remove per frame")] public int removePerFrame = 15; [JsonProperty(PropertyName = "RemoveType - All/Structure - No item container dropped")] public bool noItemContainerDrop = true; [JsonProperty(PropertyName = "RemoveType - Normal - Max Removable Objects - Exclude admins")] public bool maxRemovableExclude = true; [JsonProperty(PropertyName = "RemoveType - Normal - Cooldown - Exclude admins")] public bool cooldownExclude = true; [JsonProperty(PropertyName = "RemoveType - Normal - Check stash under the foundation")] public bool checkStash = false; [JsonProperty(PropertyName = "RemoveType - Normal - Entity Spawned Time Limit - Enabled")] public bool entityTimeLimit = false; [JsonProperty(PropertyName = "RemoveType - Normal - Entity Spawned Time Limit - Cannot be removed when entity spawned time more than it")] public float limitTime = 300f; } [JsonProperty(PropertyName = "Container Settings")] public ContainerSettings containerS = new ContainerSettings(); public class ContainerSettings { [JsonProperty(PropertyName = "Storage Container - Enable remove of not empty storages")] public bool removeNotEmptyStorage = true; [JsonProperty(PropertyName = "Storage Container - Drop items from container")] public bool dropItemsStorage = false; [JsonProperty(PropertyName = "Storage Container - Drop a item container from container")] public bool dropContainerStorage = true; [JsonProperty(PropertyName = "IOEntity Container - Enable remove of not empty storages")] public bool removeNotEmptyIoEntity = true; [JsonProperty(PropertyName = "IOEntity Container - Drop items from container")] public bool dropItemsIoEntity = false; [JsonProperty(PropertyName = "IOEntity Container - Drop a item container from container")] public bool dropContainerIoEntity = true; } [JsonProperty(PropertyName = "Remove Damaged Entities")] public DamagedEntitySettings damagedEntityS = new DamagedEntitySettings(); public class DamagedEntitySettings { [JsonProperty(PropertyName = "Enabled")] public bool enabled = false; [JsonProperty(PropertyName = "Exclude Building Blocks")] public bool excludeBuildingBlocks = true; [JsonProperty(PropertyName = "Percentage (Can be removed when (health / max health * 100) is not less than it)")] public float percentage = 90f; } [JsonProperty(PropertyName = "Chat Settings")] public ChatSettings chatS = new ChatSettings(); public class ChatSettings { [JsonProperty(PropertyName = "Chat Command")] public string command = "remove"; [JsonProperty(PropertyName = "Chat Prefix")] public string prefix = "[RemoverTool]: "; [JsonProperty(PropertyName = "Chat SteamID Icon")] public ulong steamIDIcon = 0; } [JsonProperty(PropertyName = "Permission Settings (Just for normal type)")] public Dictionary permS = new Dictionary { [PERMISSION_NORMAL] = new PermSettings { priority = 0, distance = 3, cooldown = 60, maxTime = 300, maxRemovable = 50, removeInterval = 0.8f, pay = true, refund = true, resetTime = false } }; public class PermSettings { [JsonProperty(PropertyName = "Priority")] public int priority; [JsonProperty(PropertyName = "Distance")] public float distance; [JsonProperty(PropertyName = "Cooldown")] public float cooldown; [JsonProperty(PropertyName = "Max Time")] public int maxTime; [JsonProperty(PropertyName = "Remove Interval (Min = 0.2)")] public float removeInterval; [JsonProperty(PropertyName = "Max Removable Objects (0 = Unlimit)")] public int maxRemovable; [JsonProperty(PropertyName = "Pay")] public bool pay; [JsonProperty(PropertyName = "Refund")] public bool refund; [JsonProperty(PropertyName = "Reset the time after removing an entity")] public bool resetTime; } [JsonProperty(PropertyName = "Remove Type Settings")] public Dictionary removeTypeS = new Dictionary { [RemoveType.Normal] = new RemoveTypeSettings { displayName = RemoveType.Normal.ToString(), distance = 3, gibs = true, defaultTime = 60, maxTime = 300, resetTime = false }, [RemoveType.Structure] = new RemoveTypeSettings { displayName = RemoveType.Structure.ToString(), distance = 100, gibs = false, defaultTime = 300, maxTime = 600, resetTime = true }, [RemoveType.All] = new RemoveTypeSettings { displayName = RemoveType.All.ToString(), distance = 50, gibs = false, defaultTime = 300, maxTime = 600, resetTime = true }, [RemoveType.Admin] = new RemoveTypeSettings { displayName = RemoveType.Admin.ToString(), distance = 20, gibs = true, defaultTime = 300, maxTime = 600, resetTime = true }, [RemoveType.External] = new RemoveTypeSettings { displayName = RemoveType.External.ToString(), distance = 20, gibs = true, defaultTime = 300, maxTime = 600, resetTime = true } }; public class RemoveTypeSettings { [JsonProperty(PropertyName = "Display Name")] public string displayName; [JsonProperty(PropertyName = "Distance")] public float distance; [JsonProperty(PropertyName = "Default Time")] public int defaultTime; [JsonProperty(PropertyName = "Max Time")] public int maxTime; [JsonProperty(PropertyName = "Gibs")] public bool gibs; [JsonProperty(PropertyName = "Reset the time after removing an entity")] public bool resetTime; } [JsonProperty(PropertyName = "Remove Mode Settings (Only one model works)")] public RemoverModeSettings removerModeS = new RemoverModeSettings(); public class RemoverModeSettings { [JsonProperty(PropertyName = "No Held Item Mode")] public bool noHeldMode = true; [JsonProperty(PropertyName = "No Held Item Mode - Disable remover tool when you have any item in hand")] public bool noHeldDisableInHand = true; [JsonProperty(PropertyName = "No Held Item Mode - Show Crosshair")] public bool showCrosshair = true; [JsonProperty(PropertyName = "No Held Item Mode - Crosshair Image Url")] public string crosshairImageUrl = "https://i.imgur.com/SqLCJaQ.png"; [JsonProperty(PropertyName = "No Held Item Mode - Crosshair Box - Min Anchor (in Rust Window)")] public string crosshairAnchorMin = "0.5 0.5"; [JsonProperty(PropertyName = "No Held Item Mode - Crosshair Box - Max Anchor (in Rust Window)")] public string crosshairAnchorMax = "0.5 0.5"; [JsonProperty(PropertyName = "No Held Item Mode - Crosshair Box - Min Offset (in Rust Window)")] public string crosshairOffsetMin = "-15 -15"; [JsonProperty(PropertyName = "No Held Item Mode - Crosshair Box - Max Offset (in Rust Window)")] public string crosshairOffsetMax = "15 15"; [JsonProperty(PropertyName = "No Held Item Mode - Crosshair Box - Image Color")] public string crosshairColor = "1 0 0 1"; [JsonProperty(PropertyName = "Melee Tool Hit Mode")] public bool meleeHitMode = false; [JsonProperty(PropertyName = "Melee Tool Hit Mode - Item shortname")] public string meleeHitItemShortname = "hammer"; [JsonProperty(PropertyName = "Melee Tool Hit Mode - Item skin (-1 = All skins)")] public long meleeHitModeSkin = -1; [JsonProperty(PropertyName = "Melee Tool Hit Mode - Auto enable remover tool when you hold a melee tool")] public bool meleeHitEnableInHand = false; [JsonProperty(PropertyName = "Melee Tool Hit Mode - Requires a melee tool in your hand when remover tool is enabled")] public bool meleeHitRequires = false; [JsonProperty(PropertyName = "Melee Tool Hit Mode - Disable remover tool when you are not holding a melee tool")] public bool meleeHitDisableInHand = false; [JsonProperty(PropertyName = "Specific Tool Mode")] public bool specificToolMode = false; [JsonProperty(PropertyName = "Specific Tool Mode - Item shortname")] public string specificToolShortname = "hammer"; [JsonProperty(PropertyName = "Specific Tool Mode - Item skin (-1 = All skins)")] public long specificToolSkin = -1; [JsonProperty(PropertyName = "Specific Tool Mode - Auto enable remover tool when you hold a specific tool")] public bool specificToolEnableInHand = false; [JsonProperty(PropertyName = "Specific Tool Mode - Requires a specific tool in your hand when remover tool is enabled")] public bool specificToolRequires = false; [JsonProperty(PropertyName = "Specific Tool Mode - Disable remover tool when you are not holding a specific tool")] public bool specificToolDisableInHand = false; } [JsonProperty(PropertyName = "Raid Blocker Settings")] public RaidBlockerSettings raidS = new RaidBlockerSettings(); public class RaidBlockerSettings { [JsonProperty(PropertyName = "Enabled")] public bool enabled = false; [JsonProperty(PropertyName = "Block Time")] public float blockTime = 300; [JsonProperty(PropertyName = "By Buildings")] public bool blockBuildingID = true; [JsonProperty(PropertyName = "By Surrounding Players")] public bool blockPlayers = true; [JsonProperty(PropertyName = "By Surrounding Players - Radius")] public float blockRadius = 120; } [JsonProperty(PropertyName = "Image Urls (Used to UI image)")] public Dictionary imageUrls = new Dictionary { ["Economics"] = "https://i.imgur.com/znPwdcv.png", ["ServerRewards"] = "https://i.imgur.com/04rJsV3.png" }; [JsonProperty(PropertyName = "Display Names Of Other Things")] public Dictionary displayNames = new Dictionary(); [JsonProperty(PropertyName = "GUI")] public UiSettings uiS = new UiSettings(); public class UiSettings { [JsonProperty(PropertyName = "Enabled")] public bool enabled = true; [JsonProperty(PropertyName = "Main Box - Min Anchor (in Rust Window)")] public string removerToolAnchorMin = "0 1"; [JsonProperty(PropertyName = "Main Box - Max Anchor (in Rust Window)")] public string removerToolAnchorMax = "0 1"; [JsonProperty(PropertyName = "Main Box - Min Offset (in Rust Window)")] public string removerToolOffsetMin = "30 -330"; [JsonProperty(PropertyName = "Main Box - Max Offset (in Rust Window)")] public string removerToolOffsetMax = "470 -40"; [JsonProperty(PropertyName = "Main Box - Background Color")] public string removerToolBackgroundColor = "0 0 0 0"; [JsonProperty(PropertyName = "Remove Title - Box - Min Anchor (in Main Box)")] public string removeAnchorMin = "0 0.84"; [JsonProperty(PropertyName = "Remove Title - Box - Max Anchor (in Main Box)")] public string removeAnchorMax = "0.996 1"; [JsonProperty(PropertyName = "Remove Title - Box - Background Color")] public string removeBackgroundColor = "0.31 0.88 0.71 1"; [JsonProperty(PropertyName = "Remove Title - Text - Min Anchor (in Main Box)")] public string removeTextAnchorMin = "0.05 0.84"; [JsonProperty(PropertyName = "Remove Title - Text - Max Anchor (in Main Box)")] public string removeTextAnchorMax = "0.6 1"; [JsonProperty(PropertyName = "Remove Title - Text - Text Color")] public string removeTextColor = "1 0.1 0.1 1"; [JsonProperty(PropertyName = "Remove Title - Text - Text Size")] public int removeTextSize = 18; [JsonProperty(PropertyName = "Timeleft - Box - Min Anchor (in Main Box)")] public string timeLeftAnchorMin = "0.6 0.84"; [JsonProperty(PropertyName = "Timeleft - Box - Max Anchor (in Main Box)")] public string timeLeftAnchorMax = "1 1"; [JsonProperty(PropertyName = "Timeleft - Box - Background Color")] public string timeLeftBackgroundColor = "0 0 0 0"; [JsonProperty(PropertyName = "Timeleft - Text - Min Anchor (in Timeleft Box)")] public string timeLeftTextAnchorMin = "0 0"; [JsonProperty(PropertyName = "Timeleft - Text - Max Anchor (in Timeleft Box)")] public string timeLeftTextAnchorMax = "0.9 1"; [JsonProperty(PropertyName = "Timeleft - Text - Text Color")] public string timeLeftTextColor = "0 0 0 0.9"; [JsonProperty(PropertyName = "Timeleft - Text - Text Size")] public int timeLeftTextSize = 15; [JsonProperty(PropertyName = "Entity - Box - Min Anchor (in Main Box)")] public string entityAnchorMin = "0 0.68"; [JsonProperty(PropertyName = "Entity - Box - Max Anchor (in Main Box)")] public string entityAnchorMax = "1 0.84"; [JsonProperty(PropertyName = "Entity - Box - Background Color")] public string entityBackgroundColor = "0.82 0.58 0.30 1"; [JsonProperty(PropertyName = "Entity - Text - Min Anchor (in Entity Box)")] public string entityTextAnchorMin = "0.05 0"; [JsonProperty(PropertyName = "Entity - Text - Max Anchor (in Entity Box)")] public string entityTextAnchorMax = "1 1"; [JsonProperty(PropertyName = "Entity - Text - Text Color")] public string entityTextColor = "1 1 1 1"; [JsonProperty(PropertyName = "Entity - Text - Text Size")] public int entityTextSize = 16; [JsonProperty(PropertyName = "Entity - Image - Enabled")] public bool entityImageEnabled = true; [JsonProperty(PropertyName = "Entity - Image - Min Anchor (in Entity Box)")] public string entityImageAnchorMin = "0.795 0.01"; [JsonProperty(PropertyName = "Entity - Image - Max Anchor (in Entity Box)")] public string entityImageAnchorMax = "0.9 0.99"; [JsonProperty(PropertyName = "Authorization Check Enabled")] public bool authorizationEnabled = true; [JsonProperty(PropertyName = "Authorization Check - Box - Min Anchor (in Main Box)")] public string authorizationsAnchorMin = "0 0.6"; [JsonProperty(PropertyName = "Authorization Check - Box - Max Anchor (in Main Box)")] public string authorizationsAnchorMax = "1 0.68"; [JsonProperty(PropertyName = "Authorization Check - Box - Allowed Background Color")] public string allowedBackgroundColor = "0.22 0.78 0.27 1"; [JsonProperty(PropertyName = "Authorization Check - Box - Refused Background Color")] public string refusedBackgroundColor = "0.78 0.22 0.27 1"; [JsonProperty(PropertyName = "Authorization Check - Text - Min Anchor (in Authorization Check Box)")] public string authorizationsTextAnchorMin = "0.05 0"; [JsonProperty(PropertyName = "Authorization Check - Text - Max Anchor (in Authorization Check Box)")] public string authorizationsTextAnchorMax = "1 1"; [JsonProperty(PropertyName = "Authorization Check - Text - Text Color")] public string authorizationsTextColor = "1 1 1 0.9"; [JsonProperty(PropertyName = "Authorization Check Box - Text - Text Size")] public int authorizationsTextSize = 14; [JsonProperty(PropertyName = "Price & Refund - Image Enabled")] public bool imageEnabled = true; [JsonProperty(PropertyName = "Price & Refund - Image Scale")] public float imageScale = 0.18f; [JsonProperty(PropertyName = "Price & Refund - Distance of image from right border")] public float rightDistance = 0.05f; [JsonProperty(PropertyName = "Price Enabled")] public bool priceEnabled = true; [JsonProperty(PropertyName = "Price - Box - Min Anchor (in Main Box)")] public string priceAnchorMin = "0 0.3"; [JsonProperty(PropertyName = "Price - Box - Max Anchor (in Main Box)")] public string priceAnchorMax = "1 0.6"; [JsonProperty(PropertyName = "Price - Box - Background Color")] public string priceBackgroundColor = "0 0 0 0.8"; [JsonProperty(PropertyName = "Price - Text - Min Anchor (in Price Box)")] public string priceTextAnchorMin = "0.05 0"; [JsonProperty(PropertyName = "Price - Text - Max Anchor (in Price Box)")] public string priceTextAnchorMax = "0.25 1"; [JsonProperty(PropertyName = "Price - Text - Text Color")] public string priceTextColor = "1 1 1 0.9"; [JsonProperty(PropertyName = "Price - Text - Text Size")] public int priceTextSize = 18; [JsonProperty(PropertyName = "Price - Text2 - Min Anchor (in Price Box)")] public string price2TextAnchorMin = "0.3 0"; [JsonProperty(PropertyName = "Price - Text2 - Max Anchor (in Price Box)")] public string price2TextAnchorMax = "1 1"; [JsonProperty(PropertyName = "Price - Text2 - Text Color")] public string price2TextColor = "1 1 1 0.9"; [JsonProperty(PropertyName = "Price - Text2 - Text Size")] public int price2TextSize = 16; [JsonProperty(PropertyName = "Refund Enabled")] public bool refundEnabled = true; [JsonProperty(PropertyName = "Refund - Box - Min Anchor (in Main Box)")] public string refundAnchorMin = "0 0"; [JsonProperty(PropertyName = "Refund - Box - Max Anchor (in Main Box)")] public string refundAnchorMax = "1 0.3"; [JsonProperty(PropertyName = "Refund - Box - Background Color")] public string refundBackgroundColor = "0 0 0 0.8"; [JsonProperty(PropertyName = "Refund - Text - Min Anchor (in Refund Box)")] public string refundTextAnchorMin = "0.05 0"; [JsonProperty(PropertyName = "Refund - Text - Max Anchor (in Refund Box)")] public string refundTextAnchorMax = "0.25 1"; [JsonProperty(PropertyName = "Refund - Text - Text Color")] public string refundTextColor = "1 1 1 0.9"; [JsonProperty(PropertyName = "Refund - Text - Text Size")] public int refundTextSize = 18; [JsonProperty(PropertyName = "Refund - Text2 - Min Anchor (in Refund Box)")] public string refund2TextAnchorMin = "0.3 0"; [JsonProperty(PropertyName = "Refund - Text2 - Max Anchor (in Refund Box)")] public string refund2TextAnchorMax = "1 1"; [JsonProperty(PropertyName = "Refund - Text2 - Text Color")] public string refund2TextColor = "1 1 1 0.9"; [JsonProperty(PropertyName = "Refund - Text2 - Text Size")] public int refund2TextSize = 16; [JsonIgnore] public Vector2 Price2TextAnchorMin, Price2TextAnchorMax, Refund2TextAnchorMin, Refund2TextAnchorMax; } [JsonProperty(PropertyName = "Remove Info (Refund & Price)")] public RemoveSettings removeS = new RemoveSettings(); public class RemoveSettings { [JsonProperty(PropertyName = "Price Enabled")] public bool priceEnabled = true; [JsonProperty(PropertyName = "Refund Enabled")] public bool refundEnabled = true; [JsonProperty(PropertyName = "Refund Items In Entity Slot")] public bool refundSlot = true; [JsonProperty(PropertyName = "Allowed Building Grade")] public Dictionary validConstruction = new Dictionary(); [JsonProperty(PropertyName = "Building Blocks Settings")] public Dictionary buildingBlockS = new Dictionary(); public class BuildingBlocksS { [JsonProperty(PropertyName = "Display Name")] public string displayName; [JsonProperty(PropertyName = "Building Grade")] public Dictionary buildingGradeS = new Dictionary(); public class BuildingGradeS { [JsonProperty(PropertyName = "Price")] public object price; [JsonProperty(PropertyName = "Refund")] public object refund; [JsonIgnore] public float pricePercentage = -1, refundPercentage = -1; [JsonIgnore] public Dictionary priceDic, refundDic; } } [JsonProperty(PropertyName = "Other Entity Settings")] public Dictionary entityS = new Dictionary(); public class EntityS { [JsonProperty(PropertyName = "Remove Allowed")] public bool enabled = false; [JsonProperty(PropertyName = "Display Name")] public string displayName = string.Empty; [JsonProperty(PropertyName = "Price")] public Dictionary price = new Dictionary(); [JsonProperty(PropertyName = "Refund")] public Dictionary refund = new Dictionary(); } } [JsonProperty(PropertyName = "Version")] public VersionNumber version; } protected override void LoadConfig() { base.LoadConfig(); try { PreprocessOldConfig(); configData = Config.ReadObject(); if (configData == null) { LoadDefaultConfig(); } else { UpdateConfigValues(); } } catch (Exception ex) { PrintError($"The configuration file is corrupted. \n{ex}"); LoadDefaultConfig(); } SaveConfig(); PreprocessConfigValues(); } protected override void LoadDefaultConfig() { PrintWarning("Creating a new configuration file"); configData = new ConfigData(); configData.version = Version; } protected override void SaveConfig() => Config.WriteObject(configData); private void PreprocessConfigValues() { configData.uiS.Price2TextAnchorMin = GetAnchor(configData.uiS.price2TextAnchorMin); configData.uiS.Price2TextAnchorMax = GetAnchor(configData.uiS.price2TextAnchorMax); configData.uiS.Refund2TextAnchorMin = GetAnchor(configData.uiS.refund2TextAnchorMin); configData.uiS.Refund2TextAnchorMax = GetAnchor(configData.uiS.refund2TextAnchorMax); foreach (var entry in configData.removeS.buildingBlockS) { foreach (var gradeEntry in entry.Value.buildingGradeS) { var price = gradeEntry.Value.price; float pricePercentage; if (float.TryParse(price.ToString(), out pricePercentage)) { gradeEntry.Value.pricePercentage = pricePercentage; } else { var priceDic = price as Dictionary; if (priceDic != null) { gradeEntry.Value.priceDic = priceDic; } else { try { gradeEntry.Value.priceDic = JsonConvert.DeserializeObject>(price.ToString()); } catch (Exception e) { gradeEntry.Value.priceDic = null; PrintError($"Wrong price format for '{gradeEntry.Key}' of '{entry.Key}' in 'Building Blocks Settings'. Error Message: {e.Message}"); } } } var refund = gradeEntry.Value.refund; float refundPercentage; if (float.TryParse(refund.ToString(), out refundPercentage)) { gradeEntry.Value.refundPercentage = refundPercentage; } else { var refundDic = refund as Dictionary; if (refundDic != null) { gradeEntry.Value.refundDic = refundDic; } else { try { gradeEntry.Value.refundDic = JsonConvert.DeserializeObject>(refund.ToString()); } catch (Exception e) { gradeEntry.Value.refundDic = null; PrintError($"Wrong refund format for '{gradeEntry.Key}' of '{entry.Key}' in 'Building Blocks Settings'. Error Message: {e.Message}"); } } } } } } private void UpdateConfigValues() { if (configData.version < Version) { if (configData.version <= default(VersionNumber)) { string prefix, prefixColor; if (GetConfigValue(out prefix, "Chat Settings", "Chat Prefix") && GetConfigValue(out prefixColor, "Chat Settings", "Chat Prefix Color")) { configData.chatS.prefix = $"{prefix}: "; } if (configData.uiS.removerToolAnchorMin == "0.1 0.55") { configData.uiS.removerToolAnchorMin = "0.04 0.55"; } if (configData.uiS.removerToolAnchorMax == "0.4 0.95") { configData.uiS.removerToolAnchorMax = "0.37 0.95"; } } if (configData.version <= new VersionNumber(4, 3, 18)) { configData.removerModeS.crosshairAnchorMin = "0.5 0.5"; configData.removerModeS.crosshairAnchorMax = "0.5 0.5"; configData.uiS.removerToolAnchorMin = "0 1"; configData.uiS.removerToolAnchorMax = "0 1"; } if (configData.version <= new VersionNumber(4, 3, 21)) { if (configData.removerModeS.crosshairAnchorMin == "0.5 0") { configData.removerModeS.crosshairAnchorMin = "0.5 0.5"; } if (configData.removerModeS.crosshairAnchorMax == "0.5 0") { configData.removerModeS.crosshairAnchorMax = "0.5 0.5"; } } if (configData.version <= new VersionNumber(4, 3, 22)) { bool enabled; if (GetConfigValue(out enabled, "Remove Mode Settings (Only one model works)", "Hammer Hit Mode")) { configData.removerModeS.meleeHitMode = true; configData.removerModeS.meleeHitItemShortname = "hammer"; } 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")) { configData.removerModeS.meleeHitRequires = true; } if (GetConfigValue(out enabled, "Remove Mode Settings (Only one model works)", "Hammer Hit Mode - Disable remover tool when you are not holding a hammer")) { configData.removerModeS.meleeHitDisableInHand = true; } } if (configData.version <= new VersionNumber(4, 3, 23)) { string value; if (GetConfigValue(out value, "GUI", "Authorization Check - Box - Allowed Background")) { configData.uiS.allowedBackgroundColor = value == "0 1 0 0.8" ? "0.22 0.78 0.27 1" : value; } if (GetConfigValue(out value, "GUI", "Authorization Check - Box - Refused Background")) { configData.uiS.refusedBackgroundColor = value == "1 0 0 0.8" ? "0.78 0.22 0.27 1" : value; } if (configData.uiS.removeBackgroundColor == "0.42 0.88 0.88 1") configData.uiS.removeBackgroundColor = "0.31 0.88 0.71 1"; if (configData.uiS.entityBackgroundColor == "0 0 0 0.8") configData.uiS.entityBackgroundColor = "0.82 0.58 0.30 1"; } configData.version = Version; } } private bool GetConfigValue(out T value, params string[] path) { var configValue = Config.Get(path); if (configValue != null) { if (configValue is T) { value = (T)configValue; return true; } try { value = Config.ConvertValue(configValue); return true; } catch (Exception ex) { PrintError($"GetConfigValue ERROR: path: {string.Join("\\", path)}\n{ex}"); } } value = default(T); return false; } private void SetConfigValue(params object[] pathAndTrailingValue) { Config.Set(pathAndTrailingValue); } #region Preprocess Old Config private void PreprocessOldConfig() { var jObject = Config.ReadObject(); if (jObject == null) return; //Interface.Oxide.DataFileSystem.WriteObject(Name + "_old", jObject); VersionNumber oldVersion; if (GetConfigVersionPre(jObject, out oldVersion)) { if (oldVersion < Version) { //Fixed typos if (oldVersion <= new VersionNumber(4, 3, 23)) { foreach (RemoveType value in Enum.GetValues(typeof(RemoveType))) { if (value == RemoveType.None) continue; bool enabled; if (GetConfigValuePre(jObject, out enabled, "Remove Type Settings", value.ToString(), "Reset the time after removing a entity")) { SetConfigValuePre(jObject, enabled, "Remove Type Settings", value.ToString(), "Reset the time after removing an entity"); } } Dictionary values; if (GetConfigValuePre(jObject, out values, "Permission Settings (Just for normal type)")) { foreach (var entry in values) { object value; if (GetConfigValuePre(jObject, out value, "Permission Settings (Just for normal type)", entry.Key, "Reset the time after removing a entity")) { SetConfigValuePre(jObject, value, "Permission Settings (Just for normal type)", entry.Key, "Reset the time after removing an entity"); } } } } Config.WriteObject(jObject); //Interface.Oxide.DataFileSystem.WriteObject(Name + "_new", jObject); } } } private bool GetConfigValuePre(JObject config, out T value, params string[] path) { if (path.Length < 1) { throw new ArgumentException("path is empty"); } try { JToken jToken; if (!config.TryGetValue(path[0], out jToken)) { value = default(T); return false; } for (int i = 1; i < path.Length; i++) { var jObject = jToken.ToObject(); if (jObject == null || !jObject.TryGetValue(path[i], out jToken)) { value = default(T); return false; } } value = jToken.ToObject(); return true; } catch (Exception ex) { PrintError($"GetConfigValuePre ERROR: path: {string.Join("\\", path)}\n{ex}"); } value = default(T); return false; } private void SetConfigValuePre(JObject config, object value, params string[] path) { if (path.Length < 1) { throw new ArgumentException("path is empty"); } try { JToken jToken; if (!config.TryGetValue(path[0], out jToken)) { if (path.Length == 1) { jToken = JToken.FromObject(value); config.Add(path[0], jToken); return; } jToken = new JObject(); config.Add(path[0], jToken); } for (int i = 1; i < path.Length - 1; i++) { var jObject = jToken as JObject; if (jObject == null || !jObject.TryGetValue(path[i], out jToken)) { jToken = new JObject(); jObject?.Add(path[i], jToken); //config.Add(path[i], jToken); } } (jToken as JObject)?.Add(path[path.Length - 1], JToken.FromObject(value)); } catch (Exception ex) { PrintError($"SetConfigValuePre ERROR: value: {value} path: {string.Join("\\", path)}\n{ex}"); } } private bool GetConfigVersionPre(JObject config, out VersionNumber version) { try { JToken jToken; if (config.TryGetValue("Version", out jToken)) { version = jToken.ToObject(); return true; } } catch { // ignored } version = default(VersionNumber); return false; } #endregion Preprocess Old Config #endregion ConfigurationFile #region LanguageFile private void Print(BasePlayer player, string message) { Player.Message(player, message, configData.chatS.prefix, configData.chatS.steamIDIcon); } private void Print(ConsoleSystem.Arg arg, string message) { //SendReply(arg, message); var player = arg.Player(); if (player == null) Puts(message); else PrintToConsole(player, message); } private string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args); protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["NotAllowed"] = "You don't have '{0}' permission to use this command.", ["TargetDisabled"] = "{0}'s Remover Tool has been disabled.", ["TargetEnabled"] = "{0} is now using Remover Tool; Enabled for {1} seconds (Max Removable Objects: {2}, Remove Type: {3}).", ["ToolDisabled"] = "Remover Tool has been disabled.", ["ToolEnabled"] = "Remover Tool enabled for {0} seconds (Max Removable Objects: {1}, Remove Type: {2}).", ["Cooldown"] = "You need to wait {0} seconds before using Remover Tool again.", ["CurrentlyDisabled"] = "Remover Tool is currently disabled.", ["EntityLimit"] = "Entity limit reached, you have removed {0} entities, Remover Tool was automatically disabled.", ["MeleeToolNotHeld"] = "You need to be holding a melee tool in order to use the Remover Tool.", ["SpecificToolNotHeld"] = "You need to be holding a specific tool in order to use the Remover Tool.", ["StartRemoveAll"] = "Start running RemoveAll, please wait.", ["StartRemoveStructure"] = "Start running RemoveStructure, please wait.", ["StartRemoveExternal"] = "Start running RemoveExternal, please wait.", ["AlreadyRemoveAll"] = "There is already a RemoveAll running, please wait.", ["AlreadyRemoveStructure"] = "There is already a RemoveStructure running, please wait.", ["AlreadyRemoveExternal"] = "There is already a RemoveExternal running, please wait.", ["CompletedRemoveAll"] = "You've successfully removed {0} entities using RemoveAll.", ["CompletedRemoveStructure"] = "You've successfully removed {0} entities using RemoveStructure.", ["CompletedRemoveExternal"] = "You've successfully removed {0} entities using RemoveExternal.", ["CanRemove"] = "You can remove this entity.", ["NotEnoughCost"] = "Can't remove: You don't have enough resources.", ["EntityDisabled"] = "Can't remove: Server has disabled the entity from being removed.", ["DamagedEntity"] = "Can't remove: Server has disabled damaged objects from being removed.", ["BeBlocked"] = "Can't remove: An external plugin blocked the usage.", ["InvalidEntity"] = "Can't remove: No valid entity targeted.", ["NotFoundOrFar"] = "Can't remove: The entity is not found or too far away.", ["StorageNotEmpty"] = "Can't remove: The entity storage is not empty.", ["RaidBlocked"] = "Can't remove: Raid blocked for {0} seconds.", ["NotRemoveAccess"] = "Can't remove: You don't have any rights to remove this.", ["NotStructure"] = "Can't remove: The entity is not a structure.", ["NotExternalWall"] = "Can't remove: The entity is not an external wall.", ["HasStash"] = "Can't remove: There are stashes under the foundation.", ["EntityTimeLimit"] = "Can't remove: The entity was built more than {0} seconds ago.", //["Can'tOpenAllLocks"] = "Can't remove: There is a lock in the building that you cannot open.", ["CantPay"] = "Can't remove: Paying system crashed! Contact an administrator with the time and date to help him understand what happened.", ["Refund"] = "Refund:", ["Nothing"] = "Nothing", ["Price"] = "Price:", ["Free"] = "Free", ["TimeLeft"] = "Timeleft: {0}s\nRemoved: {1}", ["RemoverToolType"] = "Remover Tool ({0})", ["Unlimit"] = "∞", ["SyntaxError"] = "Syntax error, please type '/{0} ' to view help", ["Syntax"] = "/{0} [time (seconds)] - Enable RemoverTool ({1})", ["Syntax1"] = "/{0} [time (seconds)] - Enable RemoverTool ({1})", ["Syntax2"] = "/{0} [time (seconds)] - Enable RemoverTool ({1})", ["Syntax3"] = "/{0} [time (seconds)] - Enable RemoverTool ({1})", ["Syntax4"] = "/{0} [time (seconds)] - Enable RemoverTool ({1})", }, this); lang.RegisterMessages(new Dictionary { ["NotAllowed"] = "您没有 '{0}' 权限来使用该命令", ["TargetDisabled"] = "'{0}' 的拆除工具已禁用", ["TargetEnabled"] = "'{0}' 的拆除工具已启用 {1} 秒 (可拆除数: {2}, 拆除模式: {3}).", ["ToolDisabled"] = "您的拆除工具已禁用", ["ToolEnabled"] = "您的拆除工具已启用 {0} 秒 (可拆除数: {1}, 拆除模式: {2}).", ["Cooldown"] = "您需要等待 {0} 秒才可以再次使用拆除工具", ["CurrentlyDisabled"] = "服务器当前已禁用了拆除工具", ["EntityLimit"] = "您已经拆除了 '{0}' 个实体,拆除工具已自动禁用", ["MeleeToolNotHeld"] = "您必须拿着近战工具才可以使用拆除工具", ["SpecificToolNotHeld"] = "您必须拿着指定工具才可以使用拆除工具", ["StartRemoveAll"] = "开始运行 '所有拆除',请您等待", ["StartRemoveStructure"] = "开始运行 '建筑拆除',请您等待", ["StartRemoveExternal"] = "开始运行 '外墙拆除',请您等待", ["AlreadyRemoveAll"] = "已经有一个 '所有拆除' 正在运行,请您等待", ["AlreadyRemoveStructure"] = "已经有一个 '建筑拆除' 正在运行,请您等待", ["AlreadyRemoveExternal"] = "已经有一个 '外墙拆除' 正在运行,请您等待", ["CompletedRemoveAll"] = "您使用 '所有拆除' 成功拆除了 {0} 个实体", ["CompletedRemoveStructure"] = "您使用 '建筑拆除' 成功拆除了 {0} 个实体", ["CompletedRemoveExternal"] = "您使用 '外墙拆除' 成功拆除了 {0} 个实体", ["CanRemove"] = "您可以拆除该实体", ["NotEnoughCost"] = "无法拆除该实体: 拆除所需资源不足", ["EntityDisabled"] = "无法拆除该实体: 服务器已禁用拆除这种实体", ["DamagedEntity"] = "无法拆除该实体: 服务器已禁用拆除已损坏的实体", ["BeBlocked"] = "无法拆除该实体: 其他插件阻止您拆除该实体", ["InvalidEntity"] = "无法拆除该实体: 无效的实体", ["NotFoundOrFar"] = "无法拆除该实体: 没有找到实体或者距离太远", ["StorageNotEmpty"] = "无法拆除该实体: 该实体内含有物品", ["RaidBlocked"] = "无法拆除该实体: 拆除工具被突袭阻止了 {0} 秒", ["NotRemoveAccess"] = "无法拆除该实体: 您无权拆除该实体", ["NotStructure"] = "无法拆除该实体: 该实体不是建筑物", ["NotExternalWall"] = "无法拆除该实体: 该实体不是外高墙", ["HasStash"] = "无法拆除该实体: 地基下藏有小藏匿", ["EntityTimeLimit"] = "无法拆除该实体: 该实体的存活时间大于 {0} 秒", //["Can'tOpenAllLocks"] = "无法拆除该实体: 该建筑中有您无法打开的锁", ["CantPay"] = "无法拆除该实体: 支付失败,请联系管理员,告诉他详情", ["Refund"] = "退还:", ["Nothing"] = "没有", ["Price"] = "价格:", ["Free"] = "免费", ["TimeLeft"] = "剩余时间: {0}s\n已拆除数: {1} ", ["RemoverToolType"] = "拆除工具 ({0})", ["Unlimit"] = "∞", ["SyntaxError"] = "语法错误,输入 '/{0} ' 查看帮助", ["Syntax"] = "/{0} [time (seconds)] - 启用拆除工具 ({1})", ["Syntax1"] = "/{0} [time (seconds)] - 启用拆除工具 ({1})", ["Syntax2"] = "/{0} [time (seconds)] - 启用拆除工具 ({1})", ["Syntax3"] = "/{0} [time (seconds)] - 启用拆除工具 ({1})", ["Syntax4"] = "/{0} [time (seconds)] - 启用拆除工具 ({1})", }, this, "zh-CN"); lang.RegisterMessages(new Dictionary { ["NotAllowed"] = "У вас нет разрешения '{0}' чтобы использовать эту команду.", ["TargetDisabled"] = "{0}'s Remover Tool отключен.", ["TargetEnabled"] = "{0} теперь использует Remover Tool; Включено на {1} секунд (Макс. объектов для удаления: {2}, Тип удаления: {3}).", ["ToolDisabled"] = "Remover Tool отключен.", ["ToolEnabled"] = "Remover Tool включен на {0} секунд (Макс. объектов для удаления: {1}, Тип удаления: {2}).", ["Cooldown"] = "Необходимо подождать {0} секунд, прежде чем использовать Remover Tool снова.", ["CurrentlyDisabled"] = "Remover Tool в данный момент отключен.", ["EntityLimit"] = "Достигнут предел, удалено {0} объектов, Remover Tool автоматически отключен.", ["MeleeToolNotHeld"] = "Вы должны держать Инструмент ближнего боя, чтобы использовать инструмент для удаления.", ["SpecificToolNotHeld"] = "Вы должны держать Инструмент определенный, чтобы использовать инструмент для удаления.", ["StartRemoveAll"] = "Запускается RemoveAll, пожалуйста, подождите.", ["StartRemoveStructure"] = "Запускается RemoveStructure, пожалуйста, подождите.", ["StartRemoveExternal"] = "Запускается RemoveExternal, пожалуйста, подождите.", ["AlreadyRemoveAll"] = "RemoveAll уже выполняется, пожалуйста, подождите.", ["AlreadyRemoveStructure"] = "RemoveStructure уже выполняется, пожалуйста, подождите.", ["AlreadyRemoveExternal"] = "RemoveExternal уже выполняется, пожалуйста, подождите.", ["CompletedRemoveAll"] = "Вы успешно удалили {0} объектов используя RemoveAll.", ["CompletedRemoveStructure"] = "Вы успешно удалили {0} объектов используя RemoveStructure.", ["CompletedRemoveExternal"] = "Вы успешно удалили {0} объектов используя RemoveExternal.", ["CanRemove"] = "Вы можете удалить этот объект.", ["NotEnoughCost"] = "Нельзя удалить: У вас не достаточно ресурсов.", ["EntityDisabled"] = "Нельзя удалить: Сервер отключил возможность удаления этого объекта.", ["DamagedEntity"] = "Нельзя удалить: Сервер отключил возможность удалять повреждённые объекты.", ["BeBlocked"] = "Нельзя удалить: Внешний plugin блокирует использование.", ["InvalidEntity"] = "Нельзя удалить: Неверный объект.", ["NotFoundOrFar"] = "Нельзя удалить: Объект не найден, либо слишком далеко.", ["StorageNotEmpty"] = "Нельзя удалить: Хранилище объекта не пусто.", ["RaidBlocked"] = "Нельзя удалить: Рэйд-блок {0} секунд.", ["NotRemoveAccess"] = "Нельзя удалить: У вас нет прав удалять это.", ["NotStructure"] = "Нельзя удалить: Объект не конструкция.", ["NotExternalWall"] = "Нельзя удалить: Объект не внешняя стена.", ["HasStash"] = "Нельзя удалить: Обнаружены тайники под фундаментом.", ["EntityTimeLimit"] = "Нельзя удалить: Объект был построен более {0} секунд назад.", //["Can'tOpenAllLocks"] = "Нельзя удалить: в здании есть замок, который вы не можете открыть", ["CantPay"] = "Нельзя удалить: Система оплаты дала сбой! Свяжитесь с админом указав дату и время, чтобы помочь ему понять что случилось.", ["Refund"] = "Возврат:", ["Nothing"] = "Ничего", ["Price"] = "Цена:", ["Free"] = "Бесплатно", ["TimeLeft"] = "Осталось времени: {0}s\nУдалено: {1}", ["RemoverToolType"] = "Remover Tool ({0})", ["Unlimit"] = "∞", ["SyntaxError"] = "Синтаксическая ошибка! Пожалуйста, введите '/{0} ' для отображения помощи", ["Syntax"] = "/{0} [время (секунд)] - Включить RemoverTool ({1})", ["Syntax1"] = "/{0} [время (секунд)] - Включить RemoverTool ({1})", ["Syntax2"] = "/{0} [время (секунд)] - Включить RemoverTool ({1})", ["Syntax3"] = "/{0} [время (секунд)] - Включить RemoverTool ({1})", ["Syntax4"] = "/{0} [время (секунд)] - Включить RemoverTool ({1})", }, this, "ru"); } #endregion LanguageFile } }