C#:
using System.Collections.Generic;
using System.Linq;
using System;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Newtonsoft.Json;
using UnityEngine;
namespace Oxide.Plugins {
[Info("Door Upgrade", "Bazz3l", "1.0.2")]
[Description("Allows the ability for players to upgrade doors to the next tier using hammer or toolgun")]
internal class DoorUpgrade : CovalencePlugin {
[PluginReference]
private Plugin NoEscape;
#region Fields
private const string PERM_USE = "doorupgrade.use";
private const string UPGRADE_EFFECT = "assets/bundled/prefabs/fx/build/promote_toptier.prefab";
private const float DAMAGE_COOLDOWN = 30f;
private HashSet<string> _playerToggles = new();
private static PluginConfig _config;
#endregion
#region Lang
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
{
LangKeys.Prefix, "<color=#90fce7>Door Upgrade</color>: "
}
, {
LangKeys.BlueprintRequired, "missing blueprint requirement."
}
, {
LangKeys.ResourcesRequired, "missing upgrade items.\n{0}"
}
, {
LangKeys.AttackBlocked, "unable to upgrade, upgradable in: <color=#90fce7>{0}</color>s."
}
, {
LangKeys.BuildingBlocked, "you are currently building blocked."
}
, {
LangKeys.RaidBlocked, "you are currently raid/combat blocked."
}
, {
LangKeys.AlreadyTopTier, "door is currently top tier."
}
, {
LangKeys.ToolEnabled, "was <color=#affc62>Enabled</color>"
}
, {
LangKeys.ToolDisable, "was <color=#fc3d3d>Disabled</color>"
}
}
, this);
}
private class LangKeys {
public const string Prefix = "Prefix";
public const string BlueprintRequired = "BlueprintRequired";
public const string ResourcesRequired = "ResourcesRequired";
public const string BuildingBlocked = "BuildingBlocked";
public const string AttackBlocked = "AttackBlocked";
public const string RaidBlocked = "NoEscapeBlocked";
public const string AlreadyTopTier = "AlreadyTopTier";
public const string ToolEnabled = "ToolEnabled";
public const string ToolDisable = "ToolDisable";
}
private void MessagePlayer(IPlayer player, string langKey, string userID = "", params object[] args) {
if (player == null || !player.IsConnected) return;
string message = args?.Length > 0 ? string.Format(lang.GetMessage(langKey, this, userID), args) : lang.GetMessage(langKey, this, userID);
player.Message(lang.GetMessage(LangKeys.Prefix, this, userID) + message);
}
#endregion
#region Config
protected override void LoadDefaultConfig() => _config = PluginConfig.DefaultConfig();
protected override void LoadConfig() {
base.LoadConfig();
try {
_config = Config.ReadObject<PluginConfig>();
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("DisableOnItemSwitch: automatically disable when item is switch from repair tool")]
public bool DisableOnItemSwitch;
[JsonProperty("RequiresResources: player must have resources on them to upgrade")]
public bool RequiresResources;
[JsonProperty("RequiresBlueprint: player must have required blueprint unlocked")]
public bool RequiresBlueprint;
[JsonProperty("BlockOnRecentlyDamaged: blocked upgrade when entity has recently taken damage")]
public bool BlockOnRecentlyDamaged;
[JsonProperty("BlockOnBuildingBlocked: blocked upgrade when player is building blocked")]
public bool BlockOnBuildingBlocked;
[JsonProperty("BlockOnRaidBlocked: blocked upgrade when raid blocked")]
public bool BlockOnRaidBlocked;
public static PluginConfig DefaultConfig() {
return new PluginConfig {
ChatCommand = "dup", DisableOnItemSwitch = true, RequiresResources = true, RequiresBlueprint = true, BlockOnRecentlyDamaged = true, BlockOnBuildingBlocked = true, BlockOnRaidBlocked = 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 Unload() {
_playerToggles.Clear();
_playerToggles = null;
}
private void OnPlayerDisconnected(BasePlayer player) => _playerToggles.Remove(player.UserIDString);
private void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem) {
if (player == null || !player.userID.IsSteamId() || !_playerToggles.Contains(player.UserIDString)) return;
if (newItem == null || IsUpgradeTool(newItem)) return;
_playerToggles.Remove(player.UserIDString);
SubscribeToHooks(_playerToggles.Count);
MessagePlayer(player.IPlayer, LangKeys.ToolDisable, player.UserIDString);
}
private object OnHammerHit(BasePlayer player, HitInfo info) {
if (info?.HitEntity is not Door door || !player.userID.IsSteamId() || !_playerToggles.Contains(player.UserIDString)) return null;
if (IsUpgradeBlocked(player, door)) return false;
if (!_upgradePrefabsToType.TryGetValue(door.PrefabName, out UpgradeType upgradeType) || upgradeType == UpgradeType.None) {
MessagePlayer(player.IPlayer, LangKeys.AlreadyTopTier, player.UserIDString);
return false;
}
if (!_upgradeTypeToPrefab.TryGetValue(upgradeType, out UpgradeItem upgradeItem)) return false;
if (_config.RequiresBlueprint && !upgradeItem.HasUnlocked(player)) {
MessagePlayer(player.IPlayer, LangKeys.BlueprintRequired, player.UserIDString);
return false;
}
if (_config.RequiresResources) {
if (!upgradeItem.CanAffordUpgrade(player)) {
MessagePlayer(player.IPlayer, LangKeys.ResourcesRequired, player.UserIDString, upgradeItem.MissingItems);
return false;
}
upgradeItem.PayForUpgrade(player);
}
upgradeItem.PerformUpgrade(door);
return false;
}
#endregion
#region Door Upgrade
private readonly Dictionary<UpgradeType, UpgradeItem> _upgradeTypeToPrefab = new() {
{
UpgradeType.DoubleMetalTier, new UpgradeItem {
Prefab = "assets/prefabs/building/door.double.hinged/door.double.hinged.metal.prefab", ItemID = 1390353317
}
}
, {
UpgradeType.SingleMetalTier, new UpgradeItem {
Prefab = "assets/prefabs/building/door.hinged/door.hinged.metal.prefab", ItemID = -2067472972
}
}
, {
UpgradeType.DoubleTopTier, new UpgradeItem {
Prefab = "assets/prefabs/building/door.double.hinged/door.double.hinged.toptier.prefab", ItemID = 1221063409
}
}
, {
UpgradeType.SingleTopTier, new UpgradeItem {
Prefab = "assets/prefabs/building/door.hinged/door.hinged.toptier.prefab", ItemID = 1353298668
}
}
}
;
private readonly Dictionary<string, UpgradeType> _upgradePrefabsToType = new() {
{
"assets/prefabs/building/door.double.hinged/door.double.hinged.wood.prefab", UpgradeType.DoubleMetalTier
}
, {
"assets/prefabs/building/door.hinged/door.hinged.wood.prefab", UpgradeType.SingleMetalTier
}
, {
"assets/prefabs/building/door.double.hinged/door.double.hinged.metal.prefab", UpgradeType.DoubleTopTier
}
, {
"assets/prefabs/building/door.hinged/door.hinged.metal.prefab", UpgradeType.SingleTopTier
}
, {
"assets/prefabs/building/wall.frame.garagedoor/wall.frame.garagedoor.prefab", UpgradeType.DoubleTopTier
}
,
}
;
private enum UpgradeType : byte {
SingleMetalTier, DoubleMetalTier, SingleTopTier, DoubleTopTier, None,
}
private class UpgradeItem {
public int ItemID;
public string Prefab;
private string _missingItems;
public List<ItemAmount> ItemCost {
get {
return _itemCost ??= ItemDef.Blueprint.ingredients;
}
}
private ItemDefinition _itemDef;
public ItemDefinition ItemDef {
get {
return _itemDef ??= ItemManager.FindItemDefinition(ItemID);
}
}
private List<ItemAmount> _itemCost;
public string MissingItems {
get {
return _missingItems ??= string.Join("\n", ItemCost.Select(x => $"{x.itemDef.displayName.english} {x.amount}"));
}
}
public bool CanAffordUpgrade(BasePlayer player) {
foreach (ItemAmount itemAmount in ItemCost) {
if (player.inventory.GetAmount(itemAmount.itemid) < itemAmount.amount) return false;
}
return true;
}
public void PayForUpgrade(BasePlayer player) {
List<Item> collect = Facepunch.Pool.Get<List<Item>>();
foreach (ItemAmount itemAmount in ItemCost) {
player.inventory.Take(collect, itemAmount.itemid, (int) itemAmount.amount);
player.Command($"note.inv {itemAmount.itemid} {itemAmount.amount}");
}
foreach (Item obj in collect) obj.Remove();
Facepunch.Pool.FreeUnmanaged(ref collect);
}
public bool HasUnlocked(BasePlayer player) => player.blueprints.HasUnlocked(ItemDef);
public void PerformUpgrade(BaseEntity baseEntity) {
Vector3 localPosition = baseEntity.transform.localPosition;
Quaternion localRotation = baseEntity.transform.localRotation;
BaseEntity parentEntity = baseEntity.GetParentEntity();
ulong ownerId = baseEntity.OwnerID;
EntityRef[] slots = baseEntity.GetSlots();
List<ChildPreserveInfo> preserveInfoList = Facepunch.Pool.Get<List<ChildPreserveInfo>>();
try {
bool flag = baseEntity.children.Count > 0;
if (flag) {
foreach (BaseEntity childEntity in baseEntity.children) {
preserveInfoList.Add(new ChildPreserveInfo(childEntity, childEntity.parentBone, childEntity.transform.localPosition, childEntity.transform.localRotation) );
}
foreach (ChildPreserveInfo childPreserveInfo in preserveInfoList) childPreserveInfo.TargetEntity.SetParent(null, true);
}
baseEntity.Kill();
baseEntity = GameManager.server.CreateEntity(Prefab, parentEntity != null ? parentEntity.transform.TransformPoint(localPosition) : localPosition, parentEntity != null ? parentEntity.transform.rotation * localRotation : localRotation);
baseEntity.SetParent(parentEntity);
baseEntity.transform.localPosition = localPosition;
baseEntity.transform.localRotation = localRotation;
baseEntity.OwnerID = ownerId;
if (baseEntity is DecayEntity decayEntity) decayEntity.AttachToBuilding(null);
baseEntity.Spawn();
if (baseEntity is BaseCombatEntity baseCombatEntity) baseCombatEntity.SetHealth(baseCombatEntity.MaxHealth());
if (flag) {
foreach (ChildPreserveInfo childPreserveInfo in preserveInfoList) {
childPreserveInfo.TargetEntity.SetParent(baseEntity, childPreserveInfo.TargetBone, true);
childPreserveInfo.TargetEntity.transform.localPosition = childPreserveInfo.LocalPosition;
childPreserveInfo.TargetEntity.transform.localRotation = childPreserveInfo.LocalRotation;
childPreserveInfo.TargetEntity.SendNetworkUpdate();
}
baseEntity.SetSlots(slots);
}
}
catch (Exception e) {
//
}
Facepunch.Pool.FreeUnmanaged(ref preserveInfoList);
Effect.server.Run(UPGRADE_EFFECT, baseEntity, 0U, Vector3.zero, Vector3.zero);
}
}
private bool IsUpgradeBlocked(BasePlayer player, Door door) {
if (_config.BlockOnBuildingBlocked && !player.CanBuild()) {
MessagePlayer(player.IPlayer, LangKeys.BuildingBlocked, player.UserIDString);
return false;
}
if (_config.BlockOnRaidBlocked && (bool)(NoEscape?.Call("IsRaidBlocked", player) ?? false)) {
MessagePlayer(player.IPlayer, LangKeys.RaidBlocked, player.UserIDString);
return true;
}
if (_config.BlockOnRecentlyDamaged && door.SecondsSinceAttacked <= DAMAGE_COOLDOWN) {
MessagePlayer(player.IPlayer, LangKeys.AttackBlocked, player.UserIDString, Math.Round(DAMAGE_COOLDOWN - door.SecondsSinceAttacked));
return false;
}
return false;
}
private bool IsUpgradeTool(Item item) => item?.info.itemid is 1803831286 or 200773292;
private struct ChildPreserveInfo {
public BaseEntity TargetEntity;
public uint TargetBone;
public Vector3 LocalPosition;
public Quaternion LocalRotation;
public ChildPreserveInfo(BaseEntity targetEntity, uint targetBone, Vector3 localPosition, Quaternion localRotation) {
TargetEntity = targetEntity;
TargetBone = targetBone;
LocalPosition = localPosition;
LocalRotation = localRotation;
}
}
#endregion
#region Hook Subscriber
private void SubscribeToHooks(int count) {
if (count == 1) {
Subscribe("OnHammerHit");
if (_config.DisableOnItemSwitch) Subscribe("OnActiveItemChanged");
return;
}
if (count == 0) {
Unsubscribe("OnHammerHit");
if (_config.DisableOnItemSwitch) Unsubscribe("OnActiveItemChanged");
}
}
#endregion
#region Commands
private void ToggleCommand(IPlayer player, string command, string[] args) {
if (_playerToggles.Remove(player.Id)) {
SubscribeToHooks(_playerToggles.Count);
MessagePlayer(player, LangKeys.ToolDisable, player.Id);
}
else {
_playerToggles.Add(player.Id);
SubscribeToHooks(_playerToggles.Count);
MessagePlayer(player, LangKeys.ToolEnabled, player.Id);
}
}
#endregion
}
}