using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("LuckMonitorPlugin")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("LuckMonitorPlugin")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("c6baf2e8-69f6-4b7d-b513-705a60ab6f3d")] [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 RyukaLabs.Sulfur.LuckTweaks; [BepInPlugin("ryukalabs.sulfur.lucktweaks", "Luck Tweaks", "1.0.1")] public class LuckTweaksPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry RecoveryMultiplier; internal static ConfigEntry RecoveryFlatBonus; internal static ConfigEntry RecoveryOverridePerMinute; internal static ConfigEntry ConsumptionMultiplier; internal static ConfigEntry VerboseLogging; internal const int StatLuckGain = 58; internal const int StatusLuck = 94; internal static bool IsTweakingLuckStatus; private Harmony _harmony; private void Awake() { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; RecoveryMultiplier = ((BaseUnityPlugin)this).Config.Bind("Luck Recovery", "RecoveryMultiplier", 1f, "Multiplier for positive Status_Luck changes. 1 = vanilla. 2 = double luck recovery."); RecoveryFlatBonus = ((BaseUnityPlugin)this).Config.Bind("Luck Recovery", "RecoveryFlatBonus", 0f, "Flat bonus added to every positive Status_Luck recovery tick after multiplier."); RecoveryOverridePerMinute = ((BaseUnityPlugin)this).Config.Bind("Luck Recovery", "RecoveryOverridePerMinute", -1f, "If >= 0, replaces each positive Status_Luck recovery tick with this value. -1 = disabled."); ConsumptionMultiplier = ((BaseUnityPlugin)this).Config.Bind("Luck Consumption", "ConsumptionMultiplier", 1f, "Multiplier for negative Status_Luck changes. 1 = vanilla. 0.5 = half cost. 0 = no luck consumption."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "VerboseLogging", true, "Print Status_Luck changes to BepInEx log."); _harmony = new Harmony("ryukalabs.sulfur.lucktweaks"); _harmony.PatchAll(); Log.LogInfo((object)"Luck Tweaks loaded."); Log.LogInfo((object)"Watching Status_Luck = 94. Stat_LuckGain = 58."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal static bool IsStatusLuckArg(object arg) { int? num = TryExtractAttributeId(arg); return num.HasValue && num.Value == 94; } private static int? TryExtractAttributeId(object arg) { if (arg == null) { return null; } Type type = arg.GetType(); if (type.IsEnum) { try { return Convert.ToInt32(arg); } catch { return null; } } if (arg is int value) { return value; } if (arg is long num) { return (int)num; } string text = arg.ToString(); if (text == "Status_Luck") { return 94; } if (text == "Stat_LuckGain") { return 58; } object member = GetMember(arg, "id"); if (member != null && member != arg) { int? num2 = TryExtractAttributeId(member); if (num2.HasValue) { return num2.Value; } } object member2 = GetMember(arg, "value"); if (member2 != null && member2 != arg) { int? num3 = TryExtractAttributeId(member2); if (num3.HasValue) { return num3.Value; } } try { return Convert.ToInt32(arg); } catch { return null; } } internal static object GetMember(object obj, string name) { if (obj == null) { return null; } Type type = obj.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { try { return property.GetValue(obj, null); } catch { return null; } } FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { return field.GetValue(obj); } catch { return null; } } type = type.BaseType; } return null; } internal static Type FindTypeByFullName(string fullName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type = assembly.GetType(fullName, throwOnError: false); if (type != null) { return type; } } return null; } } [HarmonyPatch] internal static class EntityStats_ModifyStatus_Patch { [CompilerGenerated] private sealed class d__0 : IEnumerable, IEnumerable, IEnumerator, IDisposable, IEnumerator { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; private Type 5__1; private MethodInfo[] <>s__2; private int <>s__3; private MethodInfo 5__4; private ParameterInfo[] 5__5; MethodBase IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; <>s__2 = null; 5__4 = null; 5__5 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_00ef; } <>1__state = -1; 5__1 = LuckTweaksPlugin.FindTypeByFullName("PerfectRandom.Sulfur.Core.Stats.EntityStats"); if (5__1 == null) { return false; } <>s__2 = 5__1.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); <>s__3 = 0; goto IL_010c; IL_00fe: <>s__3++; goto IL_010c; IL_00ef: 5__5 = null; 5__4 = null; goto IL_00fe; IL_010c: if (<>s__3 < <>s__2.Length) { 5__4 = <>s__2[<>s__3]; if (5__4.Name != "ModifyStatus") { goto IL_00fe; } 5__5 = 5__4.GetParameters(); if (5__5.Length >= 2 && 5__5[1].ParameterType == typeof(float)) { <>2__current = 5__4; <>1__state = 1; return true; } goto IL_00ef; } <>s__2 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__0(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [IteratorStateMachine(typeof(d__0))] private static IEnumerable TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__0(-2); } private static void Prefix(object __0, ref float __1, out bool __state) { __state = false; if (!LuckTweaksPlugin.IsStatusLuckArg(__0) || LuckTweaksPlugin.IsTweakingLuckStatus) { return; } LuckTweaksPlugin.IsTweakingLuckStatus = true; __state = true; float num = __1; float num2 = num; if (num > 0f) { float value = LuckTweaksPlugin.RecoveryOverridePerMinute.Value; num2 = ((!(value >= 0f)) ? (num * LuckTweaksPlugin.RecoveryMultiplier.Value + LuckTweaksPlugin.RecoveryFlatBonus.Value) : value); if (num2 < 0f) { num2 = 0f; } } else if (num < 0f) { float num3 = LuckTweaksPlugin.ConsumptionMultiplier.Value; if (num3 < 0f) { num3 = 0f; } num2 = num * num3; } __1 = num2; if (LuckTweaksPlugin.VerboseLogging.Value) { LuckTweaksPlugin.Log.LogInfo((object)$"[LuckTweaks] Status_Luck delta changed: {num:0.###} -> {num2:0.###}"); } } private static void Postfix(bool __state) { if (__state) { LuckTweaksPlugin.IsTweakingLuckStatus = false; } } }