Decompiled source of ChestProgression v1.0.1

GrillspettChestProgression.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[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;
		}
	}
}
namespace GrillspettChestProgression
{
	internal sealed class ChestUpgradeDef
	{
		public readonly string Id;

		public readonly string BarPrefabName;

		public string GlobalKey => "GCP_Upgrade_" + Id;

		public string UpgradeItemPrefabName => "GCP_ChestUpgrade_" + Id;

		public string DisplayName => "Chest Upgrade (" + Id + ")";

		public ChestUpgradeDef(string id, string barPrefabName)
		{
			Id = id;
			BarPrefabName = barPrefabName;
		}
	}
	internal static class ChestTiers
	{
		public const string DefaultTargetChestPrefab = "piece_chest_wood";

		public static readonly ChestUpgradeDef[] All = new ChestUpgradeDef[5]
		{
			new ChestUpgradeDef("Bronze", "Bronze"),
			new ChestUpgradeDef("Iron", "Iron"),
			new ChestUpgradeDef("Silver", "Silver"),
			new ChestUpgradeDef("BlackMetal", "BlackMetal"),
			new ChestUpgradeDef("Flametal", "Flametal")
		};

		public static readonly (int Width, int Height)[] SizeSteps = new(int, int)[5]
		{
			(6, 3),
			(6, 4),
			(8, 4),
			(8, 5),
			(8, 6)
		};

		public static bool IsUpgradeKey(string globalKey)
		{
			ChestUpgradeDef[] all = All;
			for (int i = 0; i < all.Length; i++)
			{
				if (string.Equals(all[i].GlobalKey, globalKey, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		public static ChestUpgradeDef GetUpgradeForPrefabName(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return null;
			}
			ChestUpgradeDef[] all = All;
			foreach (ChestUpgradeDef chestUpgradeDef in all)
			{
				if (chestUpgradeDef.UpgradeItemPrefabName == prefabName)
				{
					return chestUpgradeDef;
				}
			}
			return null;
		}
	}
	internal static class ChestUpgradeState
	{
		private static readonly List<Container> TrackedChests = new List<Container>();

		public static bool IsTargetChest(Container container)
		{
			if ((Object)(object)container == (Object)null || (Object)(object)((Component)container).gameObject == (Object)null)
			{
				return false;
			}
			string text = ((Object)((Component)container).gameObject).name;
			if (text.EndsWith("(Clone)"))
			{
				text = text.Substring(0, text.Length - "(Clone)".Length);
			}
			return text == Plugin.TargetChestPrefab.Value;
		}

		public static void TrackContainer(Container container)
		{
			TrackedChests.Add(container);
		}

		public static int GetUnlockedUpgradeCount()
		{
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0;
			}
			int num = 0;
			ChestUpgradeDef[] all = ChestTiers.All;
			foreach (ChestUpgradeDef chestUpgradeDef in all)
			{
				if (instance.GetGlobalKey(chestUpgradeDef.GlobalKey))
				{
					num++;
				}
			}
			return num;
		}

		public static bool TryGetCurrentSize(out int width, out int height)
		{
			int unlockedUpgradeCount = GetUnlockedUpgradeCount();
			if (unlockedUpgradeCount <= 0)
			{
				width = 0;
				height = 0;
				return false;
			}
			int num = Math.Min(unlockedUpgradeCount, ChestTiers.SizeSteps.Length) - 1;
			(width, height) = ChestTiers.SizeSteps[num];
			return true;
		}

		public static void ApplyCurrentSizeBeforeAwake(Container container)
		{
			if (TryGetCurrentSize(out var width, out var height))
			{
				container.m_width = width;
				container.m_height = height;
			}
		}

		public static void ResizeAllLoadedChests()
		{
			if (!TryGetCurrentSize(out var width, out var height))
			{
				return;
			}
			for (int num = TrackedChests.Count - 1; num >= 0; num--)
			{
				Container val = TrackedChests[num];
				if ((Object)(object)val == (Object)null)
				{
					TrackedChests.RemoveAt(num);
				}
				else
				{
					Inventory inventory = val.GetInventory();
					if (inventory != null && (val.m_width != width || val.m_height != height))
					{
						val.m_width = width;
						val.m_height = height;
						inventory.m_width = width;
						inventory.m_height = height;
						inventory.m_onChanged?.Invoke();
					}
				}
			}
		}

		public static void DisableUnlockedRecipes()
		{
			ZoneSystem instance = ZoneSystem.instance;
			ObjectDB instance2 = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null)
			{
				return;
			}
			ChestUpgradeDef[] all = ChestTiers.All;
			foreach (ChestUpgradeDef chestUpgradeDef in all)
			{
				if (instance.GetGlobalKey(chestUpgradeDef.GlobalKey))
				{
					GameObject itemPrefab = instance2.GetItemPrefab(chestUpgradeDef.UpgradeItemPrefabName);
					ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>());
					Recipe val2 = (((Object)(object)val == (Object)null) ? null : instance2.GetRecipe(val.m_itemData));
					if ((Object)(object)val2 != (Object)null)
					{
						val2.m_enabled = false;
					}
				}
			}
		}
	}
	internal static class IconLoader
	{
		private static Sprite _upgradeTableIcon;

