using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Oxide.Core.Libraries.Covalence; using Oxide.Core.Plugins; using UnityEngine; namespace Oxide.Plugins { [Info("Door Upgrade", "Bazz3l", "1.0.1")] [Description("Allows the ability for players to upgrade doors to the next tier using hammer or toolgun")] public class DoorUpgrade : CovalencePlugin { [PluginReference] private Plugin NoEscape; #region Fields private const string UPGRADE_EFFECT = "assets/bundled/prefabs/fx/build/promote_toptier.prefab"; private const string PERM_USE = "doorupgrade.use"; private const float DAMAGE_COOLDOWN = 30f; private readonly HashSet _toggles = new HashSet(); private static PluginConfig _config; #endregion #region Lang protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { {LangKeys.Prefix, "Door Upgrade: "}, {LangKeys.MissingBlueprintReq, "missing blueprint requirement."}, {LangKeys.MissingIngredientReq, "missing upgrade items.\n{0}"}, {LangKeys.AttackBlocked, "unable to upgrade, upgradable in: {0}s."}, {LangKeys.BuildingBlocked, "you are currently building blocked."}, {LangKeys.RaidBlocked, "you are currently raid/combat blocked."}, {LangKeys.AlreadyTopTier, "door is currently top tier."}, {LangKeys.UpgradeToggle, "was {0}."}, {LangKeys.Enabled, "Enabled"}, {LangKeys.Disable, "Disabled"} }, this); } private class LangKeys { public const string Prefix = "Prefix"; public const string MissingBlueprintReq = "MissingBlueprintReq"; public const string MissingIngredientReq = "MissingIngredientReq"; public const string BuildingBlocked = "BuildingBlocked"; public const string RaidBlocked = "NoEscapeBlocked"; public const string AttackBlocked = "AttackBlocked"; public const string AlreadyTopTier = "AlreadyTopTier"; public const string UpgradeToggle = "UpgradeToggle"; public const string Enabled = "Enabled"; public const string Disable = "Disable"; } private void MessagePlayer(IPlayer player, string key, string userID = "", params object[] args) => player.Message(lang.GetMessage(LangKeys.Prefix, this, userID) + string.Format(lang.GetMessage(key, this, userID), args)); #endregion #region Config protected override void LoadDefaultConfig() => _config = PluginConfig.DefaultConfig(); protected override void LoadConfig() { base.LoadConfig(); try { _config = Config.ReadObject(); if (_config == null) throw new JsonException(); } catch { PrintWarning("Default config loaded."); LoadDefaultConfig(); } } protected override void SaveConfig() => Config.WriteObject(_config, true); private class PluginConfig { [JsonProperty("Chat command")] public string ChatCommand; [JsonProperty("Disable when item is switch from allowed tool")] public bool EnabledSwitch; [JsonProperty("Player must pay upgrade costs")] public bool EnabledCost; [JsonProperty("Player must have required blueprint unlocked")] public bool EnabledBlueprint; [JsonProperty("Blocked upgrade when recently damaged")] public bool RecentDamaged; [JsonProperty("Blocked upgrade when building blocked")] public bool BuildingBlocked; [JsonProperty("Blocked upgrade when raid blocked")] public bool RaidBlocked; public static PluginConfig DefaultConfig() { return new PluginConfig { ChatCommand = "dup", EnabledSwitch = true, EnabledCost = true, EnabledBlueprint = true, RecentDamaged = true, BuildingBlocked = true, RaidBlocked = true }; } } #endregion #region Oxide Hooks private void OnServerInitialized() { AddCovalenceCommand(_config.ChatCommand, nameof(ToggleCommand), PERM_USE); Puts($"Chat command {_config.ChatCommand} | Required permission {PERM_USE}"); } private void OnPlayerDisconnected(BasePlayer player) => _toggles.Remove(player.UserIDString); private void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem) { if (player == null || !player.userID.IsSteamId()) return; if (newItem == null) return; if (!_config.EnabledSwitch || IsRepairTool(player)) return; if (_toggles.Contains(player.UserIDString)) { _toggles.Remove(player.UserIDString); MessagePlayer(player.IPlayer, LangKeys.UpgradeToggle, player.UserIDString, lang.GetMessage(LangKeys.Disable, this, player.UserIDString)); } } private object OnHammerHit(BasePlayer player, HitInfo info) { var door = info?.HitEntity as Door; if (door == null || !_toggles.Contains(player.UserIDString)) return null; if (IsUpgradeBlocked(player, door)) return false; if (!HasUpgradePath(door)) { MessagePlayer(player.IPlayer, LangKeys.AlreadyTopTier, player.UserIDString); return false; } var upgradePathType = GetUpgradeType(door); if (upgradePathType == UpgradePathType.None) return false; var upgradePathItem = _upgradePathItem[upgradePathType]; if (upgradePathItem == null) return false; if (_config.EnabledBlueprint && !upgradePathItem.HasUnlocked(player)) { MessagePlayer(player.IPlayer, LangKeys.MissingBlueprintReq, player.UserIDString); return false; } if (_config.EnabledCost) { if (!upgradePathItem.CanAffordUpgrade(player)) { MessagePlayer(player.IPlayer, LangKeys.MissingIngredientReq, player.UserIDString, upgradePathItem.MissingItems); return false; } upgradePathItem.PayForUpgrade(player); } upgradePathItem.PerformUpgrade(door); return false; } #endregion #region Door Upgrade private readonly Dictionary _upgradePathItem = new Dictionary { { UpgradePathType.DoubleMetalTier, new UpgradePathItem { Prefab = "assets/prefabs/building/door.double.hinged/door.double.hinged.metal.prefab", ItemID = 1390353317 } }, { UpgradePathType.SingleMetalTier, new UpgradePathItem { Prefab = "assets/prefabs/building/door.hinged/door.hinged.metal.prefab", ItemID = -2067472972 } }, { UpgradePathType.DoubleTopTier, new UpgradePathItem { Prefab = "assets/prefabs/building/door.double.hinged/door.double.hinged.toptier.prefab", ItemID = 1221063409 } }, { UpgradePathType.SingleTopTier, new UpgradePathItem { Prefab = "assets/prefabs/building/door.hinged/door.hinged.toptier.prefab", ItemID = 1353298668 } } }; private readonly Dictionary _upgradePathType = new Dictionary { { "assets/prefabs/building/door.double.hinged/door.double.hinged.wood.prefab", UpgradePathType.DoubleMetalTier }, { "assets/prefabs/building/door.hinged/door.hinged.wood.prefab", UpgradePathType.SingleMetalTier }, { "assets/prefabs/building/door.double.hinged/door.double.hinged.metal.prefab", UpgradePathType.DoubleTopTier }, { "assets/prefabs/building/door.hinged/door.hinged.metal.prefab", UpgradePathType.SingleTopTier }, { "assets/prefabs/building/wall.frame.garagedoor/wall.frame.garagedoor.prefab", UpgradePathType.DoubleTopTier }, }; private readonly List _repairTools = new List { "toolgun", "hammer" }; private UpgradePathType GetUpgradeType(Door door) => _upgradePathType[door.PrefabName]; private bool HasUpgradePath(Door door) => _upgradePathType.ContainsKey(door.PrefabName); private enum UpgradePathType : byte { SingleMetalTier, DoubleMetalTier, SingleTopTier, DoubleTopTier, None, } private class UpgradePathItem { public int ItemID; public string Prefab; private List _itemCost; private ItemDefinition _itemDef; private string _missingItems; private List ItemCost { get { if (_itemCost == null) _itemCost = ItemDef.Blueprint.ingredients; return _itemCost; } } private ItemDefinition ItemDef { get { if (_itemDef == null) _itemDef = ItemManager.FindItemDefinition(ItemID); return _itemDef; } } public string MissingItems { get { if (_missingItems == null) _missingItems = string.Join("\n", ItemCost.Select(x => $"{x.itemDef.displayName.english} {x.amount}")); return _missingItems; } } public bool CanAffordUpgrade(BasePlayer player) { foreach (var itemAmount in ItemCost) { if (player.inventory.GetAmount(itemAmount.itemid) < itemAmount.amount) return false; } return true; } public void PayForUpgrade(BasePlayer player) { var collect = Facepunch.Pool.GetList(); foreach (var itemAmount in ItemCost) { player.inventory.Take(collect, itemAmount.itemid, (int) itemAmount.amount); player.Command($"note.inv {itemAmount.itemid} {itemAmount.amount}"); } foreach (var obj in collect) obj.Remove(); Facepunch.Pool.FreeList(ref collect); } public bool HasUnlocked(BasePlayer player) => player.blueprints.HasUnlocked(ItemDef); public void PerformUpgrade(Door door) { var buildingID = door.buildingID; var doorPos = door.transform.position; var doorRot = door.transform.rotation.eulerAngles; var baseLock = door.GetSlot(BaseEntity.Slot.Lock) as BaseLock; if (baseLock != null) baseLock.SetParent(null); var ownerID = door.OwnerID; DestroyEntity(door); door = (Door)GameManager.server.CreateEntity(Prefab, doorPos, Quaternion.Euler(doorRot)); door.OwnerID = ownerID; door.Spawn(); door.AttachToBuilding(buildingID); door.RefreshEntityLinks(); door.SendNetworkUpdate(); if (baseLock != null && door.canTakeLock) { baseLock.SetParent(door, baseLock.GetSlotAnchorName(BaseEntity.Slot.Lock)); door.SetSlot(BaseEntity.Slot.Lock, baseLock); } else { DestroyEntity(baseLock); } Effect.server.Run(UPGRADE_EFFECT, door, 0U, Vector3.zero, Vector3.zero); } } private bool ToggleUpgrade(string userID) { if (_toggles.Remove(userID)) return false; _toggles.Add(userID); return true; } #endregion #region Helpers private bool IsUpgradeBlocked(BasePlayer player, Door door) { if (_config.BuildingBlocked && !player.CanBuild()) { MessagePlayer(player.IPlayer, LangKeys.BuildingBlocked, player.UserIDString); return false; } if (_config.RaidBlocked && (bool)(NoEscape?.Call("IsRaidBlocked", player) ?? false)) { MessagePlayer(player.IPlayer, LangKeys.RaidBlocked, player.UserIDString); return true; } if (_config.RecentDamaged && door.SecondsSinceAttacked <= DAMAGE_COOLDOWN) { MessagePlayer(player.IPlayer, LangKeys.AttackBlocked, player.UserIDString, Math.Round(DAMAGE_COOLDOWN - door.SecondsSinceAttacked)); return false; } return false; } private bool IsRepairTool(BasePlayer player) => IsRepairTool(player.GetActiveItem()); private bool IsRepairTool(Item heldItem) => heldItem != null && _repairTools.Contains(heldItem.info.shortname); private static void DestroyEntity(BaseEntity entity) { if (entity != null && !entity.IsDestroyed) entity.Kill(); } #endregion #region Commands private void ToggleCommand(IPlayer player, string command, string[] args) { var enabled = ToggleUpgrade(player.Id); MessagePlayer(player, LangKeys.UpgradeToggle, player.Id, (enabled ? lang.GetMessage(LangKeys.Enabled, this, player.Id) : lang.GetMessage(LangKeys.Disable, this, player.Id))); } #endregion } }