Decompiled source of SilkSuture v0.5.1

plugins/SilkSuture.dll

Decompiled 16 hours ago
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<bool> configEnabled;

		internal static ConfigEntry<string> configMultibinderPattern;

		internal static ConfigEntry<bool> configWitchAdvance;

		internal static ConfigEntry<bool> configBeastAdvance;

		internal static ConfigEntry<bool> configShamanAdvance;

		internal static ConfigEntry<bool> configMaggotAdvance;

		internal static ConfigEntry<bool> configMaggotMultibindAdvance;

		internal static ConfigEntry<bool> 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<bool>("General", "Enabled", true, "Enable Silk Suture healing modifications");
			configMultibinderPattern = ((BaseUnityPlugin)this).Config.Bind<string>("Healing", "MultibinderPattern", "1+1+2", new ConfigDescription("Multibinder healing split (immediate + first completion + second completion, total 4)", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "1+1+2", "1+2+1" }), Array.Empty<object>()));
			configWitchAdvance = ((BaseUnityPlugin)this).Config.Bind<bool>("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<bool>("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<bool>("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<bool>("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<bool>("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<bool>("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<PlayMakerFSM>();
			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<FsmStateAction> list = new List<FsmStateAction>(state.Actions);
			list.Insert(0, action);
			state.Actions = list.ToArray();
		}

		private static void RemoveAction(FsmState state, FsmStateAction action)
		{
			List<FsmStateAction> list = new List<FsmStateAction>(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<PlayMakerFSM>();
			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";
	}
}