using System; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Rhythmic Repairs")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Rhythmic Repairs")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("69840b67-9e2e-4e40-97a6-bfcd82234ddf")] [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")] namespace RhythmicRepairs { [BepInPlugin("petri.valheim.rhythmicrepairs", "Rhythmic Repairs", "1.0.0")] public class Plugin : BaseUnityPlugin { internal enum RepairTriggerMode { OnClick, OnOpen, Both } public const string ModGuid = "petri.valheim.rhythmicrepairs"; public const string ModName = "Rhythmic Repairs"; public const string ModVersion = "1.0.0"; internal static ConfigEntry EnableMod; internal static ConfigEntry RepairTrigger; internal static ConfigEntry RepairDelaySeconds; internal static ConfigEntry MaxRepairsPerClick; internal static ConfigEntry DebugLogging; private static MethodInfo haveRepairableItemsMethod; private static MethodInfo repairOneItemMethod; private Harmony harmony; private Coroutine repairRoutine; internal static Plugin Instance { get; private set; } internal static bool IsVanillaRepairCallInProgress { get; private set; } internal static bool IsRepairSequenceRunning => (Object)(object)Instance != (Object)null && Instance.repairRoutine != null; private void Awake() { //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown Instance = this; EnableMod = ((BaseUnityPlugin)this).Config.Bind("General", "EnableMod", true, "Enable or disable the mod."); RepairTrigger = ((BaseUnityPlugin)this).Config.Bind("Repair", "RepairTrigger", RepairTriggerMode.OnClick, "Controls when repair-all is triggered."); RepairDelaySeconds = ((BaseUnityPlugin)this).Config.Bind("Repair", "RepairDelaySeconds", 0.12f, "Delay in seconds between each repaired item. Higher values preserve a clearer repair rhythm."); MaxRepairsPerClick = ((BaseUnityPlugin)this).Config.Bind("Repair", "MaxRepairsPerClick", 100, "Safety limit for how many items can be repaired from one repair action."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "DebugLogging", false, "Enable debug logging."); haveRepairableItemsMethod = AccessTools.Method(typeof(InventoryGui), "HaveRepairableItems", (Type[])null, (Type[])null); repairOneItemMethod = AccessTools.Method(typeof(InventoryGui), "RepairOneItem", (Type[])null, (Type[])null); if (haveRepairableItemsMethod == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Rhythmic Repairs: could not find InventoryGui.HaveRepairableItems."); } if (repairOneItemMethod == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Rhythmic Repairs: could not find InventoryGui.RepairOneItem."); } harmony = new Harmony("petri.valheim.rhythmicrepairs"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Rhythmic Repairs 1.0.0 loaded."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } internal void StartRepairSequence(InventoryGui inventoryGui) { if (!((Object)(object)inventoryGui == (Object)null)) { if (repairRoutine != null) { DebugLog("Repair sequence already running."); } else { repairRoutine = ((MonoBehaviour)this).StartCoroutine(RepairAllRoutine(inventoryGui)); } } } private IEnumerator RepairAllRoutine(InventoryGui inventoryGui) { int repairedCount = 0; int maxRepairs = Mathf.Max(1, MaxRepairsPerClick.Value); DebugLog("Repair sequence started."); while (EnableMod.Value && (Object)(object)inventoryGui != (Object)null && repairedCount < maxRepairs && HaveRepairableItems(inventoryGui) && InvokeVanillaRepairOneItem(inventoryGui)) { repairedCount++; float delay = Mathf.Max(0f, RepairDelaySeconds.Value); if (delay > 0f) { yield return (object)new WaitForSeconds(delay); } else { yield return null; } } if (repairedCount >= maxRepairs) { ((BaseUnityPlugin)this).Logger.LogWarning((object)$"Rhythmic Repairs: stopped after safety limit of {maxRepairs} repair(s)."); } DebugLog($"Repair sequence finished. Repaired {repairedCount} item(s)."); repairRoutine = null; } private static bool HaveRepairableItems(InventoryGui inventoryGui) { if ((Object)(object)inventoryGui == (Object)null || haveRepairableItemsMethod == null) { return false; } try { return (bool)haveRepairableItemsMethod.Invoke(inventoryGui, null); } catch (Exception arg) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogError((object)$"Rhythmic Repairs: HaveRepairableItems failed: {arg}"); } return false; } } private static bool InvokeVanillaRepairOneItem(InventoryGui inventoryGui) { if ((Object)(object)inventoryGui == (Object)null || repairOneItemMethod == null) { return false; } try { IsVanillaRepairCallInProgress = true; repairOneItemMethod.Invoke(inventoryGui, null); return true; } catch (Exception arg) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogError((object)$"Rhythmic Repairs: RepairOneItem failed: {arg}"); } return false; } finally { IsVanillaRepairCallInProgress = false; } } internal void TryStartAutoRepairOnStationOpen(InventoryGui inventoryGui) { if (!((Object)(object)inventoryGui == (Object)null) && EnableMod.Value && ShouldRepairOnOpen() && !IsVanillaRepairCallInProgress && !IsRepairSequenceRunning && HaveRepairableItems(inventoryGui)) { DebugLog("Auto repair trigger detected from station UI."); StartRepairSequence(inventoryGui); } } internal static void DebugLog(string message) { ConfigEntry debugLogging = DebugLogging; if (debugLogging != null && debugLogging.Value) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogInfo((object)("Rhythmic Repairs: " + message)); } } } internal static bool ShouldRepairOnClick() { return RepairTrigger.Value == RepairTriggerMode.OnClick || RepairTrigger.Value == RepairTriggerMode.Both; } internal static bool ShouldRepairOnOpen() { return RepairTrigger.Value == RepairTriggerMode.OnOpen || RepairTrigger.Value == RepairTriggerMode.Both; } } } namespace RhythmicRepairs.Patches { [HarmonyPatch(typeof(InventoryGui), "RepairOneItem")] public static class InventoryGuiRepairOneItemPatch { private static bool Prefix(InventoryGui __instance) { if ((Object)(object)Plugin.Instance == (Object)null) { return true; } if (!Plugin.EnableMod.Value) { return true; } if (!Plugin.ShouldRepairOnClick()) { return true; } if (Plugin.IsVanillaRepairCallInProgress) { return true; } if (Plugin.IsRepairSequenceRunning) { Plugin.DebugLog("Ignored repair click because repair sequence is already running."); return false; } Plugin.Instance.StartRepairSequence(__instance); return false; } } [HarmonyPatch(typeof(InventoryGui), "UpdateRepair")] public static class InventoryGuiUpdateRepairPatch { private static void Postfix(InventoryGui __instance) { if (!((Object)(object)Plugin.Instance == (Object)null)) { Plugin.Instance.TryStartAutoRepairOnStationOpen(__instance); } } } }