Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ChestProgression v1.0.1
GrillspettChestProgression.dll
Decompiled a week agousing 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) { } } }