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 BetterSmelter v2.2.1
BetterSmelter.dll
Decompiled 2 weeks agousing System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("BetterSmelter")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BetterSmelter")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("24ffff70-bb78-41b9-bfb7-199475e6ab35")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] [BepInPlugin("com.alan.bettersmelter", "Better Smelter", "2.2.1")] public class BetterSmelter : BaseUnityPlugin { internal static ConfigEntry<int> MaxOre; internal static ConfigEntry<int> MaxFuel; internal static ConfigEntry<int> HoneyMax; internal static ConfigEntry<int> CharcoalMaxWood; internal static ConfigEntry<int> CharcoalSpeedMultiplier; internal static ConfigEntry<int> MachineCapacityMultiplier; internal static ConfigEntry<bool> DebugDiagnostics; private void Awake() { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) MaxOre = ((BaseUnityPlugin)this).Config.Bind<int>("Smelter", "MaxOre", 50, "Maximum amount of ore (applied only to fuel-burning furnaces; machines stay vanilla)"); MaxFuel = ((BaseUnityPlugin)this).Config.Bind<int>("Smelter", "MaxFuel", 100, "Maximum amount of fuel (applied only to fuel-burning furnaces; machines stay vanilla)"); CharcoalMaxWood = ((BaseUnityPlugin)this).Config.Bind<int>("Charcoal Kiln", "MaxWood", 100, "Maximum amount of wood in the charcoal kiln (only increases the wood queue, never the fuel)"); CharcoalSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<int>("Charcoal Kiln", "SpeedMultiplier", 2, "Charcoal kiln speed (2 = 2x faster)"); HoneyMax = ((BaseUnityPlugin)this).Config.Bind<int>("Beehive", "MaxHoney", 50, "Maximum amount of honey"); MachineCapacityMultiplier = ((BaseUnityPlugin)this).Config.Bind<int>("Machines", "CapacityMultiplier", 2, "Multiplier applied to the vanilla capacity of the spinning wheel, the windmill and the eitr refinery (2 = double). Only touches the input queue, never the fuel of machines that do not burn it."); DebugDiagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DiagnosticLogs", true, "If true, logs each Smelter to LogOutput on load (hierarchy, whether it is a windmill/siege, limits). Set false to silence."); new Harmony("com.alan.bettersmelter").PatchAll(); BetterSmelterLogger.Log("Better Smelter loaded successfully."); if (DebugDiagnostics.Value) { BetterSmelterLogger.Log("[Diag] DiagnosticLogs=true — [Diag] messages go to BepInEx/LogOutput.log (not just the Unity console next to the \"Added ore\" lines)."); } } } public static class BetterSmelterLogger { private static readonly ManualLogSource LogSource = Logger.CreateLogSource("BetterSmelter"); public static void Log(string msg) { LogSource.LogInfo((object)msg); } public static void LogDiag(string msg) { if (BetterSmelter.DebugDiagnostics != null && BetterSmelter.DebugDiagnostics.Value) { LogSource.LogInfo((object)("[Diag] " + msg)); } } } [HarmonyPatch(typeof(Smelter), "Awake")] public static class Smelter_MaxCapacityPatch { private static void Postfix(Smelter __instance) { string text = ((Object)((Component)__instance).gameObject).name; if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length); } int maxOre = __instance.m_maxOre; int maxFuel = __instance.m_maxFuel; bool flag = SmelterStationHelpers.IsCharcoalKiln(text); bool flag2 = SmelterStationHelpers.IsSmelterMachine(__instance, text); bool flag3 = SmelterStationHelpers.IsSiegeEngineSmelter(__instance); bool flag4 = SmelterStationHelpers.IsKnownNonFurnacePrefab(text); bool flag5 = (Object)(object)__instance.m_fuelItem != (Object)null && __instance.m_maxFuel > 0 && __instance.m_maxOre > 0; if (BetterSmelter.DebugDiagnostics != null && BetterSmelter.DebugDiagnostics.Value) { SmelterDiagnostics.LogSmelterAwake(__instance, text, maxOre, maxFuel, flag, flag2, flag3, flag4, flag5); } if (flag) { ApplyKilnCaps(__instance); if (BetterSmelter.DebugDiagnostics != null && BetterSmelter.DebugDiagnostics.Value) { BetterSmelterLogger.LogDiag($"Charcoal kiln caps APPLIED id={((Object)__instance).GetInstanceID()} | after: m_maxOre={__instance.m_maxOre}, m_maxFuel={__instance.m_maxFuel} (untouched), m_secPerProduct={__instance.m_secPerProduct} (cfg MaxWood={BetterSmelter.CharcoalMaxWood.Value}, SpeedMultiplier={BetterSmelter.CharcoalSpeedMultiplier.Value})"); } } else if (flag2) { ApplyMachineCaps(__instance, maxOre, maxFuel); if (BetterSmelter.DebugDiagnostics != null && BetterSmelter.DebugDiagnostics.Value) { BetterSmelterLogger.LogDiag($"Machine caps APPLIED id={((Object)__instance).GetInstanceID()} ({text}) | after: m_maxOre={__instance.m_maxOre}, m_maxFuel={__instance.m_maxFuel} (cfg CapacityMultiplier={BetterSmelter.MachineCapacityMultiplier.Value})"); } } else if (flag3) { BetterSmelterLogger.LogDiag($"SKIP siege id={((Object)__instance).GetInstanceID()} ({text}) — no mod caps."); } else if (flag4) { BetterSmelterLogger.LogDiag($"SKIP known machine id={((Object)__instance).GetInstanceID()} ({text}) — no mod caps."); } else if (!flag5) { BetterSmelterLogger.LogDiag($"SKIP no fuel id={((Object)__instance).GetInstanceID()} ({text}) — m_fuelItem={__instance.m_fuelItem}, m_maxFuel={__instance.m_maxFuel}, m_maxOre={__instance.m_maxOre}. Machine without a furnace stays vanilla so it does not stop."); } else { ApplySmelterCaps(__instance); if (BetterSmelter.DebugDiagnostics != null && BetterSmelter.DebugDiagnostics.Value) { BetterSmelterLogger.LogDiag($"Smelter caps APPLIED id={((Object)__instance).GetInstanceID()} ({text}) | after: m_maxOre={__instance.m_maxOre}, m_maxFuel={__instance.m_maxFuel} (cfg MaxOre={BetterSmelter.MaxOre.Value}, MaxFuel={BetterSmelter.MaxFuel.Value})"); } } } private static void ApplySmelterCaps(Smelter smelter) { int value = BetterSmelter.MaxOre.Value; if (value > 0) { smelter.m_maxOre = value; } int value2 = BetterSmelter.MaxFuel.Value; if (value2 > 0) { smelter.m_maxFuel = value2; } } private static void ApplyKilnCaps(Smelter smelter) { int value = BetterSmelter.CharcoalMaxWood.Value; if (value > 0) { smelter.m_maxOre = value; } int value2 = BetterSmelter.CharcoalSpeedMultiplier.Value; if (value2 > 1 && smelter.m_secPerProduct > 0f) { smelter.m_secPerProduct /= (float)value2; } } private static void ApplyMachineCaps(Smelter smelter, int oreBefore, int fuelBefore) { int value = BetterSmelter.MachineCapacityMultiplier.Value; if (value > 1) { if (oreBefore > 0) { smelter.m_maxOre = oreBefore * value; } if ((Object)(object)smelter.m_fuelItem != (Object)null && fuelBefore > 0 && fuelBefore > 0) { smelter.m_maxFuel = fuelBefore * value; } } } } internal static class SmelterStationHelpers { private static readonly string[] KnownSpecialPrefabs = new string[13] { "windmill", "piece_spinningwheel", "charcoal_kiln", "eitrrefinery", "piece_bathtub", "piece_FrostKiln", "BatteringRam", "DevForge", "DevGarden", "DevHouse5", "DevHouseStart", "DevBedchamber", "DevMageRoom" }; internal static bool IsCharcoalKiln(string prefabName) { return string.Equals(prefabName, "charcoal_kiln", StringComparison.OrdinalIgnoreCase); } internal static bool IsSpinningWheel(string prefabName) { return string.Equals(prefabName, "piece_spinningwheel", StringComparison.OrdinalIgnoreCase); } internal static bool IsEitrRefinery(string prefabName) { return string.Equals(prefabName, "eitrrefinery", StringComparison.OrdinalIgnoreCase); } internal static bool IsSmelterMachine(Smelter instance, string prefabName) { if (IsWindmillGrainSmelter(instance)) { return true; } if (!IsSpinningWheel(prefabName)) { return IsEitrRefinery(prefabName); } return true; } internal static bool IsKnownNonFurnacePrefab(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return false; } string[] knownSpecialPrefabs = KnownSpecialPrefabs; foreach (string b in knownSpecialPrefabs) { if (string.Equals(prefabName, b, StringComparison.OrdinalIgnoreCase)) { return true; } } if (prefabName.StartsWith("Dev", StringComparison.OrdinalIgnoreCase)) { return true; } if (prefabName.IndexOf("kiln", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (prefabName.IndexOf("refinery", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (prefabName.IndexOf("bathtub", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (prefabName.IndexOf("spinningwheel", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (prefabName.IndexOf("frost", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } return false; } internal static bool IsWindmillGrainSmelter(Smelter instance) { if ((Object)(object)instance == (Object)null) { return false; } if ((Object)(object)instance.m_windmill != (Object)null) { return true; } if ((Object)(object)((Component)instance).GetComponentInParent<Windmill>(true) != (Object)null) { return true; } return false; } internal static bool IsSiegeEngineSmelter(Smelter instance) { if ((Object)(object)instance == (Object)null) { return false; } FieldInfo fieldInfo = AccessTools.Field(typeof(SiegeMachine), "m_engine"); SiegeMachine[] componentsInChildren = ((Component)((Component)instance).transform.root).GetComponentsInChildren<SiegeMachine>(true); foreach (SiegeMachine obj in componentsInChildren) { object? obj2 = fieldInfo?.GetValue(obj); if ((Object)((obj2 is Smelter) ? obj2 : null) == (Object)(object)instance) { return true; } } if ((Object)(object)((Component)instance).GetComponentInParent<SiegeMachine>(true) != (Object)null) { return true; } return false; } } internal static class SmelterDiagnostics { internal static string GetHierarchyPath(Transform t) { StringBuilder stringBuilder = new StringBuilder(); while ((Object)(object)t != (Object)null) { if (stringBuilder.Length > 0) { stringBuilder.Insert(0, "/"); } stringBuilder.Insert(0, ((Object)t).name); t = t.parent; } return stringBuilder.ToString(); } internal static void LogSmelterAwake(Smelter instance, string prefabName, int oreBefore, int fuelBefore, bool isKiln, bool isMachine, bool skipSiege, bool isKnownMachine, bool burnsFuel) { int instanceID = ((Object)instance).GetInstanceID(); string hierarchyPath = GetHierarchyPath(((Component)instance).transform); string value = (((Object)(object)((Component)instance).transform.root != (Object)null) ? ((Object)((Component)instance).transform.root).name : "?"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("Smelter Awake id=").Append(instanceID).AppendLine(); stringBuilder.Append(" prefabName: ").AppendLine(prefabName); stringBuilder.Append(" path: ").AppendLine(hierarchyPath); stringBuilder.Append(" root.name: ").AppendLine(value); stringBuilder.Append(" before: m_maxOre=").Append(oreBefore).Append(", m_maxFuel=") .AppendLine(fuelBefore.ToString()); stringBuilder.Append(" flags: IsCharcoalKiln=").Append(isKiln).Append(", IsMachine=") .Append(isMachine) .Append(", IsSiegeEngineSmelter=") .Append(skipSiege) .Append(", IsKnownNonFurnacePrefab=") .Append(isKnownMachine) .Append(", BurnsFuel=") .AppendLine(burnsFuel.ToString()); stringBuilder.Append(" m_fuelItem=").Append(((Object)(object)instance.m_fuelItem != (Object)null) ? ((Object)((Component)instance.m_fuelItem).gameObject).name : "null").Append(", m_windmill=") .Append(((Object)(object)instance.m_windmill != (Object)null) ? ((Object)((Component)instance.m_windmill).gameObject).name : "null") .Append(", m_secPerProduct=") .AppendLine(instance.m_secPerProduct.ToString()); if (isKiln) { stringBuilder.AppendLine(" => APPLY CHARCOAL KILN caps (MaxWood, SpeedMultiplier)."); } else if (isMachine) { stringBuilder.Append(" => APPLY MACHINE caps cfg CapacityMultiplier=").AppendLine(BetterSmelter.MachineCapacityMultiplier.Value.ToString()); } else if (skipSiege) { stringBuilder.AppendLine(" => SKIP siege (no mod caps)."); } else if (isKnownMachine) { stringBuilder.AppendLine(" => SKIP known machine (no mod caps)."); } else if (!burnsFuel) { stringBuilder.AppendLine(" => SKIP no fuel (machine stays vanilla)."); } else { stringBuilder.Append(" => APPLY caps cfg MaxOre=").Append(BetterSmelter.MaxOre.Value).Append(", MaxFuel=") .AppendLine(BetterSmelter.MaxFuel.Value.ToString()); } BetterSmelterLogger.LogDiag(stringBuilder.ToString()); } } [HarmonyPatch(typeof(Smelter), "RPC_AddOre")] public static class Smelter_RPC_AddOre_DiagPatch { private static void Postfix(Smelter __instance, long sender, string name, bool cheated) { if (BetterSmelter.DebugDiagnostics != null && BetterSmelter.DebugDiagnostics.Value) { int maxOre = __instance.m_maxOre; int num = -1; MethodInfo methodInfo = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null); if (methodInfo != null) { num = (int)methodInfo.Invoke(__instance, null); } bool flag = SmelterStationHelpers.IsWindmillGrainSmelter(__instance); bool flag2 = SmelterStationHelpers.IsSiegeEngineSmelter(__instance); BetterSmelterLogger.LogDiag("RPC_AddOre item=" + name + " sender=" + sender + " smelterId=" + ((Object)__instance).GetInstanceID() + " m_maxOre=" + maxOre + " GetQueueSize(after)=" + num + " windmillSmelter=" + flag + " siegeEngine=" + flag2 + " path=" + SmelterDiagnostics.GetHierarchyPath(((Component)__instance).transform)); } } } internal static class SmelterBulkAddHelpers { internal static bool IsShiftHeld() { if (!ZInput.GetKey((KeyCode)304, true)) { return ZInput.GetKey((KeyCode)303, true); } return true; } internal static bool IsAnyAllowed(Smelter smelter, string dropName) { foreach (ItemConversion item in smelter.m_conversion) { if ((Object)(object)item.m_from != (Object)null && ((Object)((Component)item.m_from).gameObject).name == dropName) { return true; } } return false; } internal static ItemData FindFirstCookable(Smelter smelter, Inventory inventory) { foreach (ItemConversion item2 in smelter.m_conversion) { if (!((Object)(object)item2.m_from == (Object)null)) { ItemData item = inventory.GetItem(item2.m_from.m_itemData.m_shared.m_name, -1, false); if (item != null) { return item; } } } return null; } internal static void SetAddedOreAnimation(Smelter smelter) { FieldInfo fieldInfo = AccessTools.Field(typeof(Smelter), "m_addedOreTime"); if (fieldInfo != null) { fieldInfo.SetValue(smelter, Time.time); } if (smelter.m_addOreAnimationDuration > 0f) { AccessTools.Method(typeof(Smelter), "SetAnimation", (Type[])null, (Type[])null)?.Invoke(smelter, new object[1] { true }); } } internal static int GetQueueSize(Smelter smelter) { return (int)(AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null)?.Invoke(smelter, null) ?? ((object)0)); } internal static float GetFuel(Smelter smelter) { return (float)(AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null)?.Invoke(smelter, null) ?? ((object)0f)); } internal static string AppendAddMaxHint(string hoverText) { if (string.IsNullOrEmpty(hoverText)) { return hoverText; } if (hoverText.IndexOf("Shift + E", StringComparison.OrdinalIgnoreCase) >= 0) { return hoverText; } return hoverText + "\n[<color=yellow><b>Shift + E</b></color>] Add Max"; } } [HarmonyPatch(typeof(Smelter), "OnAddOre")] public static class Smelter_OnAddOre_BulkPatch { private static bool Prefix(Switch sw, Humanoid user, ItemData item, Smelter __instance, ref bool __result) { if ((Object)(object)user == (Object)null || !SmelterBulkAddHelpers.IsShiftHeld()) { return true; } __result = BulkAddOre(__instance, user, item); return false; } private static bool BulkAddOre(Smelter smelter, Humanoid user, ItemData requested) { object? obj = AccessTools.Field(typeof(Smelter), "m_nview")?.GetValue(smelter); ZNetView val = (ZNetView)((obj is ZNetView) ? obj : null); if ((Object)(object)val == (Object)null) { return false; } Inventory inventory = user.GetInventory(); ItemData val2 = requested; if (val2 == null) { val2 = SmelterBulkAddHelpers.FindFirstCookable(smelter, inventory); if (val2 == null) { ((Character)user).Message((MessageType)2, "$msg_noprocessableitems", 0, (Sprite)null, false); return false; } } string name = ((Object)val2.m_dropPrefab).name; if (!SmelterBulkAddHelpers.IsAnyAllowed(smelter, name)) { ((Character)user).Message((MessageType)2, "$msg_wontwork", 0, (Sprite)null, false); return false; } int num = smelter.m_maxOre - SmelterBulkAddHelpers.GetQueueSize(smelter); if (num <= 0) { ((Character)user).Message((MessageType)2, "$msg_itsfull", 0, (Sprite)null, false); return false; } string name2 = val2.m_shared.m_name; int num2 = inventory.CountItems(name2, -1, true); if (num2 <= 0) { ((Character)user).Message((MessageType)2, "$msg_donthaveany " + name2, 0, (Sprite)null, false); return false; } int num3 = Mathf.Min(num, num2); inventory.RemoveItem(name2, num3, -1, true); for (int i = 0; i < num3; i++) { val.InvokeRPC("RPC_AddOre", new object[2] { name, val2.m_cheated }); } SmelterBulkAddHelpers.SetAddedOreAnimation(smelter); ((Character)user).Message((MessageType)2, "$msg_added " + val2.m_shared.m_name, 0, (Sprite)null, false); return true; } } [HarmonyPatch(typeof(Smelter), "OnAddFuel")] public static class Smelter_OnAddFuel_BulkPatch { private static bool Prefix(Switch sw, Humanoid user, ItemData item, Smelter __instance, ref bool __result) { if ((Object)(object)user == (Object)null || !SmelterBulkAddHelpers.IsShiftHeld()) { return true; } __result = BulkAddFuel(__instance, user, item); return false; } private static bool BulkAddFuel(Smelter smelter, Humanoid user, ItemData requested) { if ((Object)(object)smelter.m_fuelItem == (Object)null) { return false; } object? obj = AccessTools.Field(typeof(Smelter), "m_nview")?.GetValue(smelter); ZNetView val = (ZNetView)((obj is ZNetView) ? obj : null); if ((Object)(object)val == (Object)null) { return false; } string name = smelter.m_fuelItem.m_itemData.m_shared.m_name; if (requested != null && requested.m_shared.m_name != name) { ((Character)user).Message((MessageType)2, "$msg_wrongitem", 0, (Sprite)null, false); return false; } int num = smelter.m_maxFuel - (int)SmelterBulkAddHelpers.GetFuel(smelter); if (num <= 0) { ((Character)user).Message((MessageType)2, "$msg_itsfull", 0, (Sprite)null, false); return false; } Inventory inventory = user.GetInventory(); int num2 = inventory.CountItems(name, -1, true); if (num2 <= 0) { ((Character)user).Message((MessageType)2, "$msg_donthaveany " + name, 0, (Sprite)null, false); return false; } int num3 = Mathf.Min(num, num2); inventory.RemoveItem(name, num3, -1, true); for (int i = 0; i < num3; i++) { val.InvokeRPC("RPC_AddFuel", Array.Empty<object>()); } ((Character)user).Message((MessageType)2, "$msg_added " + name, 0, (Sprite)null, false); return true; } } [HarmonyPatch(typeof(Smelter), "OnHoverAddOre")] public static class Smelter_OnHoverAddOre_HintPatch { private static void Postfix(ref string __result) { __result = SmelterBulkAddHelpers.AppendAddMaxHint(__result); } } [HarmonyPatch(typeof(Smelter), "OnHoverAddFuel")] public static class Smelter_OnHoverAddFuel_HintPatch { private static void Postfix(ref string __result) { __result = SmelterBulkAddHelpers.AppendAddMaxHint(__result); } } [HarmonyPatch(typeof(Beehive), "Awake")] public static class BeehivePatch { private static void Postfix(Beehive __instance) { __instance.m_maxHoney = BetterSmelter.HoneyMax.Value; } }