using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HutongGames.PlayMaker; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; [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: AssemblyCompany("SilkSuture")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SilkSuture")] [assembly: AssemblyTitle("SilkSuture")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 SilkSuture { [BepInPlugin("com.fabulator_k.silksuture", "Silk Suture", "0.5.1")] public class SilkSuture : BaseUnityPlugin { private class AppliedEdits { public PlayMakerFSM fsm; public FsmState bindStartState; public AdvanceHealAction advanceAction; public FsmState loopBindState; public LoopHealAdjustAction loopAction; public FsmState warriorState; public BeastAdvanceAction beastAction; } internal static ManualLogSource Logger; internal static SilkSuture Instance; internal const int AdvanceAmount = 1; internal static ConfigEntry configEnabled; internal static ConfigEntry configMultibinderPattern; internal static ConfigEntry configWitchAdvance; internal static ConfigEntry configBeastAdvance; internal static ConfigEntry configShamanAdvance; internal static ConfigEntry configMaggotAdvance; internal static ConfigEntry configMaggotMultibindAdvance; internal static ConfigEntry configLifebloodAdvance; internal static int lastAdvanceGranted; internal static int witchAdvanceGranted; internal static int rageAdvanceGranted; internal static bool maggotMultibindPending; private Harmony harmony; private AppliedEdits appliedEdits; private void Awake() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown Instance = this; Logger = ((BaseUnityPlugin)this).Logger; Logger.LogInfo((object)"[Silk Suture] Initializing..."); SetupConfig(); if (configEnabled.Value) { harmony = new Harmony("com.fabulator_k.silksuture"); harmony.PatchAll(); ((MonoBehaviour)this).StartCoroutine(SearchForBindFSM()); SceneManager.sceneLoaded += OnSceneLoaded; Logger.LogInfo((object)string.Format("[{0}] Enabled: Pattern={1}, Witch={2}, Beast={3}, Shaman={4}", "Silk Suture", configMultibinderPattern.Value, configWitchAdvance.Value, configBeastAdvance.Value, configShamanAdvance.Value)); } else { Logger.LogInfo((object)"[Silk Suture] Disabled in config"); } } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; RevertEdits(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (configEnabled.Value) { ((MonoBehaviour)this).StartCoroutine(VerifyEditsAfterLoad()); } } private IEnumerator VerifyEditsAfterLoad() { yield return null; yield return null; CheckEditsPresence(); } private void CheckEditsPresence() { try { if (appliedEdits == null) { return; } PlayMakerFSM fsm = appliedEdits.fsm; if ((Object)(object)fsm == (Object)null) { appliedEdits = null; return; } FsmState val = FindState(fsm, "Bind Start"); if (val == null || appliedEdits.advanceAction == null || Array.IndexOf(val.Actions, (FsmStateAction)(object)appliedEdits.advanceAction) < 0) { Logger.LogWarning((object)"Bind FSM edits missing after scene load - re-applying"); appliedEdits = null; ModifyBindFSM(fsm); } } catch (Exception arg) { Logger.LogError((object)$"Error verifying FSM edits after scene load: {arg}"); } } private void SetupConfig() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown configEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enable Silk Suture healing modifications"); configMultibinderPattern = ((BaseUnityPlugin)this).Config.Bind("Healing", "MultibinderPattern", "1+1+2", new ConfigDescription("Multibinder healing split (immediate + first completion + second completion, total 4)", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "1+1+2", "1+2+1" }), Array.Empty())); configWitchAdvance = ((BaseUnityPlugin)this).Config.Bind("Crests", "WitchAdvance", true, "Witch crest: advance the immediate heal and reduce the tentacle heal cap to match. Off = fully vanilla witch binds."); configBeastAdvance = ((BaseUnityPlugin)this).Config.Bind("Crests", "BeastAdvance", true, "Beast crest: advance the immediate heal and reduce the rage mode heal cap to match. Off = fully vanilla rage binds."); configShamanAdvance = ((BaseUnityPlugin)this).Config.Bind("Crests", "ShamanAdvance", true, "Shaman (Spell) crest: advance 1 mask when the bind starts, deducted from the completion heal (total stays vanilla). Applies to the mid-air held bind too. Off = fully vanilla shaman binds."); configMaggotAdvance = ((BaseUnityPlugin)this).Config.Bind("EdgeCases", "MaggotAdvance", true, "Maggoted binds: grant the immediate heal even though vanilla heals nothing while maggoted (cleanse bind heals 1 instead of 0). The Multibinder combo is governed by MaggotMultibindAdvance instead. Off = vanilla."); configMaggotMultibindAdvance = ((BaseUnityPlugin)this).Config.Bind("EdgeCases", "MaggotMultibindAdvance", true, "Maggoted + Multibinder: advance 1 mask immediately and deduct it from the second burst (first burst cleanses; total stays vanilla's 2). Takes precedence over MaggotAdvance for this combo - keeps it from stacking to 3. Off + MaggotAdvance on = uncapped 1+2."); configLifebloodAdvance = ((BaseUnityPlugin)this).Config.Bind("EdgeCases", "LifebloodAdvance", false, "Advance the immediate heal even in the lifeblood (all blue masks) state. Off = fully vanilla (recommended: lifeblood has its own regen economy)."); } private IEnumerator SearchForBindFSM() { yield return (object)new WaitForSeconds(3f); PlayMakerFSM[] array = Resources.FindObjectsOfTypeAll(); foreach (PlayMakerFSM val in array) { if (IsHeroBindFsm(val)) { Logger.LogInfo((object)("Found bind FSM: '" + val.FsmName + "' on '" + ((Object)((Component)val).gameObject).name + "'")); ModifyBindFSM(val); yield break; } } Logger.LogWarning((object)"Bind FSM not found in initial search - will catch on first bind use"); } internal static bool IsHeroBindFsm(PlayMakerFSM fsm) { if ((Object)(object)fsm == (Object)null) { return false; } if (fsm.FsmName == "Bind") { GameObject gameObject = ((Component)fsm).gameObject; return (((gameObject != null) ? ((Object)gameObject).name : null) ?? "").Contains("Hero"); } return false; } internal bool IsModified(PlayMakerFSM fsm) { if (appliedEdits != null) { return (Object)(object)appliedEdits.fsm == (Object)(object)fsm; } return false; } internal void ModifyBindFSM(PlayMakerFSM fsm) { if (IsModified(fsm)) { return; } if (this.appliedEdits != null) { RevertEdits(); } try { Logger.LogInfo((object)"Modifying Bind FSM..."); AppliedEdits appliedEdits = new AppliedEdits { fsm = fsm }; appliedEdits.bindStartState = FindState(fsm, "Bind Start"); if (appliedEdits.bindStartState != null) { appliedEdits.advanceAction = new AdvanceHealAction { fsm = fsm }; InsertAction(appliedEdits.bindStartState, (FsmStateAction)(object)appliedEdits.advanceAction); Logger.LogInfo((object)"Advance heal action installed on 'Bind Start'"); } else { Logger.LogWarning((object)"'Bind Start' state not found - advance heal unavailable"); } appliedEdits.loopBindState = FindState(fsm, "Loop Bind Start"); if (appliedEdits.loopBindState != null) { appliedEdits.loopAction = new LoopHealAdjustAction { fsm = fsm }; InsertAction(appliedEdits.loopBindState, (FsmStateAction)(object)appliedEdits.loopAction); Logger.LogInfo((object)"Multibind loop adjust installed on 'Loop Bind Start'"); } else { Logger.LogWarning((object)"'Loop Bind Start' state not found - multibind totals may be off"); } appliedEdits.warriorState = FindState(fsm, "Warrior Kickup"); if (appliedEdits.warriorState != null) { appliedEdits.beastAction = new BeastAdvanceAction(); InsertAction(appliedEdits.warriorState, (FsmStateAction)(object)appliedEdits.beastAction); Logger.LogInfo((object)"Beast advance installed on 'Warrior Kickup'"); } else { Logger.LogWarning((object)"'Warrior Kickup' state not found - Beast advance unavailable"); } this.appliedEdits = appliedEdits; } catch (Exception arg) { Logger.LogError((object)$"Failed to modify bind FSM: {arg}"); } } private void RevertEdits() { if (appliedEdits == null) { return; } try { if ((Object)(object)appliedEdits.fsm != (Object)null) { if (appliedEdits.bindStartState != null && appliedEdits.advanceAction != null) { RemoveAction(appliedEdits.bindStartState, (FsmStateAction)(object)appliedEdits.advanceAction); } if (appliedEdits.loopBindState != null && appliedEdits.loopAction != null) { RemoveAction(appliedEdits.loopBindState, (FsmStateAction)(object)appliedEdits.loopAction); } if (appliedEdits.warriorState != null && appliedEdits.beastAction != null) { RemoveAction(appliedEdits.warriorState, (FsmStateAction)(object)appliedEdits.beastAction); } Logger.LogInfo((object)"Bind FSM edits reverted"); } } catch (Exception arg) { Logger.LogError((object)$"Error reverting FSM edits: {arg}"); } appliedEdits = null; } private static FsmState FindState(PlayMakerFSM fsm, string name) { FsmState[] fsmStates = fsm.FsmStates; foreach (FsmState val in fsmStates) { if (val.Name == name) { return val; } } return null; } private static void InsertAction(FsmState state, FsmStateAction action) { List list = new List(state.Actions); list.Insert(0, action); state.Actions = list.ToArray(); } private static void RemoveAction(FsmState state, FsmStateAction action) { List list = new List(state.Actions); if (list.Remove(action)) { state.Actions = list.ToArray(); } } internal static bool LifebloodBlocks() { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return true; } if (instance.IsInLifebloodState) { return !configLifebloodAdvance.Value; } return false; } internal static bool IsMaggoted() { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null && instance.cState != null) { return instance.cState.isMaggoted; } return false; } internal static int GrantAdvance(int amount) { PlayerData instance = PlayerData.instance; int health = instance.health; HeroController.instance.AddHealth(amount); return instance.health - health; } } public class AdvanceHealAction : FsmStateAction { public PlayMakerFSM fsm; public override void OnEnter() { try { SilkSuture.lastAdvanceGranted = 0; SilkSuture.witchAdvanceGranted = 0; SilkSuture.maggotMultibindPending = false; if (!SilkSuture.configEnabled.Value || SilkSuture.LifebloodBlocks()) { ((FsmStateAction)this).Finish(); return; } bool flag = SilkSuture.IsMaggoted(); FsmBool obj = fsm.FsmVariables.FindFsmBool("Is Witch Equipped"); if (obj != null && obj.Value) { if (flag) { if (SilkSuture.configMaggotAdvance.Value) { int num = SilkSuture.GrantAdvance(1); if (num > 0) { SilkSuture.Logger.LogInfo((object)$"Maggot advance (bonus): +{num} on witch cleanse bind"); } } } else if (SilkSuture.configWitchAdvance.Value) { SilkSuture.witchAdvanceGranted = SilkSuture.GrantAdvance(1); SilkSuture.Logger.LogInfo((object)$"Witch advance: +{SilkSuture.witchAdvanceGranted}, tentacle cap reduced to match"); } ((FsmStateAction)this).Finish(); return; } FsmInt val = fsm.FsmVariables.FindFsmInt("Heal Amount"); if (val == null || val.Value <= 0) { ((FsmStateAction)this).Finish(); return; } FsmInt obj2 = fsm.FsmVariables.FindFsmInt("Bind Amount"); bool flag2 = ((obj2 == null) ? 1 : obj2.Value) >= 2; if (flag) { if (flag2 && SilkSuture.configMaggotMultibindAdvance.Value) { int num2 = SilkSuture.GrantAdvance(Mathf.Min(1, val.Value)); if (num2 > 0) { SilkSuture.lastAdvanceGranted = num2; SilkSuture.maggotMultibindPending = true; SilkSuture.Logger.LogInfo((object)$"Maggot multibind advance: +{num2}, second burst will be reduced"); } } else if (SilkSuture.configMaggotAdvance.Value) { int num3 = SilkSuture.GrantAdvance(1); if (num3 > 0) { SilkSuture.Logger.LogInfo((object)$"Maggot advance (bonus): +{num3} on cleanse bind"); } } ((FsmStateAction)this).Finish(); return; } FsmBool obj3 = fsm.FsmVariables.FindFsmBool("Is Shaman Equipped"); if (obj3 != null && obj3.Value && !SilkSuture.configShamanAdvance.Value) { ((FsmStateAction)this).Finish(); return; } int num4 = SilkSuture.GrantAdvance(Mathf.Min(1, val.Value)); if (num4 <= 0) { ((FsmStateAction)this).Finish(); return; } if (!flag2 || SilkSuture.configMultibinderPattern.Value == "1+1+2") { val.Value -= num4; } SilkSuture.lastAdvanceGranted = num4; SilkSuture.Logger.LogInfo((object)($"Advance heal: +{num4}, Heal Amount now {val.Value}" + (flag2 ? (" (multibind " + SilkSuture.configMultibinderPattern.Value + ")") : ""))); } catch (Exception arg) { SilkSuture.Logger.LogError((object)$"Error in AdvanceHealAction: {arg}"); } ((FsmStateAction)this).Finish(); } } public class LoopHealAdjustAction : FsmStateAction { public PlayMakerFSM fsm; public override void OnEnter() { try { int lastAdvanceGranted = SilkSuture.lastAdvanceGranted; if (!SilkSuture.configEnabled.Value || lastAdvanceGranted <= 0) { goto IL_0039; } FsmBool obj = fsm.FsmVariables.FindFsmBool("Is Witch Equipped"); if (obj != null && obj.Value) { goto IL_0039; } FsmInt val = fsm.FsmVariables.FindFsmInt("Heal Amount"); if (val != null) { int value = val.Value; if (SilkSuture.maggotMultibindPending) { val.Value = Mathf.Max(0, val.Value - lastAdvanceGranted); SilkSuture.maggotMultibindPending = false; } else if (SilkSuture.configMultibinderPattern.Value == "1+2+1") { val.Value = Mathf.Max(0, val.Value - lastAdvanceGranted); } else { val.Value += lastAdvanceGranted; } SilkSuture.Logger.LogInfo((object)$"Multibind loop adjust: Heal Amount {value} -> {val.Value}"); } SilkSuture.lastAdvanceGranted = 0; goto end_IL_0000; IL_0039: ((FsmStateAction)this).Finish(); return; end_IL_0000:; } catch (Exception arg) { SilkSuture.Logger.LogError((object)$"Error in LoopHealAdjustAction: {arg}"); } ((FsmStateAction)this).Finish(); } } public class BeastAdvanceAction : FsmStateAction { public override void OnEnter() { try { SilkSuture.rageAdvanceGranted = 0; if (!SilkSuture.configEnabled.Value || SilkSuture.LifebloodBlocks()) { ((FsmStateAction)this).Finish(); return; } if (SilkSuture.IsMaggoted()) { if (SilkSuture.configMaggotAdvance.Value) { int num = SilkSuture.GrantAdvance(1); if (num > 0) { SilkSuture.Logger.LogInfo((object)$"Maggot advance (bonus): +{num} on Beast cleanse bind"); } } } else if (SilkSuture.configBeastAdvance.Value) { SilkSuture.rageAdvanceGranted = SilkSuture.GrantAdvance(1); SilkSuture.Logger.LogInfo((object)$"Beast advance: +{SilkSuture.rageAdvanceGranted}, rage cap reduced to match"); } } catch (Exception arg) { SilkSuture.Logger.LogError((object)$"Error in BeastAdvanceAction: {arg}"); } ((FsmStateAction)this).Finish(); } } [HarmonyPatch(typeof(PlayMakerFSM), "Awake")] public class FSMAwakePatch { [HarmonyPostfix] public static void Postfix(PlayMakerFSM __instance) { if (SilkSuture.configEnabled.Value && !((Object)(object)SilkSuture.Instance == (Object)null) && SilkSuture.IsHeroBindFsm(__instance) && !SilkSuture.Instance.IsModified(__instance)) { SilkSuture.Logger.LogInfo((object)("Found bind FSM: '" + __instance.FsmName + "' on " + ((Object)((Component)__instance).gameObject).name)); SilkSuture.Instance.ModifyBindFSM(__instance); } } } [HarmonyPatch(typeof(HeroController), "CanBind")] public class CanBindPatch { [HarmonyPostfix] public static void Postfix(bool __result) { if (!__result || !SilkSuture.configEnabled.Value || (Object)(object)SilkSuture.Instance == (Object)null) { return; } SilkSuture.witchAdvanceGranted = 0; PlayMakerFSM[] array = Object.FindObjectsOfType(); foreach (PlayMakerFSM val in array) { if (SilkSuture.IsHeroBindFsm(val)) { if (!SilkSuture.Instance.IsModified(val)) { SilkSuture.Logger.LogInfo((object)("Found bind FSM: '" + val.FsmName + "' on " + ((Object)((Component)val).gameObject).name)); SilkSuture.Instance.ModifyBindFSM(val); } break; } } } } [HarmonyPatch(typeof(HeroController), "GetRageModeHealCap")] public class RageHealCapPatch { [HarmonyPostfix] public static void Postfix(ref int __result) { if (SilkSuture.configEnabled.Value && SilkSuture.configBeastAdvance.Value) { __result = Mathf.Max(0, __result - SilkSuture.rageAdvanceGranted); } } } [HarmonyPatch(typeof(HeroController), "GetWitchHealCap")] public class WitchHealCapPatch { [HarmonyPostfix] public static void Postfix(ref int __result) { if (SilkSuture.configEnabled.Value && SilkSuture.configWitchAdvance.Value) { __result = Mathf.Max(0, __result - SilkSuture.witchAdvanceGranted); } } } public static class PluginInfo { public const string PLUGIN_GUID = "com.fabulator_k.silksuture"; public const string PLUGIN_NAME = "Silk Suture"; public const string PLUGIN_VERSION = "0.5.1"; } }