RustServer/plugins/RemoverTool.cs

3353 lines
161 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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