using System; using System.Collections; using System.Collections.Generic; using System.Globalization; 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 Rust; using UnityEngine; using VLB; using Random = UnityEngine.Random; namespace Oxide.Plugins { [Info("Remover Tool", "Reneb/Fuji/Arainrr/Tryhard", "4.3.47", ResourceId = 651)] [Description("Building and entity removal tool")] public class RemoverTool : RustPlugin { #region Fields [PluginReference] private readonly Plugin Friends, ServerRewards, Clans, Economics, ImageLibrary, BuildingOwners, RustTranslationAPI, NoEscape; private const string ECONOMICS_KEY = "economics"; private const string SERVER_REWARDS_KEY = "serverrewards"; 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 _instance; private static BUTTON _removeButton; private static RemoveMode _removeMode; private readonly object _false = false; private bool _configChanged; private bool _removeOverride; private Coroutine _removeAllCoroutine; private Coroutine _removeStructureCoroutine; private Coroutine _removeExternalCoroutine; private Coroutine _removePlayerEntityCoroutine; private StringBuilder _debugStringBuilder; private Hash _entitySpawnedTimes; private readonly Hash _cooldownTimes = new Hash(); private enum RemoveMode { None, NoHeld, MeleeHit, SpecificTool } private enum RemoveType { None, All, Admin, Normal, External, Structure } private enum PlayerEntityRemoveType { All, Cupboard, Building } #endregion Fields #region Oxide Hooks private void Init() { _instance = this; 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); Unsubscribe(nameof(OnHammerHit)); Unsubscribe(nameof(OnEntitySpawned)); Unsubscribe(nameof(OnEntityKill)); Unsubscribe(nameof(OnPlayerAttack)); Unsubscribe(nameof(OnActiveItemChanged)); Unsubscribe(nameof(OnRaidBlock)); Unsubscribe(nameof(OnCombatBlock)); foreach (var perm in _configData.permission.Keys) { if (!permission.PermissionExists(perm, this)) { permission.RegisterPermission(perm, this); } } cmd.AddChatCommand(_configData.chat.command, this, nameof(CmdRemove)); } private void OnServerInitialized() { Initialize(); UpdateConfig(); LoadConfig(); _removeMode = RemoveMode.None; if (_configData.removerMode.noHeldMode) { _removeMode = RemoveMode.NoHeld; } if (_configData.removerMode.meleeHitMode) { _removeMode = RemoveMode.MeleeHit; } if (_configData.removerMode.specificToolMode) { _removeMode = RemoveMode.SpecificTool; } if (_removeMode == RemoveMode.MeleeHit) { BaseMelee baseMelee; ItemDefinition itemDefinition; if (string.IsNullOrEmpty(_configData.removerMode.meleeHitItemShortname) || (itemDefinition = ItemManager.FindItemDefinition(_configData.removerMode.meleeHitItemShortname)) == null || (baseMelee = itemDefinition.GetComponent()?.entityPrefab.Get()?.GetComponent()) == null) { PrintError($"{_configData.removerMode.meleeHitItemShortname} is not an item shortname for a melee tool"); _removeMode = RemoveMode.None; } else { Subscribe(baseMelee is Hammer ? nameof(OnHammerHit) : nameof(OnPlayerAttack)); } } if (_configData.noEscape.useRaidBlocker) { Subscribe(nameof(OnRaidBlock)); } if (_configData.noEscape.useCombatBlocker) { Subscribe(nameof(OnCombatBlock)); } if (_configData.global.entityTimeLimit) { _entitySpawnedTimes = new Hash(); Subscribe(nameof(OnEntitySpawned)); Subscribe(nameof(OnEntityKill)); } if (_configData.global.logToFile) { _debugStringBuilder = new StringBuilder(); } if (_removeMode == RemoveMode.MeleeHit && _configData.removerMode.meleeHitEnableInHand || _removeMode == RemoveMode.SpecificTool && _configData.removerMode.specificToolEnableInHand) { Subscribe(nameof(OnActiveItemChanged)); } if (!Enum.TryParse(_configData.global.removeButton, true, out _removeButton) || !Enum.IsDefined(typeof(BUTTON), _removeButton)) { PrintError($"{_configData.global.removeButton} is an invalid button. The remove button has been changed to 'FIRE_PRIMARY'."); _removeButton = BUTTON.FIRE_PRIMARY; _configData.global.removeButton = _removeButton.ToString(); SaveConfig(); } if (ImageLibrary != null) { foreach (var image in _configData.imageUrls) { AddImageToLibrary(image.Value, image.Key); } if (_configData.ui.showCrosshair) { AddImageToLibrary(_configData.ui.crosshairImageUrl, UINAME_CROSSHAIR); } } } private void Unload() { // if (_configChanged) // { // SaveConfig(); // } SaveDebug(); 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) { player.GetComponent()?.DisableTool(); } _configData = null; _instance = null; } private void OnServerSave() { if (_configChanged) { _configChanged = false; timer.Once(Random.Range(0f, 60f), SaveConfig); } if (_configData.global.logToFile) { timer.Once(Random.Range(0f, 60f), SaveDebug); } if (_configData.global.entityTimeLimit && _entitySpawnedTimes != null) { var currentTime = Time.realtimeSinceStartup; foreach (var entry in _entitySpawnedTimes.ToArray()) { if (currentTime - entry.Value > _configData.global.limitTime) { _entitySpawnedTimes.Remove(entry.Key); } } } } private void OnEntitySpawned(BaseEntity entity) { if (entity == null || entity.net == null) { return; } // if (!CanEntityBeSaved(entity)) return; _entitySpawnedTimes[entity.net.ID.Value] = Time.realtimeSinceStartup; } private void OnEntityKill(BaseEntity entity) { if (entity == null || entity.net == null) { return; } _entitySpawnedTimes.Remove(entity.net.ID.Value); } private object OnPlayerAttack(BasePlayer player, HitInfo info) { return 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); } } #endregion Oxide Hooks #region Initializing private readonly HashSet _constructions = new HashSet(); private readonly Dictionary _itemShortNameToItemId = new Dictionary(); private readonly Dictionary _prefabNameToStructure = new Dictionary(); private readonly Dictionary _shortPrefabNameToDeployable = new Dictionary(); 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 = Utility.GetFileNameWithoutExtension(deployablePrefab); if (!string.IsNullOrEmpty(shortPrefabName) && !_shortPrefabNameToDeployable.ContainsKey(shortPrefabName)) { _shortPrefabNameToDeployable.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) { return _configData.removeType[removeType].displayName; } private static void DropItemContainer(ItemContainer itemContainer, Vector3 position, Quaternion rotation) { itemContainer?.Drop(PREFAB_ITEM_DROP, position, rotation, 0); } private static bool IsExternalWall(StabilityEntity stabilityEntity) { return stabilityEntity.ShortPrefabName.Contains("external"); } private static bool CanEntityBeDisplayed(BaseEntity entity, BasePlayer player) { 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; } EntitySettings entitySettings; if (_configData.remove.entity.TryGetValue(entity.ShortPrefabName, out entitySettings) && entitySettings.enabled) { return true; } return false; } private static bool HasEntityEnabled(BaseEntity entity) { var buildingBlock = entity as BuildingBlock; if (buildingBlock != null) { if (buildingBlock is BoatBuildingBlock) { return false; } bool valid; if (_configData.remove.validConstruction.TryGetValue(buildingBlock.grade, out valid) && valid) { return true; } } EntitySettings entitySettings; if (_configData.remove.entity.TryGetValue(entity.ShortPrefabName, out entitySettings) && entitySettings.enabled) { return true; } return false; } private static bool IsRemovableEntity(BaseEntity entity) { if (_instance._shortPrefabNameToDeployable.ContainsKey(entity.ShortPrefabName) || _instance._prefabNameToStructure.ContainsKey(entity.PrefabName) || _configData.remove.entity.ContainsKey(entity.ShortPrefabName)) { var baseCombatEntity = entity as BaseCombatEntity; if (baseCombatEntity != null) { if (baseCombatEntity.IsDead()) { return false; } if (baseCombatEntity.pickup.itemTarget != null) { return true; } } return true; } return false; } private static string GetEntityImage(string name) { if (_instance.ImageLibrary == null) { return null; } if (_configData.imageUrls.ContainsKey(name)) { return GetImageFromLibrary(name); } if (_instance._itemShortNameToItemId.ContainsKey(name)) { return GetImageFromLibrary(name); } return null; } private static string GetItemImage(string shortname) { if (_instance.ImageLibrary == null) { return null; } switch (shortname.ToLower()) { case ECONOMICS_KEY: return GetImageFromLibrary(ECONOMICS_KEY); case SERVER_REWARDS_KEY: return GetImageFromLibrary(SERVER_REWARDS_KEY); } return GetEntityImage(shortname); } private static void TryFindEntityName(BasePlayer player, BaseEntity entity, out string displayName, out string imageName) { var target = entity as BasePlayer; if (target != null) { imageName = target.userID.IsSteamId() ? target.UserIDString : target.ShortPrefabName; displayName = $"{target.displayName} ({target.ShortPrefabName})"; return; } EntitySettings entitySettings; if (_configData.remove.entity.TryGetValue(entity.ShortPrefabName, out entitySettings)) { imageName = entity.ShortPrefabName; displayName = _instance.GetDeployableDisplayName(player, entity.ShortPrefabName, entitySettings.displayName); return; } string structureName; if (_instance._prefabNameToStructure.TryGetValue(entity.PrefabName, out structureName)) { BuildingBlocksSettings buildingBlockSettings; if (_configData.remove.buildingBlock.TryGetValue(structureName, out buildingBlockSettings)) { imageName = structureName; displayName = _instance.GetConstructionDisplayName(player, entity.PrefabName, buildingBlockSettings.displayName); return; } } imageName = entity.ShortPrefabName; displayName = entity.ShortPrefabName; } private static string GetDisplayNameByCurrencyName(string language, string currencyName, long skinId) { var itemDefinition = ItemManager.FindItemDefinition(currencyName); if (itemDefinition != null) { var translationKey = $"{itemDefinition.shortname}_{skinId}"; var translationValue = GetCurrencyDisplayName(translationKey, itemDefinition.displayName.english, true); if (skinId <= 0 || string.IsNullOrEmpty(translationValue)) { var displayName = _instance.GetItemDisplayName(language, itemDefinition.shortname); if (!string.IsNullOrEmpty(displayName)) { return displayName; } return itemDefinition.displayName.english; } return GetCurrencyDisplayName(translationKey, itemDefinition.displayName.english); } return GetCurrencyDisplayName(currencyName, currencyName); } private static string GetCurrencyDisplayName(string currencyName, string defaultName = null, bool readOnly = false) { string displayName; if (_configData.remove.displayNames.TryGetValue(currencyName, out displayName)) { return displayName; } if (!readOnly) { _configData.remove.displayNames.Add(currencyName, defaultName); _instance._configChanged = true; } return defaultName; } private static PermissionSettings GetPermissionSettings(BasePlayer player) { var priority = 0; PermissionSettings permissionSettings = null; foreach (var entry in _configData.permission) { if (entry.Value.priority >= priority && _instance.permission.UserHasPermission(player.UserIDString, entry.Key)) { priority = entry.Value.priority; permissionSettings = entry.Value; } } return permissionSettings ?? new PermissionSettings(); } 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) { return (bool)_instance.ImageLibrary.Call("AddImage", url, shortname.ToLower(), skin); } private static string GetImageFromLibrary(string shortname, ulong skin = 0, bool returnUrl = false) { return string.IsNullOrEmpty(shortname) ? null : (string)_instance.ImageLibrary.Call("GetImage", shortname.ToLower(), skin, returnUrl); } #endregion Methods #region NoEscape private void OnRaidBlock(BasePlayer player) { if (_configData.noEscape.useRaidBlocker) { // Print(player, Lang("RaidBlocked", player.UserIDString)); player.GetComponent()?.DisableTool(false); } } private void OnCombatBlock(BasePlayer player) { if (_configData.noEscape.useCombatBlocker) { // Print(player, Lang("CombatBlocked", player.UserIDString)); player.GetComponent()?.DisableTool(false); } } private bool IsPlayerBlocked(BasePlayer player, out string reason) { if (NoEscape != null) { if (_configData.noEscape.useRaidBlocker && IsRaidBlocked(player.UserIDString)) { reason = Lang("RaidBlocked", player.UserIDString); return true; } if (_configData.noEscape.useCombatBlocker && IsCombatBlocked(player.UserIDString)) { reason = Lang("CombatBlocked", player.UserIDString); return true; } } reason = null; return false; } private bool IsRaidBlocked(string playerID) { return (bool)NoEscape.Call("IsRaidBlocked", playerID); } private bool IsCombatBlocked(string playerID) { return (bool)NoEscape.Call("IsCombatBlocked", playerID); } #endregion NoEscape #region UI private static 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); } 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); } 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 { Color = color, Png = image }, new CuiRectTransformComponent { AnchorMin = anchorMin, AnchorMax = anchorMax } } }); } public static void CreateImage(ref CuiElementContainer container, string panelName, int itemId, ulong skinId, string anchorMin, string anchorMax) { container.Add(new CuiPanel { Image = { ItemId = itemId, SkinId = skinId }, RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax }, CursorEnabled = false }, panelName); } } [Flags] private enum UiEntry { None = 0, Entity = 1, Price = 1 << 1, Refund = 1 << 2, Auth = 1 << 3 } 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 (_instance.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.ui.crosshairAnchorMin, _configData.ui.crosshairAnchorMax, _configData.ui.crosshairOffsetMin, _configData.ui.crosshairOffsetMax); UI.CreateImage(ref container, UINAME_CROSSHAIR, image, "0 0", "1 1", _configData.ui.crosshairColor); CuiHelper.DestroyUi(player, UINAME_CROSSHAIR); CuiHelper.AddUi(player, container); } private static void CreateMainUI(BasePlayer player, RemoveType removeType) { var container = UI.CreateElementContainer("Hud", UINAME_MAIN, _configData.ui.removerToolBackgroundColor, _configData.ui.removerToolAnchorMin, _configData.ui.removerToolAnchorMax, _configData.ui.removerToolOffsetMin, _configData.ui.removerToolOffsetMax); UI.CreatePanel(ref container, UINAME_MAIN, _configData.ui.removeBackgroundColor, _configData.ui.removeAnchorMin, _configData.ui.removeAnchorMax); UI.CreateLabel(ref container, UINAME_MAIN, _configData.ui.removeTextColor, _instance.Lang("RemoverToolType", player.UserIDString, GetRemoveTypeName(removeType)), _configData.ui.removeTextSize, _configData.ui.removeTextAnchorMin, _configData.ui.removeTextAnchorMax, TextAnchor.MiddleLeft); 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.ui.timeLeftBackgroundColor, _configData.ui.timeLeftAnchorMin, _configData.ui.timeLeftAnchorMax); UI.CreateLabel(ref container, UINAME_TIMELEFT, _configData.ui.timeLeftTextColor, _instance.Lang("TimeLeft", player.UserIDString, timeLeft, removeType == RemoveType.Normal || removeType == RemoveType.Admin ? maxRemovable == 0 ? $"{currentRemoved} / {_instance.Lang("Unlimit", player.UserIDString)}" : $"{currentRemoved} / {maxRemovable}" : currentRemoved.ToString()), _configData.ui.timeLeftTextSize, _configData.ui.timeLeftTextAnchorMin, _configData.ui.timeLeftTextAnchorMax, TextAnchor.MiddleLeft); CuiHelper.DestroyUi(player, UINAME_TIMELEFT); CuiHelper.AddUi(player, container); } private static void UpdateEntityUI(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info) { var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_ENTITY, _configData.ui.entityBackgroundColor, _configData.ui.entityAnchorMin, _configData.ui.entityAnchorMax); string displayName, imageName; TryFindEntityName(player, targetEntity, out displayName, out imageName); if (info.HasValue && !string.IsNullOrEmpty(info.Value.DisplayName.Value)) { displayName = info.Value.DisplayName.Value; } UI.CreateLabel(ref container, UINAME_ENTITY, _configData.ui.entityTextColor, displayName, _configData.ui.entityTextSize, _configData.ui.entityTextAnchorMin, _configData.ui.entityTextAnchorMax, TextAnchor.MiddleLeft); if (_configData.ui.entityImageEnabled) { var imageAnchorMin = _configData.ui.entityImageAnchorMin; var imageAnchorMax = _configData.ui.entityImageAnchorMax; if (info.HasValue && !string.IsNullOrEmpty(info.Value.ImageId.Value)) { var image = info.Value.ImageId.Value; if (!string.IsNullOrEmpty(image)) { UI.CreateImage(ref container, UINAME_ENTITY, image, imageAnchorMin, imageAnchorMax); } } else if (!string.IsNullOrEmpty(imageName)) { string shortname; int itemId; if (_instance._shortPrefabNameToDeployable.TryGetValue(imageName, out shortname) && _instance._itemShortNameToItemId.TryGetValue(shortname, out itemId)) { UI.CreateImage(ref container, UINAME_ENTITY, itemId, targetEntity.skinID, imageAnchorMin, imageAnchorMax); } else { var image = GetEntityImage(imageName); if (!string.IsNullOrEmpty(image)) { UI.CreateImage(ref container, UINAME_ENTITY, image, imageAnchorMin, imageAnchorMax); } } } } CuiHelper.DestroyUi(player, UINAME_ENTITY); CuiHelper.AddUi(player, container); } private static void UpdatePriceUI(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info, bool usePrice) { Dictionary price = null; if (usePrice) { price = _instance.GetPrice(targetEntity, info); } var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_PRICE, _configData.ui.priceBackgroundColor, _configData.ui.priceAnchorMin, _configData.ui.priceAnchorMax); UI.CreateLabel(ref container, UINAME_PRICE, _configData.ui.priceTextColor, _instance.Lang("Price", player.UserIDString), _configData.ui.priceTextSize, _configData.ui.priceTextAnchorMin, _configData.ui.priceTextAnchorMax, TextAnchor.MiddleLeft); if (price == null || price.Count == 0) { UI.CreateLabel(ref container, UINAME_PRICE, _configData.ui.price2TextColor, _instance.Lang("Free", player.UserIDString), _configData.ui.price2TextSize, _configData.ui.price2TextAnchorMin, _configData.ui.price2TextAnchorMax, TextAnchor.MiddleLeft); } else { var anchorMin = _configData.ui.Price2TextAnchorMin; var anchorMax = _configData.ui.Price2TextAnchorMax; var x = (anchorMax.y - anchorMin.y) / price.Count; var textSize = _configData.ui.price2TextSize - price.Count; var language = _instance.lang.GetLanguage(player.UserIDString); var i = 0; foreach (var entry in price) { var externalItemInfo = info?.Price[entry.Key]; var displayText = !externalItemInfo.HasValue || string.IsNullOrEmpty(externalItemInfo.Value.DisplayName.Value) ? $"{GetDisplayNameByCurrencyName(language, entry.Key, entry.Value.SkinId)} x{entry.Value.Amount}" : $"{externalItemInfo.Value.DisplayName.Value} x{externalItemInfo.Value.Amount.Value}"; UI.CreateLabel(ref container, UINAME_PRICE, _configData.ui.price2TextColor, displayText, textSize, $"{anchorMin.x} {anchorMin.y + i * x}", $"{anchorMax.x} {anchorMin.y + (i + 1) * x}", TextAnchor.MiddleLeft); if (_configData.ui.imageEnabled) { var imageAnchorMin = $"{anchorMax.x - _configData.ui.rightDistance - x * _configData.ui.imageScale} {anchorMin.y + i * x}"; var imageAnchorMax = $"{anchorMax.x - _configData.ui.rightDistance} {anchorMin.y + (i + 1) * x}"; if (externalItemInfo.HasValue && !string.IsNullOrEmpty(externalItemInfo.Value.ImageId.Value)) { var image = externalItemInfo.Value.ImageId.Value; if (!string.IsNullOrEmpty(image)) { UI.CreateImage(ref container, UINAME_PRICE, image, imageAnchorMin, imageAnchorMax); } } else { int itemId; if (_instance._itemShortNameToItemId.TryGetValue(entry.Key, out itemId)) { UI.CreateImage(ref container, UINAME_PRICE, itemId, entry.Value.SkinId >= 0 ? (ulong)entry.Value.SkinId : 0, imageAnchorMin, imageAnchorMax); } else { var image = GetItemImage(entry.Key); if (!string.IsNullOrEmpty(image)) { UI.CreateImage(ref container, UINAME_PRICE, image, imageAnchorMin, imageAnchorMax); } } } } i++; } } CuiHelper.DestroyUi(player, UINAME_PRICE); CuiHelper.AddUi(player, container); } private static void UpdateRefundUI(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info, bool useRefund) { Dictionary refund = null; if (useRefund) { refund = _instance.GetRefund(targetEntity, info); } var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_REFUND, _configData.ui.refundBackgroundColor, _configData.ui.refundAnchorMin, _configData.ui.refundAnchorMax); UI.CreateLabel(ref container, UINAME_REFUND, _configData.ui.refundTextColor, _instance.Lang("Refund", player.UserIDString), _configData.ui.refundTextSize, _configData.ui.refundTextAnchorMin, _configData.ui.refundTextAnchorMax, TextAnchor.MiddleLeft); if (refund == null || refund.Count == 0) { UI.CreateLabel(ref container, UINAME_REFUND, _configData.ui.refund2TextColor, _instance.Lang("Nothing", player.UserIDString), _configData.ui.refund2TextSize, _configData.ui.refund2TextAnchorMin, _configData.ui.refund2TextAnchorMax, TextAnchor.MiddleLeft); } else { var anchorMin = _configData.ui.Refund2TextAnchorMin; var anchorMax = _configData.ui.Refund2TextAnchorMax; var x = (anchorMax.y - anchorMin.y) / refund.Count; var textSize = _configData.ui.refund2TextSize - refund.Count; var language = _instance.lang.GetLanguage(player.UserIDString); var i = 0; foreach (var entry in refund) { var externalItemInfo = info?.Refund[entry.Key]; var displayText = !externalItemInfo.HasValue || string.IsNullOrEmpty(externalItemInfo.Value.DisplayName.Value) ? $"{GetDisplayNameByCurrencyName(language, entry.Key, entry.Value.SkinId)} x{entry.Value.Amount}" : $"{externalItemInfo.Value.DisplayName.Value} x{externalItemInfo.Value.Amount.Value}"; UI.CreateLabel(ref container, UINAME_REFUND, _configData.ui.refund2TextColor, displayText, textSize, $"{anchorMin.x} {anchorMin.y + i * x}", $"{anchorMax.x} {anchorMin.y + (i + 1) * x}", TextAnchor.MiddleLeft); if (_configData.ui.imageEnabled) { var imageAnchorMin = $"{anchorMax.x - _configData.ui.rightDistance - x * _configData.ui.imageScale} {anchorMin.y + i * x}"; var imageAnchorMax = $"{anchorMax.x - _configData.ui.rightDistance} {anchorMin.y + (i + 1) * x}"; if (externalItemInfo.HasValue && !string.IsNullOrEmpty(externalItemInfo.Value.ImageId.Value)) { var image = externalItemInfo.Value.ImageId.Value; if (!string.IsNullOrEmpty(image)) { UI.CreateImage(ref container, UINAME_REFUND, image, imageAnchorMin, imageAnchorMax); } } else { int itemId; if (_instance._itemShortNameToItemId.TryGetValue(entry.Key, out itemId)) { UI.CreateImage(ref container, UINAME_REFUND, itemId, entry.Value.SkinId >= 0 ? (ulong)entry.Value.SkinId : 0, imageAnchorMin, imageAnchorMax); } else { var image = GetItemImage(entry.Key); if (!string.IsNullOrEmpty(image)) { UI.CreateImage(ref container, UINAME_REFUND, image, imageAnchorMin, imageAnchorMax); } } } } i++; } } CuiHelper.DestroyUi(player, UINAME_REFUND); CuiHelper.AddUi(player, container); } private static void UpdateAuthorizationUI(BasePlayer player, RemoveType removeType, BaseEntity targetEntity, RemovableEntityInfo? info, bool shouldPay) { string reason; var color = _instance.CanRemoveEntity(player, removeType, targetEntity, info, shouldPay, out reason) ? _configData.ui.allowedBackgroundColor : _configData.ui.refusedBackgroundColor; var container = UI.CreateElementContainer(UINAME_MAIN, UINAME_AUTH, color, _configData.ui.authorizationsAnchorMin, _configData.ui.authorizationsAnchorMax); UI.CreateLabel(ref container, UINAME_AUTH, _configData.ui.authorizationsTextColor, reason, _configData.ui.authorizationsTextSize, _configData.ui.authorizationsTextAnchorMin, _configData.ui.authorizationsTextAnchorMax, TextAnchor.MiddleLeft); CuiHelper.DestroyUi(player, UINAME_AUTH); CuiHelper.AddUi(player, container); } private static void DestroyAllUI(BasePlayer player) { CuiHelper.DestroyUi(player, UINAME_CROSSHAIR); CuiHelper.DestroyUi(player, UINAME_MAIN); } private static void DestroyUiEntry(BasePlayer player, UiEntry uiEntry) { switch (uiEntry) { case UiEntry.Entity: CuiHelper.DestroyUi(player, UINAME_ENTITY); return; case UiEntry.Price: CuiHelper.DestroyUi(player, UINAME_PRICE); return; case UiEntry.Refund: CuiHelper.DestroyUi(player, UINAME_REFUND); return; case UiEntry.Auth: CuiHelper.DestroyUi(player, UINAME_AUTH); return; } } #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.removerMode.specificToolShortName) { if (_configData.removerMode.specificToolSkin < 0) { return true; } return heldItem.skin == (ulong)_configData.removerMode.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.removerMode.meleeHitItemShortname) { if (_configData.removerMode.meleeHitModeSkin < 0) { return true; } return heldItem.skin == (ulong)_configData.removerMode.meleeHitModeSkin; } return false; } #endregion Tool Helpers private class ToolRemover : FacepunchBehaviour { private const float MinInterval = 0.2f; public int CurrentRemoved { get; set; } public BaseEntity HitEntity { get; set; } public bool CanOverride { get; private set; } public BasePlayer Player { get; private set; } public RemoveType RemoveType { get; private set; } private bool _resetTime; private bool _shouldPay; private bool _shouldRefund; private int _removeTime; private int _maxRemovable; private float _distance; private float _removeInterval; private int _timeLeft; private float _lastRemove; private ItemId _currentItemId; private bool _disableInHand; private Item _lastHeldItem; private BaseEntity _targetEntity; private UiEntry _activeUiEntries; private void Awake() { Player = GetComponent(); _currentItemId = Player.svActiveItemID; _disableInHand = _removeMode == RemoveMode.MeleeHit && _configData.removerMode.meleeHitDisableInHand || _removeMode == RemoveMode.SpecificTool && _configData.removerMode.specificToolDisableInHand; if (_disableInHand) { _lastHeldItem = Player.GetActiveItem(); } if (_removeMode == RemoveMode.NoHeld) { UnEquip(); } } public void Init(RemoveType removeType, int removeTime, int maxRemovable, float distance, float removeInterval, bool shouldPay, bool shouldRefund, bool resetTime, bool canOverride) { RemoveType = removeType; CanOverride = canOverride; _distance = distance; _resetTime = resetTime; _removeTime = _timeLeft = removeTime; _removeInterval = Mathf.Max(MinInterval, removeInterval); if (RemoveType == RemoveType.Normal) { _maxRemovable = maxRemovable; _shouldPay = shouldPay && _configData.remove.priceEnabled; _shouldRefund = shouldRefund && _configData.remove.refundEnabled; _instance.PrintDebug($"{Player.displayName}({Player.userID}) have Enabled the remover tool."); Interface.CallHook("OnRemoverToolActivated", Player); } else { _maxRemovable = CurrentRemoved = 0; _shouldPay = _shouldRefund = false; } DestroyAllUI(Player); if (_configData.ui.showCrosshair) { CreateCrosshairUI(Player); } if (_configData.ui.enabled) { CreateMainUI(Player, RemoveType); } CancelInvoke(RemoveUpdate); InvokeRepeating(RemoveUpdate, 0f, 1f); } private void RemoveUpdate() { if (_configData.ui.enabled) { _targetEntity = GetTargetEntity(); UpdateTimeLeftUI(Player, RemoveType, _timeLeft, CurrentRemoved, _maxRemovable); var info = RemoveType == RemoveType.Normal ? GetRemovableEntityInfo(_targetEntity, Player) : null; var canShow = (info.HasValue || _targetEntity != null) && CanEntityBeDisplayed(_targetEntity, Player); if (HandleUiEntry(UiEntry.Entity, canShow)) { UpdateEntityUI(Player, _targetEntity, info); } if (RemoveType == RemoveType.Normal) { if (_configData.ui.authorizationEnabled) { if (HandleUiEntry(UiEntry.Auth, canShow)) { UpdateAuthorizationUI(Player, RemoveType, _targetEntity, info, _shouldPay); } } if (_configData.ui.priceEnabled || _configData.ui.refundEnabled) { canShow = canShow && (info.HasValue || HasEntityEnabled(_targetEntity)); if (_configData.ui.priceEnabled) { if (HandleUiEntry(UiEntry.Price, canShow)) { UpdatePriceUI(Player, _targetEntity, info, _shouldPay); } } if (_configData.ui.refundEnabled) { if (HandleUiEntry(UiEntry.Refund, canShow)) { UpdateRefundUI(Player, _targetEntity, info, _shouldRefund); } } } } } if (_timeLeft-- <= 0) { DisableTool(); } } private BaseEntity GetTargetEntity() { BaseEntity target = null; List hitInfos = Pool.Get>(); GamePhysics.TraceAll(Player.eyes.HeadRay(), 0f, hitInfos, _distance, LAYER_TARGET); foreach (var hitInfo in hitInfos) { var hitEntity = hitInfo.GetEntity(); if (hitEntity != null) { if (target == null) { target = hitEntity; } else if (hitEntity.GetParentEntity() == target) { target = hitEntity; break; } } } Pool.FreeUnmanaged(ref hitInfos); return target; // RaycastHit hitInfo; // if (Physics.Raycast(Player.eyes.HeadRay(), out hitInfo, _distance, LAYER_TARGET)) // { // return hitInfo.GetEntity(); // } // return null; } private void Update() { if (Player == null || !Player.IsConnected || !Player.CanInteract()) { DisableTool(); return; } if (Player.svActiveItemID != _currentItemId) { if (_disableInHand) { 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.IsValid) { if (_configData.removerMode.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)) { //rt.Print(player,rt.Lang("UsageOfRemove",player.UserIDString)); return; } _targetEntity = GetTargetEntity(); } if (_instance.TryRemove(Player, _targetEntity, RemoveType, _shouldPay, _shouldRefund)) { if (_resetTime) { _timeLeft = _removeTime; } if (RemoveType == RemoveType.Normal || RemoveType == RemoveType.Admin) { CurrentRemoved++; } if (_configData.global.startCooldownOnRemoved && RemoveType == RemoveType.Normal) { _instance._cooldownTimes[Player.userID] = Time.realtimeSinceStartup; } } _lastRemove = Time.realtimeSinceStartup; } if (RemoveType == RemoveType.Normal && _maxRemovable > 0 && CurrentRemoved >= _maxRemovable) { _instance.Print(Player, _instance.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); } } private bool HandleUiEntry(UiEntry uiEntry, bool canShow) { if (canShow) { _activeUiEntries |= uiEntry; return true; } if (_activeUiEntries.HasFlag(uiEntry)) { _activeUiEntries &= ~uiEntry; DestroyUiEntry(Player, uiEntry); } return false; } public void DisableTool(bool showMessage = true) { if (showMessage) { if (_instance != null && Player != null && Player.IsConnected) { if (_configData != null && _configData.chat.showMessageWhenEnabledOrDisabled) { _instance.Print(Player, _instance.Lang("ToolDisabled", Player.UserIDString)); } } } if (RemoveType == RemoveType.Normal) { if (_instance != null && Player != null) { _instance.PrintDebug($"{Player.displayName}({Player.userID}) have Disabled the remover tool."); } Interface.CallHook("OnRemoverToolDeactivated", Player); } DestroyAllUI(Player); Destroy(this); } private void OnDestroy() { if (_instance != null && RemoveType == RemoveType.Normal) { if (_configData != null && !_configData.global.startCooldownOnRemoved) { _instance._cooldownTimes[Player.userID] = Time.realtimeSinceStartup; } } } } #endregion ToolRemover Component #region TryRemove private bool TryRemove(BasePlayer player, BaseEntity targetEntity, RemoveType removeType, bool shouldPay, bool shouldRefund) { if (targetEntity == null) { Print(player, Lang("NotFoundOrFar", player.UserIDString)); return false; } if (targetEntity.IsDestroyed) { Print(player, Lang("InvalidEntity", player.UserIDString)); return false; } if (removeType != RemoveType.Normal) { var result = Interface.CallHook("CanAdminRemove", player, targetEntity, removeType.ToString()); if (result != null) { Print(player, result is string ? (string)result : Lang("BeBlocked", player.UserIDString)); return false; } switch (removeType) { case 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.removeType[RemoveType.Admin].gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None); return true; } 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 info = GetRemovableEntityInfo(targetEntity, player); string reason; if (!CanRemoveEntity(player, removeType, targetEntity, info, shouldPay, out reason)) { Print(player, reason); return false; } DropContainerEntity(targetEntity); if (shouldPay) { var flag = TryPay(player, targetEntity, info); if (!flag) { Print(player, Lang("CantPay", player.UserIDString)); return false; } } if (shouldRefund) { GiveRefund(player, targetEntity, info); } DoNormalRemove(player, targetEntity, _configData.removeType[RemoveType.Normal].gibs); return true; } private bool CanRemoveEntity(BasePlayer player, RemoveType removeType, BaseEntity targetEntity, RemovableEntityInfo? info, bool shouldPay, out string reason) { if (removeType != RemoveType.Normal) { reason = null; return true; } if (targetEntity == null || !CanEntityBeDisplayed(targetEntity, player)) { reason = Lang("NotFoundOrFar", player.UserIDString); return false; } if (targetEntity.IsDestroyed) { reason = Lang("InvalidEntity", player.UserIDString); return false; } if (!info.HasValue) { if (!IsRemovableEntity(targetEntity)) { reason = Lang("InvalidEntity", player.UserIDString); return false; } 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.damagedEntity.enabled && IsDamagedEntity(targetEntity)) { reason = Lang("DamagedEntity", player.UserIDString); return false; } if (IsPlayerBlocked(player, out reason)) { return false; } if (_configData.global.entityTimeLimit && IsEntityTimeLimit(targetEntity)) { reason = Lang("EntityTimeLimit", player.UserIDString, _configData.global.limitTime); return false; } if (!_configData.container.removeNotEmptyStorage) { var storageContainer = targetEntity as StorageContainer; if (storageContainer != null && storageContainer.inventory?.itemList?.Count > 0) { reason = Lang("StorageNotEmpty", player.UserIDString); return false; } } if (!_configData.container.removeNotEmptyIoEntity) { var containerIOEntity = targetEntity as ContainerIOEntity; if (containerIOEntity != null && containerIOEntity.inventory?.itemList?.Count > 0) { reason = Lang("StorageNotEmpty", player.UserIDString); return false; } } if (shouldPay && !CanPay(player, targetEntity, info)) { reason = Lang("NotEnoughCost", player.UserIDString); return false; } if (!HasAccess(player, targetEntity)) { reason = Lang("NotRemoveAccess", player.UserIDString); return false; } reason = Lang("CanRemove", player.UserIDString); return true; } private bool HasAccess(BasePlayer player, BaseEntity targetEntity) { if (_configData.global.useBuildingOwners && BuildingOwners != null) { var buildingBlock = targetEntity as BuildingBlock; if (buildingBlock != null) { var result = BuildingOwners?.Call("FindBlockData", buildingBlock) as string; if (result != null) { var ownerID = ulong.Parse(result); if (AreFriends(ownerID, player.userID)) { return true; } } } } //var 1 = configData.globalS.excludeTwigs && (targetEntity as BuildingBlock)?.grade == BuildingGrade.Enum.Twigs; if (_configData.global.useEntityOwners) { if (AreFriends(targetEntity.OwnerID, player.userID)) { if (!_configData.global.useToolCupboards) { return true; } if (HasTotalAccess(player, targetEntity)) { return true; } } return false; } if (_configData.global.useToolCupboards) { if (HasTotalAccess(player, targetEntity)) { return true; } } return false; } private static bool HasTotalAccess(BasePlayer player, BaseEntity targetEntity) { if (player.IsBuildingBlocked(targetEntity.WorldSpaceBounds())) { return false; } if (_configData.global.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.Get>(); 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.FreeUnmanaged(ref decayEntities); return false; } } } Pool.FreeUnmanaged(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; } // Make no sound during the check 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 IsDamagedEntity(BaseEntity entity) { var baseCombatEntity = entity as BaseCombatEntity; if (baseCombatEntity == null || !baseCombatEntity.repair.enabled) { return false; } if (_configData.damagedEntity.excludeBuildingBlocks && (baseCombatEntity is BuildingBlock || baseCombatEntity is SimpleBuildingBlock)) { return false; } if (_configData.damagedEntity.excludeQuarries && !(baseCombatEntity is BuildingBlock)) { // Quarries if (baseCombatEntity.repair.itemTarget == null || baseCombatEntity.repair.itemTarget.Blueprint == null) { return false; } } if (baseCombatEntity.healthFraction * 100f >= _configData.damagedEntity.percentage) { return false; } return true; } private static bool IsEntityTimeLimit(BaseEntity entity) { if (entity.net == null) { return true; } float spawnedTime; if (_instance._entitySpawnedTimes.TryGetValue(entity.net.ID.Value, out spawnedTime)) { return Time.realtimeSinceStartup - spawnedTime > _configData.global.limitTime; } return true; } private static void DropContainerEntity(BaseEntity targetEntity) { var itemContainerEntity = targetEntity as IItemContainerEntity; if (itemContainerEntity != null && itemContainerEntity.inventory?.itemList?.Count > 0) { bool dropContainer = false, dropItems = false; var storageContainer = targetEntity as StorageContainer; if (storageContainer != null) { dropContainer = _configData.container.dropContainerStorage; dropItems = _configData.container.dropItemsStorage; } else { var containerIoEntity = targetEntity as ContainerIOEntity; if (containerIoEntity != null) { dropContainer = _configData.container.dropContainerIoEntity; dropItems = _configData.container.dropItemsIoEntity; } } if (dropContainer || dropItems) { if (Interface.CallHook("OnDropContainerEntity", targetEntity) == null) { if (dropContainer) { DropItemContainer(itemContainerEntity.inventory, itemContainerEntity.GetDropPosition(), itemContainerEntity.Transform.rotation); } else if (dropItems) { itemContainerEntity.DropItems(); } } } } } #region AreFriends private bool AreFriends(ulong playerID, ulong friendID) { if (!playerID.IsSteamId()) { return false; } if (playerID == friendID) { return true; } if (_configData.global.useTeams && SameTeam(playerID, friendID)) { return true; } if (_configData.global.useFriends && HasFriend(playerID, friendID)) { return true; } if (_configData.global.useClans && SameClan(playerID, friendID)) { return true; } return false; } private static bool SameTeam(ulong playerID, ulong friendID) { if (!RelationshipManager.TeamsEnabled()) { return false; } var playerTeam = RelationshipManager.ServerInstance.FindPlayersTeam(playerID); if (playerTeam == null) { return false; } var friendTeam = RelationshipManager.ServerInstance.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 Pay private Dictionary GetPrice(BaseEntity targetEntity, RemovableEntityInfo? info) { if (info.HasValue) { return info.Value.Price.ValueName2Currency; } var buildingBlock = targetEntity as BuildingBlock; if (buildingBlock != null) { var entityName = _prefabNameToStructure[buildingBlock.PrefabName]; BuildingBlocksSettings buildingBlockSettings; if (_configData.remove.buildingBlock.TryGetValue(entityName, out buildingBlockSettings)) { BuildingGradeSettings buildingGradeSettings; if (buildingBlockSettings.buildingGrade.TryGetValue(buildingBlock.grade, out buildingGradeSettings)) { if (buildingGradeSettings.priceDict != null) { return buildingGradeSettings.priceDict; } if (buildingGradeSettings.pricePercentage > 0f) { var currentGrade = buildingBlock.currentGrade; if (currentGrade != null) { var price = new Dictionary(); var costToBuild = buildingBlock.blockDefinition.GetGrade(buildingBlock.grade, buildingBlock.skinID).CostToBuild(); foreach (var itemAmount in costToBuild) { var amount = Mathf.RoundToInt(itemAmount.amount * buildingGradeSettings.pricePercentage / 100); if (amount <= 0) { continue; } price.Add(itemAmount.itemDef.shortname, new CurrencyInfo(amount)); } return price; } } else if (buildingGradeSettings.pricePercentage < 0f) { var currentGrade = buildingBlock.currentGrade; if (currentGrade != null) { return buildingBlock.blockDefinition.GetGrade(buildingBlock.grade, buildingBlock.skinID).CostToBuild().ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(Mathf.RoundToInt(y.amount))); } } } } } else { EntitySettings entitySettings; if (_configData.remove.entity.TryGetValue(targetEntity.ShortPrefabName, out entitySettings)) { return entitySettings.priceDict; } } return null; } private bool TryPay(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info) { var price = GetPrice(targetEntity, info); if (price == null || price.Count == 0) { return true; } var collect = Pool.Get>(); try { foreach (var entry in price) { if (entry.Value.Amount <= 0) { continue; } int itemId; if (_itemShortNameToItemId.TryGetValue(entry.Key, out itemId)) { var take = TakeInventory(player, itemId, entry.Value, collect); player.Command("note.inv", itemId, -take); } else if (!CheckOrPay(targetEntity, player, entry.Key, entry.Value, false)) { return false; } } } catch (Exception e) { PrintError($"{player} couldn't pay to remove entity. Error: {e}"); return false; } finally { foreach (var item in collect) { item.Remove(); } Pool.FreeUnmanaged(ref collect); } return true; } private bool CanPay(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info) { var price = GetPrice(targetEntity, info); if (price == null || price.Count == 0) { return true; } foreach (var entry in price) { if (entry.Value.Amount <= 0) { continue; } int itemId; if (_itemShortNameToItemId.TryGetValue(entry.Key, out itemId)) { var amount = GetInventoryAmount(player, itemId, entry.Value); if (amount < entry.Value.Amount) { return false; } } else if (!CheckOrPay(targetEntity, player, entry.Key, entry.Value, true)) { return false; } } return true; } private bool CheckOrPay(BaseEntity targetEntity, BasePlayer player, string itemName, CurrencyInfo currencyInfo, bool check) { if (currencyInfo.Amount <= 0) { return true; } switch (itemName.ToLower()) { case ECONOMICS_KEY: if (Economics == null) { return false; } if (check) { var balance = Economics.Call("Balance", player.userID); if (balance == null) { return false; } if ((double)balance < currencyInfo.Amount) { return false; } } else { var withdraw = Economics.Call("Withdraw", player.userID, (double)currencyInfo.Amount); if (withdraw == null || !(bool)withdraw) { return false; } } return true; case SERVER_REWARDS_KEY: if (ServerRewards == null) { return false; } if (check) { var points = ServerRewards.Call("CheckPoints", player.userID); if (points == null) { return false; } if ((int)points < currencyInfo.Amount) { return false; } } else { var takePoints = ServerRewards.Call("TakePoints", player.userID, currencyInfo.Amount); if (takePoints == null || !(bool)takePoints) { return false; } } return true; default: { var result = Interface.CallHook("OnRemovableEntityCheckOrPay", targetEntity, player, itemName, currencyInfo.Amount, currencyInfo.SkinId, check); if (result is bool) { return (bool)result; } } return true; } } private static int GetInventoryAmount(BasePlayer player, int itemId, CurrencyInfo currencyInfo) { var count = 0; if (itemId == 0) { return count; } var list = Pool.Get>(); player.inventory.GetAllItems(list); foreach (var item in list) { if (item.info.itemid == itemId && !item.IsBusy() && (currencyInfo.SkinId < 0 || item.skin == (ulong)currencyInfo.SkinId)) { count += item.amount; } } Pool.FreeUnmanaged(ref list); return count; } private static int TakeInventory(BasePlayer player, int itemId, CurrencyInfo currencyInfo, List collect) { var take = 0; if (itemId == 0) { return take; } var amount = currencyInfo.Amount; var list = Pool.Get>(); player.inventory.GetAllItems(list); foreach (var item in list) { if (item.info.itemid == itemId && !item.IsBusy() && (currencyInfo.SkinId < 0 || item.skin == (ulong)currencyInfo.SkinId)) { var need = amount - take; if (need > 0) { if (item.amount > need) { item.MarkDirty(); item.amount -= need; take += need; var newItem = ItemManager.CreateByItemID(itemId, 1, item.skin); newItem.amount = need; newItem.CollectedForCrafting(player); collect?.Add(newItem); break; } if (item.amount <= need) { take += item.amount; item.RemoveFromContainer(); collect?.Add(item); } if (take == amount) { break; } } } } Pool.FreeUnmanaged(ref list); return take; } #endregion Pay #region Refund private void GiveRefund(BasePlayer player, BaseEntity targetEntity, RemovableEntityInfo? info) { var refund = GetRefund(targetEntity, info); if (refund == null || refund.Count == 0) { return; } foreach (var entry in refund) { var itemName = entry.Key; var currencyInfo = entry.Value; if (currencyInfo.Amount <= 0) { continue; } int itemId; string shortname; _shortPrefabNameToDeployable.TryGetValue(targetEntity.ShortPrefabName, out shortname); if (_itemShortNameToItemId.TryGetValue(itemName, out itemId)) { var isOriginalItem = itemName == shortname; var isSpecifiedSkinId = currencyInfo.SkinId >= 0; var item = ItemManager.CreateByItemID(itemId, currencyInfo.Amount, isSpecifiedSkinId ? (ulong)currencyInfo.SkinId : isOriginalItem ? targetEntity.skinID : 0); if (!isSpecifiedSkinId && isOriginalItem && item.hasCondition && targetEntity is BaseCombatEntity) { item.condition = item.maxCondition * (targetEntity.Health() / targetEntity.MaxHealth()); } player.GiveItem(item); } else { var flag = false; switch (itemName.ToLower()) { case ECONOMICS_KEY: { if (Economics == null) { continue; } var result = Economics.Call("Deposit", player.userID, (double)currencyInfo.Amount); if (result != null) { flag = true; } break; } case SERVER_REWARDS_KEY: { if (ServerRewards == null) { continue; } var result = ServerRewards.Call("AddPoints", player.userID, currencyInfo.Amount); if (result != null) { flag = true; } break; } default: { var result = Interface.CallHook("OnRemovableEntityGiveRefund", targetEntity, player, itemName, currencyInfo.Amount, currencyInfo.SkinId); if (result == null) { flag = true; } break; } } if (!flag) { PrintError($"{player} didn't receive refund maybe {itemName} doesn't seem to be a valid item name"); } } } } private Dictionary GetRefund(BaseEntity targetEntity, RemovableEntityInfo? info) { if (info.HasValue) { return info.Value.Refund.ValueName2Currency; } var buildingBlock = targetEntity.GetComponent(); if (buildingBlock != null) { var entityName = _prefabNameToStructure[buildingBlock.PrefabName]; BuildingBlocksSettings buildingBlockSettings; if (_configData.remove.buildingBlock.TryGetValue(entityName, out buildingBlockSettings)) { BuildingGradeSettings buildingGradeSettings; if (buildingBlockSettings.buildingGrade.TryGetValue(buildingBlock.grade, out buildingGradeSettings)) { if (buildingGradeSettings.refundDict != null) { return buildingGradeSettings.refundDict; } if (buildingGradeSettings.refundPercentage > 0f) { var currentGrade = buildingBlock.currentGrade; if (currentGrade != null) { var refund = new Dictionary(); var costToBuild = buildingBlock.blockDefinition.GetGrade(buildingBlock.grade, buildingBlock.skinID).CostToBuild(); foreach (var itemAmount in costToBuild) { var amount = Mathf.RoundToInt(itemAmount.amount * buildingGradeSettings.refundPercentage / 100); if (amount <= 0) { continue; } refund.Add(itemAmount.itemDef.shortname, new CurrencyInfo(amount)); } return refund; } } else if (buildingGradeSettings.refundPercentage < 0f) { var currentGrade = buildingBlock.currentGrade; if (currentGrade != null) { return buildingBlock.blockDefinition.GetGrade(buildingBlock.grade, buildingBlock.skinID).CostToBuild().ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(Mathf.RoundToInt(y.amount))); } } } } } else { EntitySettings entitySettings; if (_configData.remove.entity.TryGetValue(targetEntity.ShortPrefabName, out entitySettings)) { if (_configData.remove.refundSlot) { var slots = GetSlots(targetEntity); if (slots.Any()) { var refund = new Dictionary(entitySettings.refundDict); foreach (var slotName in slots) { if (!refund.ContainsKey(slotName)) { refund.Add(slotName, new CurrencyInfo(0)); } refund[slotName] = new CurrencyInfo(refund[slotName].Amount + 1, refund[slotName].SkinId); } return refund; } } return entitySettings.refundDict; } } 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 (_shortPrefabNameToDeployable.TryGetValue(entity.ShortPrefabName, out slotName)) { yield return slotName; } } } } } #endregion Refund #region RemoveEntity private IEnumerator RemoveAll(BaseEntity sourceEntity, BasePlayer player) { var removeList = Pool.Get>(); yield return GetNearbyEntities(sourceEntity, removeList, LAYER_ALL); yield return ProcessContainers(removeList); yield return DelayRemove(removeList, player, RemoveType.All); removeList.Clear(); Pool.FreeUnmanaged(ref removeList); _removeAllCoroutine = null; } private IEnumerator RemoveExternal(StabilityEntity sourceEntity, BasePlayer player) { var removeList = Pool.Get>(); yield return GetNearbyEntities(sourceEntity, removeList, Layers.Mask.Construction, IsExternalWall); yield return DelayRemove(removeList, player, RemoveType.External); removeList.Clear(); Pool.FreeUnmanaged(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); removeList.Clear(); Pool.FreeUnmanaged(ref removeList); _removeStructureCoroutine = null; } private IEnumerator RemovePlayerEntity(ConsoleSystem.Arg arg, ulong targetID, PlayerEntityRemoveType playerEntityRemoveType) { var current = 0; var removeList = Pool.Get>(); switch (playerEntityRemoveType) { case PlayerEntityRemoveType.All: case PlayerEntityRemoveType.Building: var onlyBuilding = playerEntityRemoveType == PlayerEntityRemoveType.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 PlayerEntityRemoveType.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; } var removed = removeList.Count(x => x != null && !x.IsDestroyed); yield return DelayRemove(removeList); removeList.Clear(); Pool.FreeUnmanaged(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) { var removed = 0; var destroyMode = removeType == RemoveType.None ? BaseNetworkable.DestroyMode.None : _configData.removeType[removeType].gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None; foreach (var entity in entities) { if (DoRemove(entity, destroyMode) && ++removed % _configData.global.removePerFrame == 0) { yield return CoroutineEx.waitForEndOfFrame; } } if (removeType == RemoveType.None) { yield break; } var toolRemover = player?.GetComponent(); if (toolRemover != null && toolRemover.RemoveType == removeType) { toolRemover.CurrentRemoved += removed; } if (player != null) { Print(player, Lang($"CompletedRemove{removeType}", player.UserIDString, removed)); } } #region RemoveEntity Helpers private static IEnumerator GetNearbyEntities(T sourceEntity, HashSet removeList, int layers, Func filter = null) where T : BaseEntity { var current = 0; var checkFrom = Pool.Get>(); var nearbyEntities = Pool.Get>(); removeList.Add(sourceEntity); 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.global.removePerFrame == 0) { yield return CoroutineEx.waitForEndOfFrame; } } checkFrom.Clear(); Pool.FreeUnmanaged(ref checkFrom); Pool.FreeUnmanaged(ref nearbyEntities); } private static IEnumerator ProcessContainers(HashSet removeList) { foreach (var entity in removeList) { ProcessContainer(entity); } 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; } ProcessContainer(entity); } } else { removeList.Add(sourceEntity); } ItemManager.DoRemoves(); yield break; } private static void ProcessContainer(BaseEntity entity) { var itemContainerEntity = entity as IItemContainerEntity; if (itemContainerEntity != null && itemContainerEntity.inventory?.itemList?.Count > 0) { if (_configData.global.noItemContainerDrop) { itemContainerEntity.inventory.Clear(); } else { DropItemContainer(itemContainerEntity.inventory, itemContainerEntity.GetDropPosition(), itemContainerEntity.Transform.rotation); } } } 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) { _instance.PrintDebug($"{player.displayName}({player.userID}) has removed {entity.ShortPrefabName}({entity.OwnerID} | {entity.transform.position})", true); Interface.CallHook("OnNormalRemovedEntity", player, entity); entity.Kill(gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None); } } #endregion RemoveEntity Helpers #endregion RemoveEntity #region API public struct CurrencyInfo { [JsonProperty(PropertyName = "amount")] public int Amount { get; set; } [JsonProperty(PropertyName = "skinId")] public long SkinId { get; set; } public CurrencyInfo(int amount, long skinId = -1) { Amount = amount; SkinId = skinId; } } private struct ValueCache { private T _value; private bool _flag; private readonly string _key; private readonly Dictionary _dictionary; public ValueCache(string key, Dictionary dictionary, T defaultValue) { _flag = false; _key = key; _value = defaultValue; _dictionary = dictionary; } public T Value { get { if (!_flag) { _flag = true; object value; if (_dictionary.TryGetValue(_key, out value)) { try { _value = (T)value; } catch (Exception) { _instance.PrintError($"Incorrect type for {_key}( {typeof(T)})"); } } } return _value; } } } private struct CurrencyCache { private bool _name2InfoFlag; private bool _name2AmountFlag; private Dictionary _valueName2Currency; private Dictionary _valueName2Info; private ValueCache> _dictionary; public CurrencyCache(string key, Dictionary dictionary) { _name2InfoFlag = false; _name2AmountFlag = false; _valueName2Info = null; _valueName2Currency = null; _dictionary = new ValueCache>(key, dictionary, default(Dictionary)); } public ExternalItemInfo? this[string key] { get { if (ValueName2Info == null) { return null; } ExternalItemInfo externalItemInfo; if (!ValueName2Info.TryGetValue(key, out externalItemInfo)) { return null; } return externalItemInfo; } } public Dictionary ValueName2Currency { get { if (!_name2AmountFlag) { _name2AmountFlag = true; if (ValueName2Info == null) { return null; } _valueName2Currency = new Dictionary(); foreach (var entry in ValueName2Info) { _valueName2Currency.Add(entry.Key, new CurrencyInfo(entry.Value.Amount.Value, entry.Value.SkinId.Value)); } } return _valueName2Currency; } } private Dictionary ValueName2Info { get { if (!_name2InfoFlag) { _name2InfoFlag = true; if (_dictionary.Value == null) { return null; } _valueName2Info = new Dictionary(); foreach (var entry in _dictionary.Value) { _valueName2Info.Add(entry.Key, new ExternalItemInfo(entry.Value as Dictionary)); } } return _valueName2Info; } } } private struct ExternalItemInfo { public ExternalItemInfo(Dictionary dictionary) { Amount = new ValueCache(nameof(Amount), dictionary, 0); SkinId = new ValueCache(nameof(SkinId), dictionary, -1); ImageId = new ValueCache(nameof(ImageId), dictionary, ""); DisplayName = new ValueCache(nameof(DisplayName), dictionary, ""); } public ValueCache Amount { get; } public ValueCache SkinId { get; } public ValueCache ImageId { get; } public ValueCache DisplayName { get; } } private struct RemovableEntityInfo { public RemovableEntityInfo(Dictionary dictionary) { ImageId = new ValueCache(nameof(ImageId), dictionary, ""); DisplayName = new ValueCache(nameof(DisplayName), dictionary, ""); Price = new CurrencyCache(nameof(Price), dictionary); Refund = new CurrencyCache(nameof(Refund), dictionary); } public ValueCache ImageId { get; } public ValueCache DisplayName { get; } public CurrencyCache Price { get; } public CurrencyCache Refund { get; } } private static RemovableEntityInfo? GetRemovableEntityInfo(BaseEntity entity, BasePlayer player) { if (entity == null) { return null; } var result = Interface.CallHook("OnRemovableEntityInfo", entity, player) as Dictionary; if (result != null) { return new RemovableEntityInfo(result); } return null; } private bool IsToolRemover(BasePlayer player) { return player != null && player.GetComponent() != null; } private string GetPlayerRemoveType(BasePlayer player) { return player != null ? player.GetComponent()?.RemoveType.ToString() : null; } #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; } var removeType = RemoveType.Normal; var time = _configData.removeType[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.removeType[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.removeType[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.removeType[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.removeType[removeType].defaultTime; if (!permission.UserHasPermission(player.UserIDString, PERMISSION_EXTERNAL)) { Print(player, Lang("NotAllowed", player.UserIDString, PERMISSION_EXTERNAL)); return; } break; case "h": case "help": var stringBuilder = Pool.Get(); stringBuilder.Clear(); stringBuilder.AppendLine(Lang("Syntax", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.Normal))); stringBuilder.AppendLine(Lang("Syntax1", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.Admin))); stringBuilder.AppendLine(Lang("Syntax2", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.All))); stringBuilder.AppendLine(Lang("Syntax3", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.Structure))); stringBuilder.AppendLine(Lang("Syntax4", player.UserIDString, _configData.chat.command, GetRemoveTypeName(RemoveType.External))); Print(player, stringBuilder.ToString()); stringBuilder.Clear(); Pool.FreeUnmanaged(ref stringBuilder); return; default: if (int.TryParse(args[0], out time)) { break; } Print(player, Lang("SyntaxError", player.UserIDString, _configData.chat.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; } var maxRemovable = 0; bool pay = false, refund = false; var removeTypeS = _configData.removeType[removeType]; var distance = removeTypeS.distance; var maxTime = removeTypeS.maxTime; var resetTime = removeTypeS.resetTime; var interval = _configData.global.removeInterval; if (removeType == RemoveType.Normal) { var permissionS = GetPermissionSettings(player); var cooldown = permissionS.cooldown; if (cooldown > 0 && !(_configData.global.cooldownExclude && player.IsAdmin)) { float lastUse; if (_cooldownTimes.TryGetValue(player.userID, out lastUse)) { var timeLeft = cooldown - (Time.realtimeSinceStartup - lastUse); if (timeLeft > 0) { var timeRemaining = timeLeft > 300f ? TimeSpan.FromSeconds(timeLeft).ToShortString() : Mathf.CeilToInt(timeLeft).ToString(); Print(player, Lang("Cooldown", player.UserIDString, timeRemaining)); return false; } } } if (_removeMode == RemoveMode.MeleeHit && _configData.removerMode.meleeHitRequires) { if (!IsMeleeTool(player)) { var meleeToolDisplayName = GetDisplayNameByCurrencyName(lang.GetLanguage(player.UserIDString), _configData.removerMode.meleeHitItemShortname, _configData.removerMode.meleeHitModeSkin); Print(player, Lang("MeleeToolNotHeld", player.UserIDString, meleeToolDisplayName)); return false; } } if (_removeMode == RemoveMode.SpecificTool && _configData.removerMode.specificToolRequires) { if (!IsSpecificTool(player)) { var specificToolDisplayName = GetDisplayNameByCurrencyName(lang.GetLanguage(player.UserIDString), _configData.removerMode.specificToolShortName, _configData.removerMode.specificToolSkin); Print(player, Lang("SpecificToolNotHeld", player.UserIDString, specificToolDisplayName)); return false; } } interval = permissionS.removeInterval; resetTime = permissionS.resetTime; maxTime = permissionS.maxTime; maxRemovable = permissionS.maxRemovable; if (_configData.global.maxRemovableExclude && player.IsAdmin) { maxRemovable = 0; } distance = permissionS.distance; pay = permissionS.pay; refund = permissionS.refund; } if (time == 0) { time = _configData.removeType[removeType].defaultTime; } if (time > maxTime) { time = maxTime; } var toolRemover = player.GetOrAddComponent(); if (toolRemover.RemoveType == RemoveType.Normal) { if (!_configData.global.startCooldownOnRemoved) { _cooldownTimes[player.userID] = Time.realtimeSinceStartup; } } toolRemover.Init(removeType, time, maxRemovable, distance, interval, pay, refund, resetTime, true); if (_configData.chat.showMessageWhenEnabledOrDisabled) { 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, null, arg.Args.Select(x => (string)x).ToArray()); // Really shit } [ConsoleCommand("remove.target")] private void CCmdRemoveTarget(ConsoleSystem.Arg arg) { if (arg.Args == null || arg.Args.Length <= 1) { var stringBuilder = Pool.Get(); stringBuilder.Clear(); 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()); stringBuilder.Clear(); Pool.FreeUnmanaged(ref stringBuilder); 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].ToString()); if (target == null || !target.IsConnected) { Print(arg, target == null ? $"'{arg.Args[0]}' cannot be found." : $"'{target}' is offline."); return; } var removeType = RemoveType.Normal; switch (arg.Args[0].ToString().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: var stringBuilder = Pool.Get(); stringBuilder.Clear(); 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()); stringBuilder.Clear(); Pool.FreeUnmanaged(ref stringBuilder); return; } var maxRemovable = 0; var time = _configData.removeType[removeType].defaultTime; if (arg.Args.Length > 2) { int.TryParse(arg.Args[2].ToString(), out time); } if (arg.Args.Length > 3 && removeType == RemoveType.Normal) { int.TryParse(arg.Args[3].ToString(), out maxRemovable); } var permissionS = _configData.permission[PERMISSION_NORMAL]; var targetRemover = target.GetOrAddComponent(); targetRemover.Init(removeType, time, maxRemovable, _configData.removeType[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].ToString().ToLower()) { case "price": if (!float.TryParse(arg.Args[1].ToString(), out value)) { value = 50f; } foreach (var construction in _constructions) { BuildingBlocksSettings buildingBlocksSettings; if (_configData.remove.buildingBlock.TryGetValue(construction.info.name.english, out buildingBlocksSettings)) { foreach (var entry in buildingBlocksSettings.buildingGrade) { var grade = construction.GetGrade(entry.Key, 0); var costToBuild = grade.CostToBuild(); entry.Value.price = costToBuild.ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(value <= 0 ? 0 : Mathf.RoundToInt(y.amount * value / 100))); } } } Print(arg, $"Successfully modified all building prices to {value}% of the initial cost."); SaveConfig(); LoadConfig(); return; case "refund": if (!float.TryParse(arg.Args[1].ToString(), out value)) { value = 40f; } foreach (var construction in _constructions) { BuildingBlocksSettings buildingBlocksSettings; if (_configData.remove.buildingBlock.TryGetValue(construction.info.name.english, out buildingBlocksSettings)) { foreach (var entry in buildingBlocksSettings.buildingGrade) { var grade = construction.GetGrade(entry.Key, 0); var costToBuild = grade.CostToBuild(); entry.Value.refund = costToBuild.ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(value <= 0 ? 0 : Mathf.RoundToInt(y.amount * value / 100))); } } } Print(arg, $"Successfully modified all building refunds to {value}% of the initial cost."); SaveConfig(); LoadConfig(); return; case "pricep": if (!float.TryParse(arg.Args[1].ToString(), out value)) { value = 40f; } foreach (var buildingBlockS in _configData.remove.buildingBlock.Values) { foreach (var data in buildingBlockS.buildingGrade.Values) { data.price = value <= 0 ? 0 : value; } } Print(arg, $"Successfully modified all building prices to {value}% of the initial cost."); SaveConfig(); LoadConfig(); return; case "refundp": if (!float.TryParse(arg.Args[1].ToString(), out value)) { value = 50f; } foreach (var buildingBlockS in _configData.remove.buildingBlock.Values) { foreach (var data in buildingBlockS.buildingGrade.Values) { data.refund = value <= 0 ? 0 : value; } } Print(arg, $"Successfully modified all building refunds to {value}% of the initial cost."); SaveConfig(); LoadConfig(); 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].ToString().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) { var stringBuilder = Pool.Get(); stringBuilder.Clear(); 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()); stringBuilder.Clear(); Pool.FreeUnmanaged(ref stringBuilder); return; } if (_removePlayerEntityCoroutine != null) { Print(arg, "There is already a RemovePlayerEntity running, please wait."); return; } ulong targetID; if (!ulong.TryParse(arg.Args[1].ToString(), out targetID) || !targetID.IsSteamId()) { Print(arg, "Please enter the player's steamID."); return; } PlayerEntityRemoveType playerEntityRemoveType; switch (arg.Args[0].ToString().ToLower()) { case "a": case "all": playerEntityRemoveType = PlayerEntityRemoveType.All; break; case "b": case "building": playerEntityRemoveType = PlayerEntityRemoveType.Building; break; case "c": case "cupboard": playerEntityRemoveType = PlayerEntityRemoveType.Cupboard; break; default: Print(arg, "This is not a valid argument"); return; } _removePlayerEntityCoroutine = ServerMgr.Instance.StartCoroutine(RemovePlayerEntity(arg, targetID, playerEntityRemoveType)); Print(arg, "Start running RemovePlayerEntity, please wait."); } #endregion Commands #region Debug private void PrintDebug(string message, bool warning = false) { if (_configData.global.debugEnabled) { if (warning) { PrintWarning(message); } else { Puts(message); } } if (_configData.global.logToFile) { _debugStringBuilder.AppendLine($"[{DateTime.Now.ToString(CultureInfo.InstalledUICulture)}] | {message}"); } } private void SaveDebug() { if (!_configData.global.logToFile) { return; } var debugText = _debugStringBuilder.ToString().Trim(); _debugStringBuilder.Clear(); if (!string.IsNullOrEmpty(debugText)) { LogToFile("debug", debugText, this); } } #endregion Debug #region RustTranslationAPI private string GetItemTranslationByShortName(string language, string itemShortName) { return (string)RustTranslationAPI.Call("GetItemTranslationByShortName", language, itemShortName); } private string GetConstructionTranslation(string language, string prefabName) { return (string)RustTranslationAPI.Call("GetConstructionTranslation", language, prefabName); } private string GetDeployableTranslation(string language, string deployable) { return (string)RustTranslationAPI.Call("GetDeployableTranslation", language, deployable); } private string GetItemDisplayName(string language, string itemShortName) { if (RustTranslationAPI != null) { return GetItemTranslationByShortName(language, itemShortName); } return null; } private string GetConstructionDisplayName(BasePlayer player, string shortPrefabName, string displayName) { if (RustTranslationAPI != null) { var displayName1 = GetConstructionTranslation(lang.GetLanguage(player.UserIDString), shortPrefabName); if (!string.IsNullOrEmpty(displayName1)) { return displayName1; } } return displayName; } private string GetDeployableDisplayName(BasePlayer player, string deployable, string displayName) { if (RustTranslationAPI != null) { var displayName1 = GetDeployableTranslation(lang.GetLanguage(player.UserIDString), deployable); if (!string.IsNullOrEmpty(displayName1)) { return displayName1; } } return displayName; } #endregion RustTranslationAPI #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.remove.validConstruction.ContainsKey(value)) { _configData.remove.validConstruction.Add(value, true); } } var newBuildingBlocks = new Dictionary(); foreach (var construction in _constructions) { if (newBuildingBlocks.ContainsKey(construction.info.name.english)) continue; BuildingBlocksSettings buildingBlocksSettings; if (!_configData.remove.buildingBlock.TryGetValue(construction.info.name.english, out buildingBlocksSettings)) { var buildingGrade = new Dictionary(); foreach (var value in buildingGrades) { var grade = construction.GetGrade(value, 0); if (grade == null) { continue; } var costToBuild = grade.CostToBuild(); buildingGrade.Add(value, new BuildingGradeSettings { refund = costToBuild.ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(Mathf.RoundToInt(y.amount * 0.4f))), price = costToBuild.ToDictionary(x => x.itemDef.shortname, y => new CurrencyInfo(Mathf.RoundToInt(y.amount * 0.6f))) }); } buildingBlocksSettings = new BuildingBlocksSettings { displayName = construction.info.name.english, buildingGrade = buildingGrade }; } newBuildingBlocks.Add(construction.info.name.english, buildingBlocksSettings); } _configData.remove.buildingBlock = newBuildingBlocks; foreach (var entry in _shortPrefabNameToDeployable) { EntitySettings entitySettings; if (!_configData.remove.entity.TryGetValue(entry.Key, out entitySettings)) { var itemDefinition = ItemManager.FindItemDefinition(entry.Value); entitySettings = new EntitySettings { enabled = _configData.global.defaultEntity.removeAllowed && itemDefinition.category != ItemCategory.Food, displayName = itemDefinition.displayName.english, refund = new Dictionary { [entry.Value] = new CurrencyInfo(1) }, price = new Dictionary() }; _configData.remove.entity.Add(entry.Key, entitySettings); } } SaveConfig(); } private static ConfigData _configData; private class ConfigData { [JsonProperty(PropertyName = "Settings")] public readonly GlobalSettings global = new GlobalSettings(); [JsonProperty(PropertyName = "Container Settings")] public readonly ContainerSettings container = new ContainerSettings(); [JsonProperty(PropertyName = "Remove Damaged Entities")] public readonly DamagedEntitySettings damagedEntity = new DamagedEntitySettings(); [JsonProperty(PropertyName = "Chat Settings")] public readonly ChatSettings chat = new ChatSettings(); [JsonProperty(PropertyName = "Permission Settings (Just for normal type)")] public readonly Dictionary permission = new Dictionary { [PERMISSION_NORMAL] = new PermissionSettings { priority = 0, distance = 3, cooldown = 60, maxTime = 300, maxRemovable = 50, removeInterval = 0.8f, pay = true, refund = true, resetTime = false } }; [JsonProperty(PropertyName = "Remove Type Settings")] public readonly Dictionary removeType = 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 } }; [JsonProperty(PropertyName = "Remove Mode Settings (Only one model works)")] public readonly RemoverModeSettings removerMode = new RemoverModeSettings(); [JsonProperty(PropertyName = "NoEscape Settings")] public readonly NoEscapeSettings noEscape = new NoEscapeSettings(); [JsonProperty(PropertyName = "Image Urls (Used to UI image)")] public readonly Dictionary imageUrls = new Dictionary { [ECONOMICS_KEY] = "https://cdn3.mapstr.gg/f4abf259cd314a67a8e0c12cf86e9b50.png", [SERVER_REWARDS_KEY] = "https://cdn3.mapstr.gg/e189bdf530cdc149a99fe711e3ba7531.png" }; [JsonProperty(PropertyName = "GUI")] public readonly UiSettings ui = new UiSettings(); [JsonProperty(PropertyName = "Remove Info (Refund & Price)")] public readonly RemoveSettings remove = new RemoveSettings(); [JsonProperty(PropertyName = "Version")] public VersionNumber version; } public class GlobalSettings { [JsonProperty(PropertyName = "Enable Debug Mode")] public bool debugEnabled; [JsonProperty(PropertyName = "Log Debug To File")] public bool logToFile; [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 - 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 = "Default Entity Settings (When automatically adding new entities to 'Other Entity Settings')")] public DefaultEntitySettings defaultEntity = new DefaultEntitySettings(); public class DefaultEntitySettings { [JsonProperty(PropertyName = "Default Remove Allowed")] public bool removeAllowed = true; } } 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; } public class DamagedEntitySettings { [JsonProperty(PropertyName = "Enabled")] public bool enabled = false; [JsonProperty(PropertyName = "Exclude Quarries")] public bool excludeQuarries = true; [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; } 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 = "Show Message When Enabled/Disabled")] public bool showMessageWhenEnabledOrDisabled = true; } public class PermissionSettings { [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 = Unlimited)")] 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; } 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; } 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 = "Melee Tool Hit Mode")] public bool meleeHitMode; [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; [JsonProperty(PropertyName = "Melee Tool Hit Mode - Disable remover tool when you are not holding a melee tool")] public bool meleeHitDisableInHand; [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; } public class NoEscapeSettings { [JsonProperty(PropertyName = "Use Raid Blocker")] public bool useRaidBlocker; [JsonProperty(PropertyName = "Use Combat Blocker")] public bool useCombatBlocker; } 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; [JsonProperty(PropertyName = "Crosshair - Enabled")] public bool showCrosshair = true; [JsonProperty(PropertyName = "Crosshair - Image Url")] public string crosshairImageUrl = "https://cdn3.mapstr.gg/23b32dce14edec41b849a7e729e4b3ca.png"; [JsonProperty(PropertyName = "Crosshair - Box - Min Anchor (in Rust Window)")] public string crosshairAnchorMin = "0.5 0.5"; [JsonProperty(PropertyName = "Crosshair - Box - Max Anchor (in Rust Window)")] public string crosshairAnchorMax = "0.5 0.5"; [JsonProperty(PropertyName = "Crosshair - Box - Min Offset (in Rust Window)")] public string crosshairOffsetMin = "-15 -15"; [JsonProperty(PropertyName = "Crosshair - Box - Max Offset (in Rust Window)")] public string crosshairOffsetMax = "15 15"; [JsonProperty(PropertyName = "Crosshair - Box - Image Color")] public string crosshairColor = "1 0 0 1"; [JsonIgnore] public Vector2 Price2TextAnchorMin, Price2TextAnchorMax, Refund2TextAnchorMin, Refund2TextAnchorMax; } 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 = "Display Names (Refund & Price)")] public readonly Dictionary displayNames = new Dictionary(); [JsonProperty(PropertyName = "Building Blocks Settings")] public Dictionary buildingBlock = new Dictionary(); [JsonProperty(PropertyName = "Other Entity Settings")] public Dictionary entity = new Dictionary(); } public class BuildingBlocksSettings { [JsonProperty(PropertyName = "Display Name")] public string displayName; [JsonProperty(PropertyName = "Building Grade")] public Dictionary buildingGrade = new Dictionary(); } public class BuildingGradeSettings { [JsonProperty(PropertyName = "Price")] public object price; [JsonProperty(PropertyName = "Refund")] public object refund; [JsonIgnore] public float pricePercentage = -1, refundPercentage = -1; [JsonIgnore] public Dictionary priceDict, refundDict; } public class EntitySettings { [JsonProperty(PropertyName = "Remove Allowed")] public bool enabled; [JsonProperty(PropertyName = "Display Name")] public string displayName = string.Empty; [JsonProperty(PropertyName = "Price")] public object price; [JsonProperty(PropertyName = "Refund")] public object refund; [JsonIgnore] public Dictionary priceDict, refundDict; } 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.ui.Price2TextAnchorMin = GetAnchor(_configData.ui.price2TextAnchorMin); _configData.ui.Price2TextAnchorMax = GetAnchor(_configData.ui.price2TextAnchorMax); _configData.ui.Refund2TextAnchorMin = GetAnchor(_configData.ui.refund2TextAnchorMin); _configData.ui.Refund2TextAnchorMax = GetAnchor(_configData.ui.refund2TextAnchorMax); foreach (var entry in _configData.remove.buildingBlock) { foreach (var gradeEntry in entry.Value.buildingGrade) { 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.priceDict = priceDic; } else { try { gradeEntry.Value.priceDict = JsonConvert.DeserializeObject>(price.ToString()); } catch (Exception e) { gradeEntry.Value.priceDict = 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.refundDict = refundDic; } else { try { gradeEntry.Value.refundDict = JsonConvert.DeserializeObject>(refund.ToString()); } catch (Exception e) { gradeEntry.Value.refundDict = null; PrintError($"Wrong refund format for '{gradeEntry.Key}' of '{entry.Key}' in 'Building Blocks Settings'. Error Message: {e.Message}"); } } } } } foreach (var entry in _configData.remove.entity) { var price = entry.Value.price; var priceDic = price as Dictionary; if (priceDic != null) { entry.Value.priceDict = priceDic; } else { try { entry.Value.priceDict = JsonConvert.DeserializeObject>(price.ToString()); } catch (Exception e) { entry.Value.priceDict = null; PrintError($"Wrong price format for '{entry.Key}' of '{entry.Key}' in 'Other Entity Settings'. Error Message: {e.Message}"); } } var refund = entry.Value.refund; var refundDic = refund as Dictionary; if (refundDic != null) { entry.Value.refundDict = refundDic; } else { try { entry.Value.refundDict = JsonConvert.DeserializeObject>(refund.ToString()); } catch (Exception e) { entry.Value.refundDict = null; PrintError($"Wrong refund format for '{entry.Key}' of '{entry.Key}' in 'Other Entity 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.chat.prefix = $"{prefix}: "; } if (_configData.ui.removerToolAnchorMin == "0.1 0.55") { _configData.ui.removerToolAnchorMin = "0.04 0.55"; } if (_configData.ui.removerToolAnchorMax == "0.4 0.95") { _configData.ui.removerToolAnchorMax = "0.37 0.95"; } } 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.removerMode.meleeHitMode = true; _configData.removerMode.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.removerMode.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.removerMode.meleeHitDisableInHand = true; } } if (_configData.version <= new VersionNumber(4, 3, 23)) { string value; if (GetConfigValue(out value, "GUI", "Authorization Check - Box - Allowed Background")) { _configData.ui.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.ui.refusedBackgroundColor = value == "1 0 0 0.8" ? "0.78 0.22 0.27 1" : value; } if (_configData.ui.removeBackgroundColor == "0.42 0.88 0.88 1") { _configData.ui.removeBackgroundColor = "0.31 0.88 0.71 1"; } if (_configData.ui.entityBackgroundColor == "0 0 0 0.8") { _configData.ui.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 config = Config.ReadObject(); if (config == null) { return; } //Interface.Oxide.DataFileSystem.WriteObject(Name + "_old", jObject); VersionNumber oldVersion; if (GetConfigVersionPre(config, 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(config, out enabled, "Remove Type Settings", value.ToString(), "Reset the time after removing a entity")) { SetConfigValuePre(config, enabled, "Remove Type Settings", value.ToString(), "Reset the time after removing an entity"); } } Dictionary values; if (GetConfigValuePre(config, out values, "Permission Settings (Just for normal type)")) { foreach (var entry in values) { object value; if (GetConfigValuePre(config, out value, "Permission Settings (Just for normal type)", entry.Key, "Reset the time after removing a entity")) { SetConfigValuePre(config, value, "Permission Settings (Just for normal type)", entry.Key, "Reset the time after removing an entity"); } } } } if (oldVersion <= new VersionNumber(4, 3, 25)) { bool enabled; if (GetConfigValuePre(config, out enabled, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Show Crosshair")) { SetConfigValuePre(config, enabled, "GUI", "Crosshair - Enabled"); } object value; if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Image Url")) { SetConfigValuePre(config, value, "GUI", "Crosshair - Image Url"); } if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Min Anchor (in Rust Window)")) { SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Min Anchor (in Rust Window)"); } if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Max Anchor (in Rust Window)")) { SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Max Anchor (in Rust Window)"); } if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Min Offset (in Rust Window)")) { SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Min Offset (in Rust Window)"); } if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Max Offset (in Rust Window)")) { SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Max Offset (in Rust Window)"); } if (GetConfigValuePre(config, out value, "Remove Mode Settings (Only one model works)", "No Held Item Mode - Crosshair Box - Image Color")) { SetConfigValuePre(config, value, "GUI", "Crosshair - Box - Image Color"); } } if (oldVersion <= new VersionNumber(4, 3, 32)) { try { var permissions = GetConfigValue(config, "Permission Settings (Just for normal type)"); if (permissions != null) { foreach (var perm in permissions) { var maxRemovables = perm.Value.Value("Max Removable Objects (0 = Unlimit)"); perm.Value["Max Removable Objects (0 = Unlimited)"] = JToken.FromObject(maxRemovables); } } var entities = GetConfigValue(config, "Remove Info (Refund & Price)", "Other Entity Settings"); if (entities != null) { foreach (var entity in entities) { var price = entity.Value.Value("Price"); var priceDict = price?.ToObject>(); if (priceDict != null) { entity.Value["Price"] = JToken.FromObject(priceDict.ToDictionary(x => x.Key, y => new CurrencyInfo(y.Value))); } var refund = entity.Value.Value("Refund"); var refundDict = refund?.ToObject>(); if (refundDict != null) { entity.Value["Refund"] = JToken.FromObject(refundDict.ToDictionary(x => x.Key, y => new CurrencyInfo(y.Value))); } } } var buildingBlocks = GetConfigValue(config, "Remove Info (Refund & Price)", "Building Blocks Settings"); if (buildingBlocks != null) { foreach (var item in buildingBlocks) { var buildingGrades = item.Value.Value("Building Grade"); foreach (var buildingGrade in buildingGrades) { float percentage; var price = buildingGrade.Value.Value("Price"); if (price != null && !float.TryParse(price.ToString(), out percentage)) { var target = buildingGrade.Value["Price"] as JObject; var dict = target?.ToObject>(); if (dict != null) { buildingGrade.Value["Price"] = JToken.FromObject(dict.ToDictionary(x => x.Key, y => new CurrencyInfo(y.Value))); } } var refund = buildingGrade.Value.Value("Refund"); if (refund != null && !float.TryParse(refund.ToString(), out percentage)) { var target = buildingGrade.Value["Refund"] as JObject; var dict = target?.ToObject>(); if (dict != null) { buildingGrade.Value["Refund"] = JToken.FromObject(dict.ToDictionary(x => x.Key, y => new CurrencyInfo(y.Value))); } } } } } } catch { // ignored } } Config.WriteObject(config); // Interface.Oxide.DataFileSystem.WriteObject(Name + "_new", jObject); } } } private JObject GetConfigValue(JObject config, params string[] path) { if (path.Length < 1) { throw new ArgumentException("path is empty"); } try { JToken jToken; if (!config.TryGetValue(path[0], out jToken)) { return null; } for (var i = 1; i < path.Length; i++) { var jObject = jToken as JObject; if (jObject == null || !jObject.TryGetValue(path[i], out jToken)) { return null; } } return jToken as JObject; } catch (Exception ex) { PrintError($"GetConfigValue ERROR: path: {string.Join("\\", path)}\n{ex}"); } return null; } 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 (var 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 (var 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); } } var targetToken = jToken as JObject; if (targetToken != null) { targetToken[path[path.Length - 1]] = JToken.FromObject(value); } // (jToken as JObject)?.TryAdd(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.chat.prefix, _configData.chat.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) { try { return string.Format(lang.GetMessage(key, this, id), args); } catch (Exception) { PrintError($"Error in the language formatting of '{key}'. (userid: {id}. lang: {lang.GetLanguage(id)}. args: {string.Join(" ,", args)})"); throw; } } 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 {0} in order to use the Remover Tool", ["SpecificToolNotHeld"] = "You need to be holding a {0} 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.", ["CombatBlocked"] = "Can't remove: Combat blocked.", ["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.", ["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.", //["UsageOfRemove"] = "You have to hold a hammer in your hand and press the left mouse button.", ["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"] = "您必须拿着{0}才可以使用拆除工具", ["SpecificToolNotHeld"] = "您必须拿着{0}才可以使用拆除工具", ["StartRemoveAll"] = "开始运行 '所有拆除',请稍等片刻", ["StartRemoveStructure"] = "开始运行 '建筑拆除',请稍等片刻", ["StartRemoveExternal"] = "开始运行 '外墙拆除',请稍等片刻", ["AlreadyRemoveAll"] = "已经有一个 '所有拆除' 正在运行,请稍等片刻", ["AlreadyRemoveStructure"] = "已经有一个 '建筑拆除' 正在运行,请稍等片刻", ["AlreadyRemoveExternal"] = "已经有一个 '外墙拆除' 正在运行,请稍等片刻", ["CompletedRemoveAll"] = "您使用 '所有拆除' 成功拆除了 {0} 个实体", ["CompletedRemoveStructure"] = "您使用 '建筑拆除' 成功拆除了 {0} 个实体", ["CompletedRemoveExternal"] = "您使用 '外墙拆除' 成功拆除了 {0} 个实体", ["CanRemove"] = "您可以拆除该实体", ["NotEnoughCost"] = "无法拆除: 拆除所需资源不足", ["EntityDisabled"] = "无法拆除: 服务器已禁用拆除这种实体", ["DamagedEntity"] = "无法拆除: 服务器已禁用拆除已损坏的实体", ["BeBlocked"] = "无法拆除: 其他插件阻止您拆除该实体", ["InvalidEntity"] = "无法拆除: 无效的实体", ["NotFoundOrFar"] = "无法拆除: 没有找到实体或者距离太远", ["StorageNotEmpty"] = "无法拆除: 该实体内含有物品", ["RaidBlocked"] = "无法拆除: 拆除工具被突袭阻止了", ["CombatBlocked"] = "无法拆除: 拆除工具被战斗阻止了", ["NotRemoveAccess"] = "无法拆除: 您无权拆除该实体", ["NotStructure"] = "无法拆除: 该实体不是建筑物", ["NotExternalWall"] = "无法拆除: 该实体不是外高墙", ["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"] = "Вы должны держать {0}, чтобы использовать инструмент для удаления.", ["SpecificToolNotHeld"] = "Вы должны держать {0}, чтобы использовать инструмент для удаления.", ["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"] = "Нельзя удалить: Рейды остановки.", ["CombatBlocked"] = "Нельзя удалить: боевые остановки", ["NotRemoveAccess"] = "Нельзя удалить: У вас нет прав удалять это.", ["NotStructure"] = "Нельзя удалить: Объект не конструкция.", ["NotExternalWall"] = "Нельзя удалить: Объект не внешняя стена.", ["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"); lang.RegisterMessages(new Dictionary { ["NotAllowed"] = "No tienes permiso '{0}' para usar este comando.", ["TargetDisabled"] = "La herramienta de eliminación de {0} ha sido desactivada.", ["TargetEnabled"] = "{0} ahora está usando la herramienta de eliminación; habilitada durante {1} segundos (Máximo de objetos removibles: {2}, Tipo de eliminación: {3}).", ["ToolDisabled"] = "La herramienta de eliminación ha sido desactivada.", ["ToolEnabled"] = "Herramienta de eliminación habilitada durante {0} segundos (Máximo de objetos removibles: {1}, Tipo de eliminación: {2}).", ["Cooldown"] = "Debes esperar {0} segundos antes de usar la herramienta de eliminación nuevamente.", ["CurrentlyDisabled"] = "La herramienta de eliminación está actualmente desactivada.", ["EntityLimit"] = "Límite de entidades alcanzado, has eliminado {0} entidades, la herramienta de eliminación se ha desactivado automáticamente.", ["MeleeToolNotHeld"] = "Debes estar sosteniendo un(a) {0} para usar la herramienta de eliminación", ["SpecificToolNotHeld"] = "Debes estar sosteniendo un(a) {0} para usar la herramienta de eliminación.", ["StartRemoveAll"] = "Iniciando eliminación total, por favor espera.", ["StartRemoveStructure"] = "Iniciando eliminación de estructura, por favor espera.", ["StartRemoveExternal"] = "Iniciando eliminación de externos, por favor espera.", ["AlreadyRemoveAll"] = "Ya hay una eliminación total en curso, por favor espera.", ["AlreadyRemoveStructure"] = "Ya hay una eliminación de estructura en curso, por favor espera.", ["AlreadyRemoveExternal"] = "Ya hay una eliminación de externos en curso, por favor espera.", ["CompletedRemoveAll"] = "Has eliminado exitosamente {0} entidades utilizando la eliminación total.", ["CompletedRemoveStructure"] = "Has eliminado exitosamente {0} entidades utilizando la eliminación de estructura.", ["CompletedRemoveExternal"] = "Has eliminado exitosamente {0} entidades utilizando la eliminación de externos.", ["CanRemove"] = "Puedes eliminar esta entidad.", ["NotEnoughCost"] = "No se puede eliminar: no tienes suficientes recursos.", ["EntityDisabled"] = "No se puede eliminar: el servidor ha desactivado la eliminación de esta entidad.", ["DamagedEntity"] = "No se puede eliminar: el servidor ha desactivado la eliminación de objetos dañados.", ["BeBlocked"] = "No se puede eliminar: un complemento externo bloqueó su uso.", ["InvalidEntity"] = "No se puede eliminar: no se ha seleccionado una entidad válida.", ["NotFoundOrFar"] = "No se puede eliminar: la entidad no se encuentra o está demasiado lejos.", ["StorageNotEmpty"] = "No se puede eliminar: el almacenamiento de la entidad no está vacío.", ["RaidBlocked"] = "No se puede eliminar: bloqueado durante un asalto.", ["CombatBlocked"] = "No se puede eliminar: bloqueado durante el combate.", ["NotRemoveAccess"] = "No se puede eliminar: no tienes permisos para eliminar esto.", ["NotStructure"] = "No se puede eliminar: la entidad no es una estructura.", ["NotExternalWall"] = "No se puede eliminar: la entidad no es una pared externa.", ["EntityTimeLimit"] = "No se puede eliminar: la entidad fue construida hace más de {0} segundos.", //["Can'tOpenAllLocks"] = "No se puede eliminar: hay una cerradura en el edificio que no puedes abrir.", ["CantPay"] = "No se puede eliminar: ¡el sistema de pago ha fallado! Contacta a un administrador con la fecha y hora para ayudarlo a entender lo que sucedió.", //["UsageOfRemove"] = "Debes sostener un martillo en tu mano y presionar el botón izquierdo del mouse.", ["Refund"] = "Reembolso:", ["Nothing"] = "Nada", ["Price"] = "Precio:", ["Free"] = "Gratis", ["TimeLeft"] = "Tiempo restante: {0}s\nEliminado: {1}", ["RemoverToolType"] = "Herramienta de eliminación ({0})", ["Unlimit"] = "∞", ["SyntaxError"] = "Error de sintaxis, por favor escribe '/{0} ' para ver la ayuda", ["Syntax"] = "/{0} [tiempo (segundos)] - Habilitar la herramienta de eliminación ({1})", ["Syntax1"] = "/{0} [tiempo (segundos)] - Habilitar la herramienta de eliminación ({1})", ["Syntax2"] = "/{0} [tiempo (segundos)] - Habilitar la herramienta de eliminación ({1})", ["Syntax3"] = "/{0} [tiempo (segundos)] - Habilitar la herramienta de eliminación ({1})", ["Syntax4"] = "/{0} [tiempo (segundos)] - Habilitar la herramienta de eliminación ({1})" }, this, "es"); } #endregion LanguageFile } }