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 HammerTiers v0.1.0
HammerTiers.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.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; } } } namespace HammerTiers { [BepInPlugin("nifusion.hammertiers", "HammerTiers", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class HammerTiersPlugin : BaseUnityPlugin { public const string PluginGuid = "nifusion.hammertiers"; public const string PluginName = "HammerTiers"; public const string PluginVersion = "0.1.0"; public const string JotunnGuid = "com.jotunn.jotunn"; internal static ManualLogSource Log; internal static ConfigEntry<string> ToolPrefabName; internal static ConfigEntry<string> TierSpec; internal static ConfigEntry<bool> ReplaceVanillaUpgrades; internal static ConfigEntry<int> MetalUpgradeWorkbenchLevel; internal static ConfigEntry<int> BaseMaterialsPerLevel; internal static ConfigEntry<float> PlaceDistancePerLevel; internal static ConfigEntry<bool> Verbose; private static ConfigDescription Admin(string description) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown ConfigurationManagerAttributes val = new ConfigurationManagerAttributes(); val.IsAdminOnly = true; return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { val }); } private void Awake() { //IL_011f: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; ToolPrefabName = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ToolPrefab", "Hammer", Admin("Tool to add levels to. Hammer, Hoe and Cultivator all work.")); TierSpec = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Tiers", "Tin:3:300,Copper:3:300,Bronze:3:300,Iron:3:300,Silver:3:500,BlackMetal:3:500,FlametalNew:3:500", Admin("One entry per added level, in order: Prefab:Amount:Durability. Durability is optional. Unknown prefabs are skipped. The flametal bar is FlametalNew - plain Flametal is the old unusable one.")); ReplaceVanillaUpgrades = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ReplaceVanillaUpgrades", false, Admin("true = metal levels start at 2 and replace the tool's own upgrades instead of stacking after them.")); MetalUpgradeWorkbenchLevel = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MetalUpgradeWorkbenchLevel", 4, Admin("Workbench level the metal levels need. Workbench maxes out at 5.")); BaseMaterialsPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("General", "BaseMaterialsPerLevel", 1, Admin("Wood and stone per metal level, times the level. 0 = metal only.")); PlaceDistancePerLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Effects", "PlaceDistancePerLevel", 1f, Admin("Extra build reach per metal level. 0 = off.")); Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Verbose", false, Admin("Extra logging.")); new Harmony("nifusion.hammertiers").PatchAll(Assembly.GetExecutingAssembly()); Log.LogInfo((object)"HammerTiers 0.1.0 loaded."); } } internal static class HammerTiers { private sealed class ReferenceComparer : IEqualityComparer<Requirement> { public bool Equals(Requirement a, Requirement b) { return a == b; } public int GetHashCode(Requirement o) { return RuntimeHelpers.GetHashCode(o); } } internal static ItemDrop ToolDrop; internal static Recipe ToolRecipe; internal static Requirement[][] ByQuality; internal static int VanillaMaxQuality = 1; internal static Requirement[] TierRequirements = (Requirement[])(object)new Requirement[0]; internal static int FirstMetalLevel = 2; internal static int FullMaxQuality = 1; internal static string[] TierMaterialNames = new string[0]; internal static float[] TierDurability = new float[0]; private static int _lastCap = -1; private static bool _loggedDiag; private static FieldRef<InventoryGui, Container> _containerRef; private static bool _containerRefResolved; private static readonly HashSet<Requirement> TierSet = new HashSet<Requirement>(new ReferenceComparer()); private static SharedData _capturedShared; private static Requirement[] _baseResources; private static string _lastSummary; internal static bool TryGetMaxDurability(ItemData item, int quality, out float result) { result = 0f; if ((Object)(object)ToolDrop == (Object)null || item == null || item.m_shared == null) { return false; } if (item.m_shared.m_name != ToolDrop.m_itemData.m_shared.m_name) { return false; } if (TierDurability.Length == 0) { return false; } int num = FirstMetalLevel - 1; if (quality <= num) { return false; } float num2 = item.m_shared.m_maxDurability + (float)Mathf.Max(0, num - 1) * item.m_shared.m_durabilityPerLevel; for (int i = 0; i < TierDurability.Length && FirstMetalLevel + i <= quality; i++) { num2 += TierDurability[i]; } result = num2; return true; } internal static void LogDiagnosticsOnce() { if (_loggedDiag || !HammerTiersPlugin.Verbose.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ToolDrop == (Object)null) { return; } _loggedDiag = true; SharedData shared = ToolDrop.m_itemData.m_shared; HammerTiersPlugin.Log.LogInfo((object)("prefab max=" + shared.m_maxQuality + " full=" + FullMaxQuality + " firstMetal=" + FirstMetalLevel)); for (int i = 0; i < TierMaterialNames.Length; i++) { HammerTiersPlugin.Log.LogInfo((object)(" level " + (FirstMetalLevel + i) + " " + TierMaterialNames[i] + " known=" + localPlayer.IsKnownMaterial(TierMaterialNames[i]))); } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { HammerTiersPlugin.Log.LogInfo((object)" no inventory"); return; } List<ItemData> allItems = inventory.GetAllItems(); for (int j = 0; j < allItems.Count; j++) { ItemData val = allItems[j]; if (val != null && val.m_shared != null && !(val.m_shared.m_name != shared.m_name)) { TryGetMaxDurability(val, val.m_quality, out var result); HammerTiersPlugin.Log.LogInfo((object)(" held quality=" + val.m_quality + " max=" + val.m_shared.m_maxQuality + " durability=" + result)); } } } internal static void ApplyKnownMaterialCap() { if ((Object)(object)ToolDrop == (Object)null || TierMaterialNames.Length == 0) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { int num = FirstMetalLevel - 1; for (int i = 0; i < TierMaterialNames.Length && localPlayer.IsKnownMaterial(TierMaterialNames[i]); i++) { num = FirstMetalLevel + i; } if (num > FullMaxQuality) { num = FullMaxQuality; } if (num < 1) { num = 1; } ToolDrop.m_itemData.m_shared.m_maxQuality = num; CapInventory(((Humanoid)localPlayer).GetInventory(), num); Container openContainer = GetOpenContainer(); if ((Object)(object)openContainer != (Object)null) { CapInventory(openContainer.GetInventory(), num); } if (num != _lastCap) { _lastCap = num; HammerTiersPlugin.Log.LogInfo((object)("Max upgrade level: " + num + " of " + FullMaxQuality)); } } } private static Container GetOpenContainer() { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null) { return null; } if (!_containerRefResolved) { _containerRefResolved = true; try { _containerRef = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer"); } catch (Exception ex) { HammerTiersPlugin.Log.LogWarning((object)("Could not read m_currentContainer (" + ex.GetType().Name + "), chest contents will show the uncapped max.")); _containerRef = null; } } if (_containerRef != null) { return _containerRef.Invoke(instance); } return null; } private static void CapInventory(Inventory inventory, int allowed) { if (inventory == null || (Object)(object)ToolDrop == (Object)null) { return; } string name = ToolDrop.m_itemData.m_shared.m_name; List<ItemData> allItems = inventory.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (val != null && val.m_shared != null && val.m_shared.m_name == name) { val.m_shared.m_maxQuality = allowed; } } } internal static bool TryGetFlatAmount(Requirement req, out int amount) { if (req != null && TierSet.Contains(req)) { amount = req.m_amount; return true; } amount = 0; return false; } internal static void Build(ObjectDB odb) { //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Expected O, but got Unknown if ((Object)(object)odb == (Object)null || odb.m_items == null || odb.m_items.Count == 0 || odb.m_recipes == null || odb.m_recipes.Count == 0) { return; } string value = HammerTiersPlugin.ToolPrefabName.Value; GameObject itemPrefab = odb.GetItemPrefab(value); if ((Object)(object)itemPrefab == (Object)null) { HammerTiersPlugin.Log.LogWarning((object)("Tool prefab '" + value + "' not found.")); return; } ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { HammerTiersPlugin.Log.LogWarning((object)("Prefab '" + value + "' has no ItemDrop.")); return; } Recipe val = null; for (int i = 0; i < odb.m_recipes.Count; i++) { Recipe val2 = odb.m_recipes[i]; if ((Object)(object)val2 != (Object)null && (Object)(object)val2.m_item == (Object)(object)component) { val = val2; break; } } if ((Object)(object)val == (Object)null) { HammerTiersPlugin.Log.LogWarning((object)("No recipe for '" + value + "'.")); return; } SharedData shared = component.m_itemData.m_shared; if ((Object)(object)ToolRecipe == (Object)(object)val && _baseResources != null) { val.m_resources = _baseResources; } if (_capturedShared != shared) { _capturedShared = shared; _baseResources = val.m_resources; VanillaMaxQuality = Mathf.Max(1, shared.m_maxQuality); HammerTiersPlugin.Log.LogInfo((object)(value + " base max quality: " + VanillaMaxQuality)); for (int j = 0; j < _baseResources.Length; j++) { Requirement val3 = _baseResources[j]; if (val3 != null && !((Object)(object)val3.m_resItem == (Object)null)) { HammerTiersPlugin.Log.LogInfo((object)(" base: " + ((Object)val3.m_resItem).name + " x" + val3.m_amount + " (+" + val3.m_amountPerLevel + "/level)" + (val3.m_upgraderResource ? " [upgrader resource, skipped]" : ""))); } } } List<float> list = new List<float>(); List<Requirement> list2 = ResolveTiers(odb, list, shared.m_durabilityPerLevel); if (list2.Count == 0) { HammerTiersPlugin.Log.LogWarning((object)("No tier materials found, leaving '" + value + "' alone.")); return; } int num = (HammerTiersPlugin.ReplaceVanillaUpgrades.Value ? 2 : (VanillaMaxQuality + 1)); int num2 = num - 1 + list2.Count; int value2 = HammerTiersPlugin.BaseMaterialsPerLevel.Value; List<Requirement> list3 = new List<Requirement>(list2); Requirement[][] array = new Requirement[num2 + 1][]; for (int k = 1; k < num && k <= num2; k++) { array[k] = _baseResources; } for (int l = 0; l < list2.Count; l++) { int num3 = num + l; List<Requirement> list4 = new List<Requirement>(); list4.Add(list2[l]); if (value2 > 0) { for (int m = 0; m < _baseResources.Length; m++) { Requirement val4 = _baseResources[m]; if (val4 != null && !((Object)(object)val4.m_resItem == (Object)null) && !val4.m_upgraderResource) { Requirement val5 = new Requirement(); val5.m_resItem = val4.m_resItem; val5.m_amount = value2 * num3; val5.m_amountPerLevel = 0; list4.Add(val5); list3.Add(val5); } } } array[num3] = list4.ToArray(); } TierRequirements = list3.ToArray(); TierSet.Clear(); for (int n = 0; n < TierRequirements.Length; n++) { TierSet.Add(TierRequirements[n]); } string text = ""; for (int num4 = 0; num4 < list2.Count; num4++) { int num5 = num + num4; float result; string text2 = (TryGetMaxDurability(component.m_itemData, num5, out result) ? (" (durability " + result + ")") : ""); text = text + "Level " + num5 + " requires " + Describe(array[num5], num5) + text2 + "\n"; } if (text != _lastSummary) { _lastSummary = text; string[] array2 = text.Split(new char[1] { '\n' }); foreach (string text3 in array2) { if (text3.Length > 0) { HammerTiersPlugin.Log.LogInfo((object)text3); } } } string[] array3 = new string[list2.Count]; for (int num7 = 0; num7 < list2.Count; num7++) { array3[num7] = list2[num7].m_resItem.m_itemData.m_shared.m_name; } ToolDrop = component; ToolRecipe = val; ByQuality = array; FirstMetalLevel = num; FullMaxQuality = num2; TierMaterialNames = array3; TierDurability = list.ToArray(); _lastCap = -1; shared.m_maxQuality = num2; val.m_resources = _baseResources; if (text != "") { HammerTiersPlugin.Log.LogInfo((object)(value + " max quality " + VanillaMaxQuality + " -> " + num2 + " (" + list2.Count + " metal levels from " + num + ").")); } } private static string Describe(Requirement[] set, int level) { string text = ""; for (int i = 0; i < set.Length; i++) { if (set[i] == null || (Object)(object)set[i].m_resItem == (Object)null) { continue; } int amount = set[i].GetAmount(level); if (amount > 0) { if (text.Length > 0) { text += ", "; } text = text + amount + "x " + ((Object)set[i].m_resItem).name; } } return text; } private static List<Requirement> ResolveTiers(ObjectDB odb, List<float> durability, float vanillaPerLevel) { //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Expected O, but got Unknown List<Requirement> list = new List<Requirement>(); string[] array = HammerTiersPlugin.TierSpec.Value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0) { string[] array2 = text.Split(new char[1] { ':' }); string text2 = array2[0].Trim(); int result = 1; float result2 = vanillaPerLevel; if (array2.Length > 1 && array2[1].Trim().Length > 0 && (!int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result) || result < 1)) { HammerTiersPlugin.Log.LogWarning((object)("Bad amount in '" + text + "', using 1.")); result = 1; } if (array2.Length > 2 && array2[2].Trim().Length > 0 && (!float.TryParse(array2[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result2) || result2 < 0f)) { HammerTiersPlugin.Log.LogWarning((object)("Bad durability in '" + text + "', using " + vanillaPerLevel + ".")); result2 = vanillaPerLevel; } GameObject itemPrefab = odb.GetItemPrefab(text2); ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>()); if ((Object)(object)val == (Object)null) { HammerTiersPlugin.Log.LogWarning((object)("'" + text2 + "' not found, skipping that level.")); continue; } Requirement val2 = new Requirement(); val2.m_resItem = val; val2.m_amount = result; val2.m_amountPerLevel = 0; list.Add(val2); durability.Add(result2); } } return list; } internal static void RestoreVanillaResources() { if ((Object)(object)ToolRecipe != (Object)null && _baseResources != null) { ToolRecipe.m_resources = _baseResources; } } internal static void Apply(int targetQuality) { if ((Object)(object)ToolRecipe == (Object)null || ByQuality == null) { return; } int num = targetQuality; if (num < 1) { num = 1; } if (num > ByQuality.Length - 1) { num = ByQuality.Length - 1; } Requirement[] array = ByQuality[num]; if (array != null) { ToolRecipe.m_resources = array; if (HammerTiersPlugin.Verbose.Value) { HammerTiersPlugin.Log.LogInfo((object)("Swapped to level " + num + ".")); } } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class Patch_ObjectDB_Awake { private static void Postfix(ObjectDB __instance) { HammerTiers.Build(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class Patch_ObjectDB_CopyOtherDB { private static void Postfix(ObjectDB __instance) { HammerTiers.Build(__instance); } } [HarmonyPatch(typeof(Requirement), "GetAmount", new Type[] { typeof(int) })] internal static class Patch_Requirement_GetAmount { private static bool Prefix(Requirement __instance, ref int __result) { if (!HammerTiers.TryGetFlatAmount(__instance, out var amount)) { return true; } __result = amount; return false; } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirementList")] internal static class Patch_InventoryGui_SetupRequirementList { private static void Prefix(int quality) { HammerTiers.Apply(quality); } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Recipe), typeof(bool), typeof(int), typeof(int) })] internal static class Patch_Player_HaveRequirements { private static void Prefix(Recipe recipe, int qualityLevel) { if ((Object)(object)recipe == (Object)(object)HammerTiers.ToolRecipe) { HammerTiers.Apply(qualityLevel); } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class Patch_InventoryGui_DoCrafting { private static void Prefix(Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem) { if (!((Object)(object)___m_craftRecipe != (Object)(object)HammerTiers.ToolRecipe)) { HammerTiers.Apply((___m_craftUpgradeItem == null) ? 1 : (___m_craftUpgradeItem.m_quality + 1)); } } private static void Postfix(Recipe ___m_craftRecipe) { if ((Object)(object)___m_craftRecipe == (Object)(object)HammerTiers.ToolRecipe) { HammerTiers.RestoreVanillaResources(); } } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] internal static class Patch_InventoryGui_UpdateRecipe { private static void Prefix() { HammerTiers.ApplyKnownMaterialCap(); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] internal static class Patch_InventoryGui_UpdateRecipeList { private static void Prefix() { HammerTiers.ApplyKnownMaterialCap(); HammerTiers.LogDiagnosticsOnce(); } } [HarmonyPatch(typeof(Recipe), "GetRequiredStationLevel")] internal static class Patch_Recipe_GetRequiredStationLevel { private static void Postfix(Recipe __instance, int quality, ref int __result) { if (!((Object)(object)__instance != (Object)(object)HammerTiers.ToolRecipe) && quality > HammerTiers.VanillaMaxQuality) { __result = Mathf.Max(1, HammerTiersPlugin.MetalUpgradeWorkbenchLevel.Value); } } } [HarmonyPatch(typeof(ItemData), "GetMaxDurability", new Type[] { typeof(int) })] internal static class Patch_ItemData_GetMaxDurability { private static void Postfix(ItemData __instance, int quality, ref float __result) { if (HammerTiers.TryGetMaxDurability(__instance, quality, out var result)) { __result = result; } } } [HarmonyPatch(typeof(Player), "UpdatePlacement")] internal static class Patch_Player_UpdatePlacement { private static float _vanillaDistance = -1f; private static float _lastWritten = float.NaN; private static void Prefix(Player __instance, ItemData ___m_rightItem) { if (_vanillaDistance < 0f || __instance.m_maxPlaceDistance != _lastWritten) { _vanillaDistance = __instance.m_maxPlaceDistance; } float value = HammerTiersPlugin.PlaceDistancePerLevel.Value; if (value == 0f) { return; } int num = 0; if ((Object)(object)HammerTiers.ToolDrop != (Object)null) { if (___m_rightItem != null && ___m_rightItem.m_shared != null && ___m_rightItem.m_shared.m_name == HammerTiers.ToolDrop.m_itemData.m_shared.m_name) { num = ___m_rightItem.m_quality - (HammerTiers.FirstMetalLevel - 1); if (num < 0) { num = 0; } } } __instance.m_maxPlaceDistance = _vanillaDistance + (float)num * value; _lastWritten = __instance.m_maxPlaceDistance; } } }