Decompiled source of ChestUpgradeMod v1.2.1

ChestUpgradeMod.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ChestUpgradeMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Upgrade Valheim chests in place with a keypress.")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ChestUpgradeMod")]
[assembly: AssemblyTitle("ChestUpgradeMod")]
[assembly: AssemblyVersion("1.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
internal sealed class ConfigurationManagerAttributes
{
	public delegate void CustomHotkeyDrawerFunc(ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput);

	public bool? ShowRangeAsPercent;

	public Action<ConfigEntryBase> CustomDrawer;

	public CustomHotkeyDrawerFunc CustomHotkeyDrawer;

	public bool? Browsable;

	public string Category;

	public object DefaultValue;

	public bool? HideDefaultButton;

	public bool? HideSettingName;

	public string Description;

	public string DispName;

	public int? Order;

	public bool? ReadOnly;

	public bool? IsAdvanced;

	public Func<object, string> ObjToStr;

	public Func<string, object> StrToObj;
}
namespace ChestUpgradeMod
{
	[BepInPlugin("yumi.valheim.chestupgrade", "ChestUpgrade", "1.2.1")]
	public class ChestUpgradePlugin : BaseUnityPlugin
	{
		private class UpgradeRequirement
		{
			public string ItemPrefabName;

			public int Amount;
		}

		private class UpgradeRecipe
		{
			public string NextPrefabName;

			public List<UpgradeRequirement> Requirements;
		}

		[HarmonyPatch(typeof(Container), "GetHoverText")]
		private static class Container_GetHoverText_Patch
		{
			private static void Postfix(Container __instance, ref string __result)
			{
				//IL_0263: Unknown result type (might be due to invalid IL or missing references)
				if (!_showHoverText.Value || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid())
				{
					return;
				}
				StringBuilder stringBuilder = new StringBuilder(__result);
				bool flag = false;
				if (_hammerReplaceEnabled.Value)
				{
					GameObject selectedHammerChestPrefab = GetSelectedHammerChestPrefab(Player.m_localPlayer);
					if ((Object)(object)selectedHammerChestPrefab != (Object)null && Utils.GetPrefabName(selectedHammerChestPrefab) != Utils.GetPrefabName(((Component)__instance).gameObject))
					{
						Piece component2 = selectedHammerChestPrefab.GetComponent<Piece>();
						string value = (((Object)(object)component2 != (Object)null && !string.IsNullOrEmpty(component2.m_name)) ? TryLocalize(component2.m_name) : Utils.GetPrefabName(selectedHammerChestPrefab));
						stringBuilder.Append("\n[<color=yellow><b>Click</b></color>] Replace with ").Append(value);
						if ((Object)(object)component2 != (Object)null && component2.m_resources != null)
						{
							Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
							Requirement[] resources = component2.m_resources;
							foreach (Requirement val in resources)
							{
								if (!((Object)(object)val.m_resItem == (Object)null))
								{
									string name = ((Object)((Component)val.m_resItem).gameObject).name;
									int num = CountItem(inventory, name);
									string value2 = ((num >= val.m_amount) ? "#00ff00" : "#ff5555");
									stringBuilder.Append("\n  <color=").Append(value2).Append(">")
										.Append(GetItemDisplayName(name))
										.Append(" ")
										.Append(num)
										.Append("/")
										.Append(val.m_amount)
										.Append("</color>");
								}
							}
						}
						flag = true;
					}
				}
				if (!flag && _hotkeyUpgradeEnabled.Value)
				{
					UpgradeRecipe recipeFor = GetRecipeFor(__instance);
					if (recipeFor != null)
					{
						GameObject val2 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(recipeFor.NextPrefabName) : null);
						if ((Object)(object)val2 != (Object)null)
						{
							string value3 = recipeFor.NextPrefabName;
							Piece component3 = val2.GetComponent<Piece>();
							if ((Object)(object)component3 != (Object)null && !string.IsNullOrEmpty(component3.m_name))
							{
								value3 = TryLocalize(component3.m_name);
							}
							Inventory inventory2 = ((Humanoid)Player.m_localPlayer).GetInventory();
							stringBuilder.Append("\n[<color=yellow><b>").Append(_upgradeKey.Value).Append("</b></color>] Upgrade to ")
								.Append(value3);
							foreach (UpgradeRequirement requirement in recipeFor.Requirements)
							{
								int num2 = CountItem(inventory2, requirement.ItemPrefabName);
								string value4 = ((num2 >= requirement.Amount) ? "#00ff00" : "#ff5555");
								stringBuilder.Append("\n  <color=").Append(value4).Append(">")
									.Append(GetItemDisplayName(requirement.ItemPrefabName))
									.Append(" ")
									.Append(num2)
									.Append("/")
									.Append(requirement.Amount)
									.Append("</color>");
							}
						}
					}
				}
				__result = stringBuilder.ToString();
			}
		}

		public const string PluginGUID = "yumi.valheim.chestupgrade";

		public const string PluginName = "ChestUpgrade";

		public const string PluginVersion = "1.2.1";

		private const string WoodChest = "piece_chest_wood";

		private const string ReinforcedChest = "piece_chest";

		private const string BlackMetalChest = "piece_chest_blackmetal";

		private static readonly FieldInfo BuildPiecesField = AccessTools.Field(typeof(Player), "m_buildPieces");

		private static readonly FieldInfo PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost");

		internal static ManualLogSource Log;

		private static ConfigEntry<KeyCode> _upgradeKey;

		private static ConfigEntry<bool> _showHoverText;

		private static ConfigEntry<bool> _hotkeyUpgradeEnabled;

		private static ConfigEntry<bool> _hammerReplaceEnabled;

		private static ConfigEntry<bool> _debugLogging;

		private static ConfigEntry<float> _replaceDetectRadius;

		private static ConfigEntry<bool> _refundOnDowngrade;

		private static ConfigEntry<string> _tier1Requirements;

		private static ConfigEntry<string> _tier2Requirements;

		private static readonly Dictionary<string, UpgradeRecipe> Upgrades = new Dictionary<string, UpgradeRecipe>();

		private static readonly Dictionary<string, string> SharedNameCache = new Dictionary<string, string>();

		private Harmony _harmony;

		private static float _lastDebugLogTime;

		private static MethodInfo _localizeMethod;

		private static object _localizationInstance;

		private static bool _localizationChecked;

		private static int GetChestTier(string prefabName)
		{
			return prefabName switch
			{
				"piece_chest_wood" => 1, 
				"piece_chest" => 2, 
				"piece_chest_blackmetal" => 3, 
				_ => 0, 
			};
		}

		private void Awake()
		{
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Expected O, but got Unknown
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			_upgradeKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "UpgradeKey", (KeyCode)117, "Key to press while looking at a chest to upgrade it.");
			_showHoverText = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowHoverText", true, "Show the upgrade key and the required materials on the chest hover text.");
			_hotkeyUpgradeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableHotkeyUpgrade", true, "Enable the original hover+hotkey upgrade (Wood -> Reinforced -> Black Metal using the Requirements below).");
			_hammerReplaceEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableHammerReplace", true, "Enable replacing a hovered chest with whatever chest piece is currently selected on the Hammer, at the exact same spot, using that piece's own normal build cost. Works for upgrades AND downgrades; items that don't fit after a downgrade drop on the ground instead of being lost.");
			_debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Log hammer-replace detection state once a second, for troubleshooting. Turn off once things work.");
			_replaceDetectRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ReplaceDetectRadius", 0.5f, "How close (in meters) the placement ghost must be to an existing chest to trigger a replace instead of a normal placement. Keep this small — roughly one chest's own footprint — so it doesn't grab a different nearby chest.");
			_refundOnDowngrade = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RefundOnDowngrade", true, "When hammer-replacing a chest with a lower tier one (e.g. Black Metal -> Reinforced), drop the materials the OLD chest itself cost to build on the ground, since that value is being destroyed.");
			_tier1Requirements = ((BaseUnityPlugin)this).Config.Bind<string>("Requirements", "WoodToReinforced", "FineWood:10,Iron:2", "Materials to upgrade Wood Chest -> Reinforced Chest. Format: ItemPrefabName:Amount,ItemPrefabName:Amount");
			_tier2Requirements = ((BaseUnityPlugin)this).Config.Bind<string>("Requirements", "ReinforcedToBlackMetal", "Wood:10,Tar:2,BlackMetal:6", "Materials to upgrade Reinforced Chest -> Black Metal Chest. Format: ItemPrefabName:Amount,ItemPrefabName:Amount");
			_tier1Requirements.SettingChanged += delegate
			{
				BuildUpgradeTable();
			};
			_tier2Requirements.SettingChanged += delegate
			{
				BuildUpgradeTable();
			};
			BuildUpgradeTable();
			if (BuildPiecesField == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find Player.m_buildPieces — hammer-replace feature will be disabled. Chain-based hotkey upgrade still works normally.");
			}
			if (PlacementGhostField == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find Player.m_placementGhost — hammer-replace feature will be disabled. Chain-based hotkey upgrade still works normally.");
			}
			_harmony = new Harmony("yumi.valheim.chestupgrade");
			_harmony.PatchAll(typeof(ChestUpgradePlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} v{1} loaded. Upgrade key: {2}", "ChestUpgrade", "1.2.1", _upgradeKey.Value));
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private static void BuildUpgradeTable()
		{
			Upgrades.Clear();
			Upgrades["piece_chest_wood"] = new UpgradeRecipe
			{
				NextPrefabName = "piece_chest",
				Requirements = ParseRequirements(_tier1Requirements.Value)
			};
			Upgrades["piece_chest"] = new UpgradeRecipe
			{
				NextPrefabName = "piece_chest_blackmetal",
				Requirements = ParseRequirements(_tier2Requirements.Value)
			};
		}

		private static List<UpgradeRequirement> ParseRequirements(string raw)
		{
			List<UpgradeRequirement> list = new List<UpgradeRequirement>();
			if (string.IsNullOrEmpty(raw))
			{
				return list;
			}
			string[] array = raw.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(':');
				if (array2.Length == 2 && int.TryParse(array2[1].Trim(), out var result))
				{
					list.Add(new UpgradeRequirement
					{
						ItemPrefabName = array2[0].Trim(),
						Amount = result
					});
				}
			}
			return list;
		}

		private void Update()
		{
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (_debugLogging.Value && Time.time - _lastDebugLogTime > 1f)
			{
				_lastDebugLogTime = Time.time;
				GameObject selectedHammerChestPrefab = GetSelectedHammerChestPrefab(localPlayer);
				Container targetContainer = GetTargetContainer(localPlayer);
				object? obj = PlacementGhostField?.GetValue(localPlayer);
				GameObject val = (GameObject)((obj is GameObject) ? obj : null);
				string text = (((Object)(object)val != (Object)null) ? ((object)val.transform.position/*cast due to .constrained prefix*/).ToString() : "n/a");
				string text2 = ((object)((Component)localPlayer).transform.position/*cast due to .constrained prefix*/).ToString();
				Log.LogInfo((object)string.Format("[DEBUG] InPlaceMode={0} selectedChestPrefab={1} containerNearGhost={2} ghostPos={3} playerPos={4} placementGhostFieldFound={5} buildPiecesFieldFound={6}", ((Character)localPlayer).InPlaceMode(), ((Object)(object)selectedHammerChestPrefab != (Object)null) ? ((Object)selectedHammerChestPrefab).name : "null", ((Object)(object)targetContainer != (Object)null) ? ((Object)targetContainer).name : "null", text, text2, PlacementGhostField != null, BuildPiecesField != null));
			}
			if (_hammerReplaceEnabled.Value && ((Character)localPlayer).InPlaceMode() && ZInput.GetButtonDown("Attack"))
			{
				if (_debugLogging.Value)
				{
					Log.LogInfo((object)"[DEBUG] Attack pressed while InPlaceMode=true, attempting replace.");
				}
				TryHammerReplaceOnClick(localPlayer);
			}
			if (!Input.GetKeyDown(_upgradeKey.Value) || IsUiBlocking())
			{
				return;
			}
			GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject();
			if (!((Object)(object)hoverObject == (Object)null))
			{
				Container componentInParent = hoverObject.GetComponentInParent<Container>();
				if (!((Object)(object)componentInParent == (Object)null) && _hotkeyUpgradeEnabled.Value)
				{
					TryUpgradeHoveredChest(localPlayer, componentInParent);
				}
			}
		}

		private static GameObject GetSelectedHammerChestPrefab(Player player)
		{
			if (BuildPiecesField == null)
			{
				return null;
			}
			object? value = BuildPiecesField.GetValue(player);
			PieceTable val = (PieceTable)((value is PieceTable) ? value : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Piece selectedPiece = val.GetSelectedPiece();
			if ((Object)(object)selectedPiece == (Object)null)
			{
				return null;
			}
			if ((Object)(object)((Component)selectedPiece).GetComponent<Container>() == (Object)null)
			{
				return null;
			}
			return ((Component)selectedPiece).gameObject;
		}

		private static Container GetTargetContainer(Player player, bool verbose = false)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			object? obj = PlacementGhostField?.GetValue(player);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				RaycastHit[] array = Physics.RaycastAll(new Ray(((Component)main).transform.position, ((Component)main).transform.forward), 10f);
				Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
				RaycastHit[] array2;
				if (verbose && _debugLogging.Value)
				{
					List<string> list = new List<string>();
					array2 = array;
					for (int num = 0; num < array2.Length; num++)
					{
						RaycastHit val2 = array2[num];
						bool flag = (Object)(object)val != (Object)null && ((RaycastHit)(ref val2)).transform.IsChildOf(val.transform);
						list.Add(string.Format("{0}@{1:F2}{2}", ((Object)((RaycastHit)(ref val2)).collider).name, ((RaycastHit)(ref val2)).distance, flag ? "(ghost-self)" : ""));
					}
					Log.LogInfo((object)string.Format("[DEBUG] RaycastAll found {0} hits: [{1}]", array.Length, string.Join(", ", list)));
				}
				array2 = array;
				for (int num = 0; num < array2.Length; num++)
				{
					RaycastHit val3 = array2[num];
					if ((Object)(object)val != (Object)null && ((RaycastHit)(ref val3)).transform.IsChildOf(val.transform))
					{
						continue;
					}
					Container componentInParent = ((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent<Container>();
					if ((Object)(object)componentInParent != (Object)null)
					{
						if (verbose && _debugLogging.Value)
						{
							Log.LogInfo((object)("[DEBUG] Raycast resolved container: " + ((Object)componentInParent).name));
						}
						return componentInParent;
					}
				}
			}
			else if (verbose && _debugLogging.Value)
			{
				Log.LogInfo((object)"[DEBUG] Camera.main is null, skipping raycast.");
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Vector3 position = val.transform.position;
			float value = _replaceDetectRadius.Value;
			Collider[] array3 = Physics.OverlapSphere(position, value);
			Container val4 = null;
			float num2 = float.MaxValue;
			Collider[] array4 = array3;
			foreach (Collider val5 in array4)
			{
				if (((Component)val5).transform.IsChildOf(val.transform))
				{
					continue;
				}
				Container componentInParent2 = ((Component)val5).GetComponentInParent<Container>();
				if (!((Object)(object)componentInParent2 == (Object)null))
				{
					float num3 = Vector3.Distance(position, ((Component)componentInParent2).transform.position);
					if (num3 < num2)
					{
						num2 = num3;
						val4 = componentInParent2;
					}
				}
			}
			if (verbose && _debugLogging.Value)
			{
				Log.LogInfo((object)string.Format("[DEBUG] Fallback: ghost at {0}, radius={1}, closest container={2} dist={3}", position, value, ((Object)(object)val4 != (Object)null) ? ((Object)val4).name : "null", ((Object)(object)val4 != (Object)null) ? num2.ToString("F2") : "n/a"));
			}
			return val4;
		}

		private void TryHammerReplaceOnClick(Player player)
		{
			GameObject selectedHammerChestPrefab = GetSelectedHammerChestPrefab(player);
			if ((Object)(object)selectedHammerChestPrefab == (Object)null)
			{
				if (_debugLogging.Value)
				{
					Log.LogInfo((object)"[DEBUG] No chest selected on hammer, letting click through normally.");
				}
				return;
			}
			Container targetContainer = GetTargetContainer(player, verbose: true);
			if ((Object)(object)targetContainer == (Object)null)
			{
				if (_debugLogging.Value)
				{
					Log.LogInfo((object)"[DEBUG] No existing chest near the placement ghost, letting click through normally.");
				}
				return;
			}
			if (_debugLogging.Value)
			{
				string prefabName = Utils.GetPrefabName(((Component)targetContainer).gameObject);
				string prefabName2 = Utils.GetPrefabName(selectedHammerChestPrefab);
				Log.LogInfo((object)("[DEBUG] Replacing '" + prefabName + "' with '" + prefabName2 + "'."));
			}
			TryReplaceWithHammerPiece(player, targetContainer, selectedHammerChestPrefab);
		}

		private static bool IsUiBlocking()
		{
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return true;
			}
			if (Console.IsVisible())
			{
				return true;
			}
			if (TextInput.IsVisible())
			{
				return true;
			}
			if (InventoryGui.IsVisible())
			{
				return true;
			}
			if (Menu.IsVisible())
			{
				return true;
			}
			return false;
		}

		private void TryUpgradeHoveredChest(Player player, Container container)
		{
			UpgradeRecipe recipeFor = GetRecipeFor(container);
			if (recipeFor == null)
			{
				((Character)player).Message((MessageType)2, "This chest cannot be upgraded further.", 0, (Sprite)null, false);
				return;
			}
			if (container.IsInUse())
			{
				((Character)player).Message((MessageType)2, "That chest is in use.", 0, (Sprite)null, false);
				return;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(recipeFor.NextPrefabName);
			if ((Object)(object)prefab == (Object)null)
			{
				Log.LogError((object)("Upgrade target prefab '" + recipeFor.NextPrefabName + "' was not found in ZNetScene."));
				((Character)player).Message((MessageType)2, "Upgrade failed: target chest prefab missing.", 0, (Sprite)null, false);
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			foreach (UpgradeRequirement requirement in recipeFor.Requirements)
			{
				int num = CountItem(inventory, requirement.ItemPrefabName);
				if (num < requirement.Amount)
				{
					((Character)player).Message((MessageType)2, $"Missing {GetItemDisplayName(requirement.ItemPrefabName)} ({num}/{requirement.Amount})", 0, (Sprite)null, false);
					return;
				}
			}
			foreach (UpgradeRequirement requirement2 in recipeFor.Requirements)
			{
				RemoveItem(inventory, requirement2.ItemPrefabName, requirement2.Amount);
			}
			ReplaceChest(container, prefab, "Chest upgraded!");
		}

		private void TryReplaceWithHammerPiece(Player player, Container oldContainer, GameObject newPrefab)
		{
			//IL_034e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0359: Unknown result type (might be due to invalid IL or missing references)
			string prefabName = Utils.GetPrefabName(((Component)oldContainer).gameObject);
			string prefabName2 = Utils.GetPrefabName(newPrefab);
			if (prefabName == prefabName2)
			{
				if (_debugLogging.Value)
				{
					Log.LogInfo((object)"[DEBUG] Same prefab type, nothing to replace.");
				}
				return;
			}
			if (oldContainer.IsInUse())
			{
				if (_debugLogging.Value)
				{
					Log.LogInfo((object)"[DEBUG] Bailing: chest is in use.");
				}
				((Character)player).Message((MessageType)2, "That chest is in use.", 0, (Sprite)null, false);
				return;
			}
			Piece component = newPrefab.GetComponent<Piece>();
			if ((Object)(object)component == (Object)null || component.m_resources == null || component.m_resources.Length == 0)
			{
				if (_debugLogging.Value)
				{
					Log.LogInfo((object)string.Format("[DEBUG] Bailing: piece={0} resources={1}", component, (!((Object)(object)component != (Object)null)) ? "n/a" : component.m_resources?.Length.ToString()));
				}
				((Character)player).Message((MessageType)2, "That piece has no build cost, can't replace with it.", 0, (Sprite)null, false);
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			Requirement[] resources;
			if (_debugLogging.Value)
			{
				resources = component.m_resources;
				foreach (Requirement val in resources)
				{
					string text = (((Object)(object)val.m_resItem != (Object)null) ? ((Object)((Component)val.m_resItem).gameObject).name : "NULL_ITEM");
					int num = (((Object)(object)val.m_resItem != (Object)null) ? CountItem(inventory, text) : (-1));
					Log.LogInfo((object)$"[DEBUG] Requirement: {text} need={val.m_amount} have={num}");
				}
			}
			resources = component.m_resources;
			foreach (Requirement val2 in resources)
			{
				if ((Object)(object)val2.m_resItem == (Object)null)
				{
					continue;
				}
				string name = ((Object)((Component)val2.m_resItem).gameObject).name;
				int num2 = CountItem(inventory, name);
				if (num2 < val2.m_amount)
				{
					if (_debugLogging.Value)
					{
						Log.LogInfo((object)$"[DEBUG] Bailing: missing {name} ({num2}/{val2.m_amount}).");
					}
					((Character)player).Message((MessageType)2, $"Missing {GetItemDisplayName(name)} ({num2}/{val2.m_amount})", 0, (Sprite)null, false);
					return;
				}
			}
			resources = component.m_resources;
			foreach (Requirement val3 in resources)
			{
				if (!((Object)(object)val3.m_resItem == (Object)null))
				{
					RemoveItem(inventory, ((Object)((Component)val3.m_resItem).gameObject).name, val3.m_amount);
				}
			}
			Piece component2 = newPrefab.GetComponent<Piece>();
			string text2 = (((Object)(object)component2 != (Object)null && !string.IsNullOrEmpty(component2.m_name)) ? TryLocalize(component2.m_name) : prefabName2);
			if (GetChestTier(prefabName) > 0 && GetChestTier(prefabName2) > 0 && GetChestTier(prefabName2) < GetChestTier(prefabName) && _refundOnDowngrade.Value)
			{
				if (_debugLogging.Value)
				{
					Log.LogInfo((object)("[DEBUG] Downgrade detected (" + prefabName + " -> " + prefabName2 + "), refunding old chest's build cost."));
				}
				RefundChestResources(prefabName, ((Component)oldContainer).transform.position, ((Component)oldContainer).transform.rotation);
			}
			if (_debugLogging.Value)
			{
				Log.LogInfo((object)"[DEBUG] Requirements met, calling ReplaceChest.");
			}
			ReplaceChest(oldContainer, newPrefab, "Chest replaced with " + text2 + "!");
		}

		private void RefundChestResources(string prefabName, Vector3 pos, Quaternion rot)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabName) : null);
			Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
			if ((Object)(object)val2 == (Object)null || val2.m_resources == null)
			{
				return;
			}
			Requirement[] resources = val2.m_resources;
			foreach (Requirement val3 in resources)
			{
				if (!((Object)(object)val3.m_resItem == (Object)null))
				{
					try
					{
						ItemData obj = val3.m_resItem.m_itemData.Clone();
						obj.m_dropPrefab = ((Component)val3.m_resItem).gameObject;
						ItemDrop.DropItem(obj, val3.m_amount, pos + Vector3.up, rot);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"Failed to refund {((Object)((Component)val3.m_resItem).gameObject).name} on downgrade: {arg}");
					}
				}
			}
		}

		private static UpgradeRecipe GetRecipeFor(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			string prefabName = Utils.GetPrefabName(((Component)container).gameObject);
			if (!Upgrades.TryGetValue(prefabName, out var value))
			{
				return null;
			}
			return value;
		}

		private void ReplaceChest(Container oldContainer, GameObject nextPrefab, string successMessage)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			if (_debugLogging.Value)
			{
				Log.LogInfo((object)"[DEBUG] ReplaceChest entered.");
			}
			ZNetView component = ((Component)oldContainer).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				Log.LogError((object)"Old chest has no valid ZNetView, aborting upgrade.");
				return;
			}
			component.ClaimOwnership();
			Vector3 position = ((Component)oldContainer).transform.position;
			Quaternion rotation = ((Component)oldContainer).transform.rotation;
			Inventory inventory = oldContainer.GetInventory();
			List<ItemData> list = new List<ItemData>(inventory.GetAllItems());
			inventory.RemoveAll();
			ZNetScene.instance.Destroy(((Component)oldContainer).gameObject);
			GameObject val = Object.Instantiate<GameObject>(nextPrefab, position, rotation);
			Container component2 = val.GetComponent<Container>();
			if ((Object)(object)component2 == (Object)null)
			{
				Log.LogError((object)"Upgraded prefab has no Container component, aborting upgrade.");
				ZNetScene.instance.Destroy(val);
				return;
			}
			Piece component3 = val.GetComponent<Piece>();
			if ((Object)(object)component3 != (Object)null)
			{
				EffectList placeEffect = component3.m_placeEffect;
				if (placeEffect != null)
				{
					placeEffect.Create(position, rotation, (Transform)null, 1f, -1, default(ZDOID));
				}
			}
			Inventory inventory2 = component2.GetInventory();
			foreach (ItemData item in list)
			{
				if (!inventory2.AddItem(item))
				{
					ItemDrop.DropItem(item, item.m_stack, position + Vector3.up, rotation);
				}
			}
			((Character)Player.m_localPlayer).Message((MessageType)2, successMessage, 0, (Sprite)null, false);
			if (_debugLogging.Value)
			{
				Log.LogInfo((object)"[DEBUG] ReplaceChest completed successfully.");
			}
		}

		private static string GetSharedName(string itemPrefabName)
		{
			if (SharedNameCache.TryGetValue(itemPrefabName, out var value))
			{
				return value;
			}
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemPrefabName);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			string text = (((Object)(object)val != (Object)null) ? val.m_itemData.m_shared.m_name : null);
			if (text != null)
			{
				SharedNameCache[itemPrefabName] = text;
			}
			return text;
		}

		internal static string GetItemDisplayName(string itemPrefabName)
		{
			return TryLocalize(GetSharedName(itemPrefabName) ?? itemPrefabName);
		}

		internal static string TryLocalize(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			if (!_localizationChecked)
			{
				_localizationChecked = true;
				try
				{
					Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
					for (int i = 0; i < assemblies.Length; i++)
					{
						Type type = assemblies[i].GetType("Localization");
						if (type == null)
						{
							continue;
						}
						object obj = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) ?? type.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
						if (obj != null)
						{
							MethodInfo method = type.GetMethod("Localize", new Type[1] { typeof(string) });
							if (!(method == null))
							{
								_localizationInstance = obj;
								_localizeMethod = method;
								break;
							}
						}
					}
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Could not hook into Localization, names will show as raw tokens: " + ex.Message));
				}
			}
			if (_localizeMethod == null || _localizationInstance == null)
			{
				return text;
			}
			try
			{
				return (string)_localizeMethod.Invoke(_localizationInstance, new object[1] { text });
			}
			catch
			{
				return text;
			}
		}

		private static bool Matches(ItemData item, string itemPrefabName, string sharedName)
		{
			if (item?.m_shared == null)
			{
				return false;
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == itemPrefabName)
			{
				return true;
			}
			if (sharedName != null)
			{
				return item.m_shared.m_name == sharedName;
			}
			return false;
		}

		internal static int CountItem(Inventory inv, string itemPrefabName)
		{
			if (inv == null)
			{
				return 0;
			}
			string sharedName = GetSharedName(itemPrefabName);
			int num = 0;
			foreach (ItemData allItem in inv.GetAllItems())
			{
				if (Matches(allItem, itemPrefabName, sharedName))
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		private static void RemoveItem(Inventory inv, string itemPrefabName, int amount)
		{
			string sharedName = GetSharedName(itemPrefabName);
			int num = amount;
			foreach (ItemData item in new List<ItemData>(inv.GetAllItems()))
			{
				if (num <= 0)
				{
					break;
				}
				if (Matches(item, itemPrefabName, sharedName))
				{
					int num2 = Mathf.Min(item.m_stack, num);
					inv.RemoveItem(item, num2);
					num -= num2;
				}
			}
		}
	}
}