		public static Sprite UpgradeTableIcon()
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_upgradeTableIcon != (Object)null)
			{
				return _upgradeTableIcon;
			}
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string n) => n.EndsWith("UpgradeTableIcon.png"));
			if (text == null)
			{
				Plugin.Log.LogError((object)"Embedded resource 'UpgradeTableIcon.png' not found - the Chest Upgrade Table will keep the Workbench's build-menu icon.");
				return null;
			}
			byte[] array;
			using (Stream stream = executingAssembly.GetManifestResourceStream(text))
			{
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				array = memoryStream.ToArray();
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			MethodInfo method = typeof(ImageConversion).GetMethod("LoadImage", new Type[2]
			{
				typeof(Texture2D),
				typeof(byte[])
			});
			if (!(method != null) || !(bool)method.Invoke(null, new object[2] { val, array }))
			{
				Plugin.Log.LogError((object)"Failed to decode the embedded UpgradeTableIcon.png.");
				return null;
			}
			_upgradeTableIcon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
			return _upgradeTableIcon;
		}
	}
	[BepInPlugin("grillspett.valheim.chestprogression", "Grillspett Chest Progression", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "grillspett.valheim.chestprogression";

		public const string PluginName = "Grillspett Chest Progression";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<string> TargetChestPrefab;

		internal static ConfigEntry<int> FineWoodCost;

		internal static readonly Dictionary<string, ConfigEntry<int>> TierCosts = new Dictionary<string, ConfigEntry<int>>();

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			_harmony = new Harmony("grillspett.valheim.chestprogression");
			_harmony.PatchAll();
			PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable;
			Log.LogInfo((object)("Grillspett Chest Progression 1.0.0 loaded. Target chest prefab: '" + TargetChestPrefab.Value + "'."));
		}

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

		private void OnVanillaPrefabsAvailable()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable;
			Registration.RegisterAll();
		}

		private void BindConfig()
		{
			TargetChestPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("General", "TargetChestPrefab", "piece_chest_wood", Admin("Prefab name of the basic chest this mod upgrades. Only change this if the Chest Upgrade Table's recipes work but chests never actually grow - check the BepInEx log for this mod's registration lines first."));
			FineWoodCost = ((BaseUnityPlugin)this).Config.Bind<int>("Costs", "UpgradeTableFineWoodCost", 10, Admin("Fine Wood required to build the Chest Upgrade Table."));
			ChestUpgradeDef[] all = ChestTiers.All;
			foreach (ChestUpgradeDef chestUpgradeDef in all)
			{
				TierCosts[chestUpgradeDef.Id] = ((BaseUnityPlugin)this).Config.Bind<int>("Costs", chestUpgradeDef.Id + "UpgradeCost", 30, Admin(chestUpgradeDef.BarPrefabName + " bars required at the Chest Upgrade Table for this upgrade. Each of the five upgrades (regardless of which) advances every basic chest one step up the shared storage progression - see ChestTiers.SizeSteps / the README."));
			}
		}

		private static ConfigDescription Admin(string description)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} });
		}

		internal static int GetTierCost(ChestUpgradeDef upgrade)
		{
			return TierCosts[upgrade.Id].Value;
		}
	}
	internal static class Registration
	{
		internal const string UpgradeTablePrefabName = "piece_gcp_chestupgradetable";

		public static void RegisterAll()
		{
			RegisterUpgradeTable();
			RegisterUpgradeItems();
		}

		private static void RegisterUpgradeTable()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			string text = string.Join(" -> ", ChestTiers.SizeSteps.Select(((int Width, int Height) s) => $"{s.Width}x{s.Height}"));
			PieceConfig val = new PieceConfig();
			val.Name = "Chest Upgrade Table";
			val.Description = "Pay materials here to permanently upgrade the storage capacity of every basic chest - including ones you've already built. Each of the five upgrades below is worth one step up the progression (5x2 -> " + text + "); it doesn't matter which ones you buy or in what order, only how many.";
			val.Enabled = true;
			val.PieceTable = PieceTables.Hammer;
			val.Icon = IconLoader.UpgradeTableIcon();
			val.Requirements = (RequirementConfig[])(object)new RequirementConfig[1]
			{
				new RequirementConfig("FineWood", Plugin.FineWoodCost.Value, 0, true)
			};
			PieceConfig val2 = val;
			CustomPiece val3 = new CustomPiece("piece_gcp_chestupgradetable", "piece_workbench", val2);
			if (!val3.IsValid())
			{
				Plugin.Log.LogError((object)"Chest Upgrade Table piece failed Jotunn validation; not registered.");
				return;
			}
			Sprite val4 = IconLoader.UpgradeTableIcon();
			if ((Object)(object)val4 != (Object)null)
			{
				val3.Piece.m_icon = val4;
			}
			CraftingStation component = val3.PiecePrefab.GetComponent<CraftingStation>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.Log.LogError((object)"Cloned Chest Upgrade Table prefab has no CraftingStation component (donor 'piece_workbench' may have changed) - upgrade recipes will not be craftable.");
			}
			else
			{
				component.m_name = "Chest Upgrade Table";
				component.m_showBasicRecipies = false;
				component.m_craftRequireRoof = false;
			}
			PieceManager.Instance.AddPiece(val3);
			Plugin.Log.LogInfo((object)"Registered Chest Upgrade Table piece.");
		}

		private static void RegisterUpgradeItems()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_0061: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			ChestUpgradeDef[] all = ChestTiers.All;
			foreach (ChestUpgradeDef chestUpgradeDef in all)
			{
				int tierCost = Plugin.GetTierCost(chestUpgradeDef);
				ItemConfig val = new ItemConfig
				{
					Name = chestUpgradeDef.DisplayName,
					Description = "Consumed at the Chest Upgrade Table. Advances every basic chest one step up the shared storage progression, world-wide. Order and material don't matter - only how many of the five upgrades have been bought so far.",
					Enabled = true,
					Amount = 1,
					CraftingStation = "piece_gcp_chestupgradetable",
					StackSize = 1,
					Weight = 0f
				};
				val.AddRequirement(chestUpgradeDef.BarPrefabName, tierCost, 0);
				CustomItem val2 = new CustomItem(chestUpgradeDef.UpgradeItemPrefabName, chestUpgradeDef.BarPrefabName, val);
				if (!val2.IsValid())
				{
					Plugin.Log.LogError((object)("Chest upgrade item for '" + chestUpgradeDef.Id + "' failed Jotunn validation (donor '" + chestUpgradeDef.BarPrefabName + "' may not exist in this Valheim version); not registered."));
				}
				else
				{
					ItemManager.Instance.AddItem(val2);
					if (val2.Recipe == null)
					{
						Plugin.Log.LogWarning((object)("Chest upgrade item for '" + chestUpgradeDef.Id + "' registered but has no recipe - it will not be craftable, and its unlock won't auto-hide once bought."));
					}
					Plugin.Log.LogInfo((object)$"Registered chest upgrade item '{chestUpgradeDef.Id}' (cost: {tierCost}x {chestUpgradeDef.BarPrefabName}).");
				}
			}
		}
	}
}
namespace GrillspettChestProgression.Patches
{
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class Container_Awake_Patch
	{
		private static readonly HashSet<string> LoggedNames = new HashSet<string>();

		private static void Prefix(Container __instance)
		{
			if (ChestUpgradeState.IsTargetChest(__instance))
			{
				ChestUpgradeState.ApplyCurrentSizeBeforeAwake(__instance);
			}
		}

		private static void Postfix(Container __instance)
		{
			bool flag = ChestUpgradeState.IsTargetChest(__instance);
			string text = (((Object)(object)((Component)__instance).gameObject == (Object)null) ? "<null>" : ((Object)((Component)__instance).gameObject).name);
			if (LoggedNames.Add(text))
			{
				Plugin.Log.LogInfo((object)("[Diagnostics] Container prefab seen: '" + text + "' " + $"(TargetChestPrefab='{Plugin.TargetChestPrefab.Value}', matches={flag})"));
			}
			if (flag)
			{
				ChestUpgradeState.TrackContainer(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class InventoryGui_DoCrafting_Patch
	{
		private static void Postfix(Player player, Recipe ___m_craftRecipe)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)___m_craftRecipe == (Object)null || (Object)(object)___m_craftRecipe.m_item == (Object)null)
			{
				return;
			}
			string name = ((Object)((Component)___m_craftRecipe.m_item).gameObject).name;
			ChestUpgradeDef upgradeForPrefabName = ChestTiers.GetUpgradeForPrefabName(name);
			if (upgradeForPrefabName == null)
			{
				if (name.StartsWith("GCP_ChestUpgrade_"))
				{
					Plugin.Log.LogWarning((object)("Crafted '" + name + "' looks like a chest upgrade token but matched no known upgrade - this is a bug in the mod, please report it."));
				}
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			bool flag = false;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_shared != null && allItem.m_shared.m_name == upgradeForPrefabName.DisplayName)
				{
					inventory.RemoveItem(allItem);
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				Plugin.Log.LogWarning((object)("Crafted the '" + upgradeForPrefabName.Id + "' chest upgrade token but couldn't find it in " + player.GetPlayerName() + "'s inventory to remove it - it may be left behind."));
			}
			if (!ZoneSystem.instance.GetGlobalKey(upgradeForPrefabName.GlobalKey))
			{
				Plugin.Log.LogInfo((object)("Chest upgrade '" + upgradeForPrefabName.Id + "' requested by " + player.GetPlayerName() + "."));
				ZoneSystem.instance.SetGlobalKey(upgradeForPrefabName.GlobalKey);
			}
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "GlobalKeyAdd")]
	internal static class ZoneSystem_GlobalKeyAdd_Patch
	{
		private static void Postfix(string keyStr)
		{
			if (ChestTiers.IsUpgradeKey(keyStr))
			{
				ChestUpgradeState.ResizeAllLoadedChests();
				ChestUpgradeState.DisableUnlockedRecipes();
				Plugin.Log.LogInfo((object)("Chest upgrade key '" + keyStr + "' applied on this peer."));
				if (ChestUpgradeState.TryGetCurrentSize(out var width, out var height) && (Object)(object)Player.m_localPlayer != (Object)null)
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, $"Chest storage upgraded to {width}x{height}!", 0, (Sprite)null, false);
				}
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		internal IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}