Decompiled source of RandomCrestMod v0.2.6

RandomCrestMod.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using GlobalEnums;
using GlobalSettings;
using HarmonyLib;
using HutongGames.PlayMaker;
using HutongGames.PlayMaker.Actions;
using InControl;
using Microsoft.CodeAnalysis;
using RandomCrestMod.Core;
using TeamCherry.Localization;
using TeamCherry.SharedUtils;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RandomCrestMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.6.0")]
[assembly: AssemblyInformationalVersion("0.2.6+63d5860dea1bf5f61e5a03c6ffcd126cdb34f00c")]
[assembly: AssemblyProduct("RandomCrestMod")]
[assembly: AssemblyTitle("RandomCrestMod")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/LIFELAN/RandomCrestMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.6.0")]
[module: UnverifiableCode]
[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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace RandomCrestMod
{
	internal static class Attacks
	{
		private const float AttackTimeout = 4f;

		private const float NailArtMinHold = 0.3f;

		private const float NailArtTimeout = 4f;

		private const float DashGrace = 0.25f;

		private const float DashMaxHold = 10f;

		private const string DashStartState = "Start Attack";

		private const string CrestAttacksFsm = "Crest Attacks";

		private static int _requestFrame = -1;

		private static AttackDirection _requestDir;

		private static bool _requestWall;

		private static float _attackMinHold;

		private static bool _attackQuiet;

		private static float _dashLastActive;

		private static PlayMakerFSM? _nailArtsFsm;

		internal static void RequestAttack(AttackDirection dir, bool wallSliding)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			_requestFrame = Time.frameCount;
			_requestDir = dir;
			_requestWall = wallSliding;
		}

		internal static void OnUpdateConfig(HeroController hero)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (_requestFrame != Time.frameCount || ConfigLease.Restoring)
			{
				return;
			}
			_requestFrame = -1;
			if (!Gate.Attacks)
			{
				return;
			}
			try
			{
				ConfigGroup val = ConfigPool.PickForSlash(hero, _requestDir, _requestWall);
				if (val != null)
				{
					bool flag = ConfigPool.IsSprintOrSkid(hero);
					if (ConfigLease.Acquire(hero, ConfigOwner.Attack, val, flag, force: true))
					{
						_attackQuiet = flag;
						_attackMinHold = Mathf.Max(0.05f, ((Object)(object)val.Config != (Object)null) ? val.Config.AttackDuration : 0.35f);
						RandomCrestModPlugin.Log($"Random attack -> '{NameOf(val)}' dir={_requestDir}.");
					}
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[Attacks] apply failed: " + ex);
			}
		}

		internal static void OnNailArtConfirmed(HeroController hero)
		{
			if (!Gate.Attacks || ConfigLease.IsOwnedBy(ConfigOwner.NailArt))
			{
				return;
			}
			try
			{
				ConfigGroup val = ConfigPool.PickForChargeSlash(hero);
				if (val != null)
				{
					bool flag = ConfigPool.ShouldSwapQuietly(hero);
					ConfigLease.Acquire(hero, ConfigOwner.NailArt, val, flag);
					RandomCrestModPlugin.Log($"Random charge slash -> '{NameOf(val)}' (quiet={flag}).");
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[Attacks] charge slash failed: " + ex);
			}
		}

		internal static void OnDashStabStarted(HeroController hero)
		{
			if (_requestFrame == Time.frameCount || !RandomCrestModPlugin.EnableRandomAttacks)
			{
				return;
			}
			bool flag = (Object)(object)hero.sprintFSM != (Object)null && hero.sprintFSM.ActiveStateName == "Start Attack";
			ConfigOwner owner = ConfigLease.Owner;
			if ((owner != ConfigOwner.Dash && (!flag || hero.cState.isBinding || (owner != ConfigOwner.None && owner != ConfigOwner.Bind && owner != ConfigOwner.Taunt))) || !Gate.Attacks)
			{
				return;
			}
			try
			{
				ConfigGroup val = ConfigPool.PickAny(hero);
				if (val != null && ConfigLease.Acquire(hero, ConfigOwner.Dash, val, quiet: true, force: true))
				{
					SetDashStabVariables(hero, val);
					_dashLastActive = Time.time;
					RandomCrestModPlugin.Log($"Random dash stab -> '{NameOf(val)}' (re-roll, was {owner}).");
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[Attacks] dash stab failed: " + ex);
			}
		}

		internal static void Tick(HeroController? hero)
		{
			if (!((Object)(object)hero == (Object)null))
			{
				switch (ConfigLease.Owner)
				{
				case ConfigOwner.Attack:
					TickAttack(hero);
					break;
				case ConfigOwner.NailArt:
					TickNailArt(hero);
					break;
				case ConfigOwner.Dash:
					TickDash(hero);
					break;
				case ConfigOwner.None:
					TryStartSprintMoveset(hero);
					break;
				}
			}
		}

		private static void TickAttack(HeroController hero)
		{
			HeroControllerStates cState = hero.cState;
			bool flag = IsCrestAttackBusy(hero);
			float heldFor = ConfigLease.HeldFor;
			if (_attackQuiet)
			{
				if (ConfigPool.IsSprintOrSkid(hero) && heldFor < 4f)
				{
					return;
				}
			}
			else if (ConfigPool.IsSprintOrDash(cState) && !flag)
			{
				ConfigLease.Release(hero, ConfigOwner.Attack);
				return;
			}
			if ((!(cState.attacking || cState.downAttacking || cState.upAttacking || cState.downSpikeAntic || cState.downSpiking || cState.downSpikeBouncing || cState.downSpikeRecovery || cState.isToolThrowing || ConfigPool.IsSprintOrSkid(hero) || flag) && heldFor >= _attackMinHold) || heldFor > 4f)
			{
				ConfigLease.Release(hero, ConfigOwner.Attack);
			}
		}

		private static void TickNailArt(HeroController hero)
		{
			PlayMakerFSM nailArtsFsm = GetNailArtsFsm(hero);
			string text = (((Object)(object)nailArtsFsm != (Object)null) ? nailArtsFsm.ActiveStateName : null);
			bool num = !string.IsNullOrEmpty(text) && text != "Inactive";
			float heldFor = ConfigLease.HeldFor;
			if ((!num && heldFor >= 0.3f) || heldFor > 4f)
			{
				ConfigLease.Release(hero, ConfigOwner.NailArt);
			}
		}

		private static void TickDash(HeroController hero)
		{
			if (hero.cState.isSprinting || ConfigPool.SprintFsmBusy(hero))
			{
				_dashLastActive = Time.time;
			}
			else if (Time.time - _dashLastActive > 0.25f || ConfigLease.HeldFor > 10f)
			{
				ConfigLease.Release(hero, ConfigOwner.Dash);
			}
		}

		private static void TryStartSprintMoveset(HeroController hero)
		{
			HeroControllerStates cState = hero.cState;
			if (cState.isSprinting && !cState.transitioning && Gate.Attacks)
			{
				ConfigGroup val = ConfigPool.PickAny(hero);
				if (val != null && ConfigLease.Acquire(hero, ConfigOwner.Dash, val, quiet: true))
				{
					SetDashStabVariables(hero, val);
					_dashLastActive = Time.time;
					RandomCrestModPlugin.Log("Random dash -> '" + NameOf(val) + "'.");
				}
			}
		}

		private static bool IsCrestAttackBusy(HeroController hero)
		{
			PlayMakerFSM crestAttacksFSM = hero.crestAttacksFSM;
			string text = (((Object)(object)crestAttacksFSM != (Object)null) ? crestAttacksFSM.ActiveStateName : null);
			if (!string.IsNullOrEmpty(text))
			{
				return text != "Idle";
			}
			return false;
		}

		private static void SetDashStabVariables(HeroController hero, ConfigGroup group)
		{
			PlayMakerFSM sprintFSM = hero.sprintFSM;
			if ((Object)(object)sprintFSM == (Object)null)
			{
				return;
			}
			try
			{
				FsmGameObject val = sprintFSM.FsmVariables.FindFsmGameObject("Dash Stab");
				if (val != null)
				{
					val.Value = group.DashStab;
				}
				FsmGameObject val2 = sprintFSM.FsmVariables.FindFsmGameObject("Dash Stab Alt");
				if (val2 != null)
				{
					val2.Value = group.DashStabAlt;
				}
				FsmGameObject val3 = sprintFSM.FsmVariables.FindFsmGameObject("Dash Stab Crt");
				if (val3 != null)
				{
					val3.Value = group.DashStab;
				}
				if (!((Object)(object)group.Config == (Object)null))
				{
					FsmFloat val4 = sprintFSM.FsmVariables.FindFsmFloat("Attack Speed");
					if (val4 != null)
					{
						val4.Value = group.Config.DashStabSpeed;
					}
					FsmFloat val5 = sprintFSM.FsmVariables.FindFsmFloat("Attack Time");
					if (val5 != null)
					{
						val5.Value = group.Config.DashStabTime;
					}
					FsmInt val6 = sprintFSM.FsmVariables.FindFsmInt("Attack Steps");
					if (val6 != null)
					{
						val6.Value = group.Config.DashStabSteps;
					}
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[Attacks] SetDashStabVariables failed: " + ex.Message);
			}
		}

		private static PlayMakerFSM? GetNailArtsFsm(HeroController hero)
		{
			if (_nailArtsFsm == null)
			{
				_nailArtsFsm = ConfigPool.LocateFsm(hero, "Nail Arts");
			}
			return _nailArtsFsm;
		}

		private static string NameOf(ConfigGroup group)
		{
			if (!((Object)(object)group.Config != (Object)null))
			{
				return "?";
			}
			return ((Object)group.Config).name;
		}
	}
	[HarmonyPatch]
	internal static class AttackPatches
	{
		[HarmonyPatch(typeof(HeroController), "Attack", new Type[] { typeof(AttackDirection) })]
		[HarmonyPrefix]
		private static void Attack_Prefix(HeroController __instance, AttackDirection attackDir)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			HeroControllerStates cState = __instance.cState;
			Attacks.RequestAttack(attackDir, cState.wallSliding || cState.wallScrambling);
		}

		[HarmonyPatch(typeof(HeroController), "UpdateConfig")]
		[HarmonyPostfix]
		private static void UpdateConfig_Postfix(HeroController __instance)
		{
			Attacks.OnUpdateConfig(__instance);
		}

		[HarmonyPatch(typeof(HeroController), "CanNailArt")]
		[HarmonyPostfix]
		private static void CanNailArt_Postfix(HeroController __instance, ref bool __result)
		{
			if (__result)
			{
				Attacks.OnNailArtConfirmed(__instance);
			}
		}

		[HarmonyPatch(typeof(HeroController), "IncrementAttackCounter")]
		[HarmonyPostfix]
		private static void IncrementAttackCounter_Postfix(HeroController __instance)
		{
			Attacks.OnDashStabStarted(__instance);
		}
	}
	internal static class Bind
	{
		private const float AttemptTimeout = 10f;

		private const float MinHold = 0.15f;

		private const float MaxHold = 30f;

		private const string FsmName = "Bind";

		private static bool _attemptActive;

		private static int _rollFrame = -1;

		private static float _attemptStart;

		private static bool _wasBinding;

		private static bool _cancelSent;

		private static PlayMakerFSM? _bindFsm;

		internal static void OnCanBind(HeroController hero)
		{
			if (Gate.Binds && CastWasPressed())
			{
				BeginAttempt(hero);
			}
		}

		internal static void Tick(HeroController? hero)
		{
			if (!RandomCrestModPlugin.EnableRandomBind || (Object)(object)hero == (Object)null)
			{
				if (_attemptActive)
				{
					EndAttempt(hero);
				}
				return;
			}
			bool flag = IsCastHeld();
			if (Gate.Binds && (CastWasPressed() || (flag && !_attemptActive)))
			{
				BeginAttempt(hero);
			}
			if (_attemptActive && ((!flag && !hero.cState.isBinding) || Time.time - _attemptStart > 10f))
			{
				EndAttempt(hero);
			}
			TickOwner(hero);
		}

		internal static void Reset()
		{
			CursedBind.Clear();
		}

		internal static bool IsShamanAirBind(HeroController hero)
		{
			try
			{
				string text = BindState(hero);
				return text == "Shaman Air" || text == "Shaman Fall";
			}
			catch
			{
				return false;
			}
		}

		private static void BeginAttempt(HeroController hero)
		{
			if (_rollFrame == Time.frameCount || (hero.cState != null && (hero.cState.isBinding || hero.cState.transitioning)))
			{
				return;
			}
			_rollFrame = Time.frameCount;
			_attemptActive = true;
			_attemptStart = Time.time;
			CursedBind.Arm(CursedBind.Roll());
			ConfigGroup val = ConfigPool.PickAny(hero);
			if (val != null)
			{
				bool quiet = ConfigPool.IsSprintOrSkid(hero) || IsBindFsmBusy(hero);
				if (ConfigLease.Acquire(hero, ConfigOwner.Bind, val, quiet))
				{
					_wasBinding = false;
					_cancelSent = false;
					RandomCrestModPlugin.Log("Random bind -> '" + (((Object)(object)val.Config != (Object)null) ? ((Object)val.Config).name : "?") + "'.");
				}
			}
		}

		private static void EndAttempt(HeroController? hero)
		{
			_attemptActive = false;
			CursedBind.Clear();
			if (!((Object)(object)hero == (Object)null) && ConfigLease.IsOwnedBy(ConfigOwner.Bind) && !ConfigPool.IsSprintOrSkid(hero) && !IsBindFsmBusy(hero))
			{
				Release(hero);
			}
		}

		private static void TickOwner(HeroController hero)
		{
			if (!ConfigLease.IsOwnedBy(ConfigOwner.Bind))
			{
				return;
			}
			HeroControllerStates cState = hero.cState;
			if (cState.isBinding)
			{
				_wasBinding = true;
			}
			if (cState.swimming && !_cancelSent)
			{
				_cancelSent = true;
				CancelBindFsm(hero);
			}
			float heldFor = ConfigLease.HeldFor;
			if (heldFor > 30f)
			{
				Release(hero);
				return;
			}
			bool num = ConfigPool.IsSprintOrSkid(hero);
			bool flag = IsBindFsmBusy(hero);
			if (!num && !flag && _wasBinding && !cState.isBinding && heldFor >= 0.15f)
			{
				Release(hero);
			}
		}

		private static void Release(HeroController hero)
		{
			_wasBinding = false;
			_cancelSent = false;
			ConfigLease.Release(hero, ConfigOwner.Bind);
		}

		private static void CancelBindFsm(HeroController hero)
		{
			try
			{
				if (_bindFsm == null)
				{
					_bindFsm = ConfigPool.LocateFsm(hero, "Bind");
				}
				if ((Object)(object)_bindFsm != (Object)null && _bindFsm.ActiveStateName != "Idle")
				{
					_bindFsm.SendEvent("CANCEL");
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[Bind] cancel failed: " + ex.Message);
			}
		}

		private static bool IsBindFsmBusy(HeroController hero)
		{
			string text = BindState(hero);
			if (!string.IsNullOrEmpty(text))
			{
				return text != "Idle";
			}
			return false;
		}

		private static string? BindState(HeroController hero)
		{
			if (_bindFsm == null)
			{
				_bindFsm = ConfigPool.LocateFsm(hero, "Bind");
			}
			if (!((Object)(object)_bindFsm != (Object)null))
			{
				return null;
			}
			return _bindFsm.ActiveStateName;
		}

		private static bool IsCastHeld()
		{
			try
			{
				InputHandler unsafeInstance = ManagerSingleton<InputHandler>.UnsafeInstance;
				if ((Object)(object)unsafeInstance == (Object)null)
				{
					return false;
				}
				PlayerAction cast = unsafeInstance.inputActions.Cast;
				return ((OneAxisInputControl)cast).IsPressed || ((OneAxisInputControl)cast).WasPressed;
			}
			catch
			{
				return false;
			}
		}

		private static bool CastWasPressed()
		{
			try
			{
				InputHandler unsafeInstance = ManagerSingleton<InputHandler>.UnsafeInstance;
				return (Object)(object)unsafeInstance != (Object)null && ((OneAxisInputControl)unsafeInstance.inputActions.Cast).WasPressed;
			}
			catch
			{
				return false;
			}
		}
	}
	internal static class CursedBind
	{
		private const string FsmName = "Bind";

		private const string DoBindState = "Do Bind";

		private const string CursedVariable = "IsAnyCursed";

		private const string CursedEventName = "CURSED";

		private const float StaleTimeout = 15f;

		private static bool _active;

		private static float _armedTime;

		internal static bool Active => _active;

		internal static bool Roll()
		{
			if (RandomCrestModPlugin.EnableCursedBind)
			{
				return Random.value < 0.05f;
			}
			return false;
		}

		internal static void Arm(bool cursed)
		{
			_active = cursed;
			_armedTime = Time.time;
			if (cursed)
			{
				RandomCrestModPlugin.LogInfo("[CursedBind] cursed bind rolled.");
			}
		}

		internal static void Clear()
		{
			_active = false;
		}

		internal static void Tick(HeroController? hero)
		{
			if (_active && (!Gate.Crest || (Object)(object)hero == (Object)null || hero.cState.dead || hero.cState.transitioning || Time.time - _armedTime > 15f))
			{
				Clear();
			}
		}

		internal static bool ShouldOverride(FsmStateAction? action, string? variableName)
		{
			if (!_active || !RandomCrestModPlugin.EnableCursedBind)
			{
				return false;
			}
			try
			{
				Fsm val = ((action != null) ? action.Fsm : null);
				return val != null && val.Name == "Bind" && val.ActiveStateName == "Do Bind" && string.Equals(variableName, "IsAnyCursed", StringComparison.Ordinal);
			}
			catch
			{
				return false;
			}
		}

		internal static FsmEvent? ResolveEvent(PlayerDataVariableTest action)
		{
			FsmEvent isExpectedEvent = action.IsExpectedEvent;
			FsmEvent isNotExpectedEvent = action.IsNotExpectedEvent;
			if (isExpectedEvent != null && isExpectedEvent.Name == "CURSED")
			{
				return isExpectedEvent;
			}
			if (isNotExpectedEvent != null && isNotExpectedEvent.Name == "CURSED")
			{
				return isNotExpectedEvent;
			}
			return isExpectedEvent ?? isNotExpectedEvent;
		}
	}
	[HarmonyPatch]
	internal static class CursedBindPatches
	{
		[HarmonyPatch(typeof(PlayerDataVariableTest), "OnEnter")]
		[HarmonyPrefix]
		private static bool PlayerDataVariableTest_OnEnter_Prefix(PlayerDataVariableTest __instance)
		{
			if (__instance == null)
			{
				return true;
			}
			try
			{
				string variableName = ((__instance.VariableName != null) ? __instance.VariableName.Value : null);
				if (!CursedBind.ShouldOverride((FsmStateAction?)(object)__instance, variableName))
				{
					return true;
				}
				FsmEvent val = CursedBind.ResolveEvent(__instance);
				if (val != null && ((FsmStateAction)__instance).Fsm != null)
				{
					((FsmStateAction)__instance).Fsm.Event(val);
				}
				((FsmStateAction)__instance).Finish();
				RandomCrestModPlugin.LogInfo("[CursedBind] Diverted the bind into the Cursed branch.");
				return false;
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[CursedBind] override failed: " + ex.Message);
				return true;
			}
		}
	}
	[HarmonyPatch]
	internal static class BindStartPatches
	{
		[HarmonyPatch(typeof(HeroController), "CanBind")]
		[HarmonyPostfix]
		private static void CanBind_Postfix(HeroController __instance)
		{
			Bind.OnCanBind(__instance);
		}
	}
	[HarmonyPatch]
	internal static class CursedBindRewardPatch
	{
		[HarmonyPatch(typeof(HeroController), "TakeSilk", new Type[]
		{
			typeof(int),
			typeof(SilkTakeSource)
		})]
		[HarmonyPostfix]
		private static void TakeSilk_Postfix(int amount, SilkTakeSource source)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)source == 2 && RandomCrestModPlugin.EnableCursedBind && CursedBind.Active)
			{
				RewardLedger.PayCursedSilk(amount);
			}
		}
	}
	internal static class ShamanBridge
	{
		internal static bool ForceSpellCrestForWater { get; set; }

		internal static bool SpoofingShaman
		{
			get
			{
				if (!ConfigLease.IsHeld || (Object)(object)ConfigLease.SpoofCrest == (Object)null)
				{
					return false;
				}
				try
				{
					return ConfigLease.SpoofCrest == Gameplay.SpellCrest;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static bool IsShamanCrestEquippedForTransition(HeroController hero)
		{
			if ((Object)(object)hero == (Object)null)
			{
				return false;
			}
			if (hero.playerData != null && hero.playerData.CurrentCrestID == "Spell")
			{
				return true;
			}
			return SpoofingShaman;
		}
	}
	[HarmonyPatch]
	internal static class ShamanWaterPatches
	{
		[HarmonyPatch(typeof(SurfaceWaterRegion), "OnTriggerEnter2D")]
		[HarmonyPrefix]
		private static void OnTriggerEnter2D_Prefix(Collider2D collision)
		{
			try
			{
				if ((Object)(object)collision == (Object)null || !RandomCrestModPlugin.EnableRandomBind)
				{
					ShamanBridge.ForceSpellCrestForWater = false;
					return;
				}
				HeroController component = ((Component)collision).GetComponent<HeroController>();
				ShamanBridge.ForceSpellCrestForWater = (Object)(object)component != (Object)null && component.cState.isBinding && Bind.IsShamanAirBind(component);
			}
			catch
			{
				ShamanBridge.ForceSpellCrestForWater = false;
			}
		}

		[HarmonyPatch(typeof(SurfaceWaterRegion), "OnTriggerEnter2D")]
		[HarmonyPostfix]
		private static void OnTriggerEnter2D_Postfix()
		{
			ShamanBridge.ForceSpellCrestForWater = false;
		}

		[HarmonyPatch(typeof(TransitionPoint), "TryDoTransition")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> TryDoTransition_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo original = AccessTools.Method(typeof(HeroController), "IsShamanCrestEquipped", (Type[])null, (Type[])null);
			MethodInfo replacement = AccessTools.Method(typeof(ShamanBridge), "IsShamanCrestEquippedForTransition", (Type[])null, (Type[])null);
			foreach (CodeInstruction instruction in instructions)
			{
				yield return (CodeInstruction)((!(original != null) || !CodeInstructionExtensions.Calls(instruction, original)) ? ((object)instruction) : ((object)new CodeInstruction(OpCodes.Call, (object)replacement)));
			}
		}
	}
	internal static class LifebloodSyringeService
	{
		private const string AddBlueHealthEvent = "ADD BLUE HEALTH";

		private const string ToolHealState = "Heal";

		private const string BlueHealthFsmName = "Blue Health Control";

		private const string BlueHealthIdleState = "Idle";

		private const int ExtraAdds = 2;

		private const float AddGap = 0.1f;

		private const float QueueTimeout = 3f;

		private static int _extraAddsRemaining;

		private static float _nextAddTime;

		private static float _queueExpiry;

		private static PlayMakerFSM? _blueHealthFsm;

		internal static void OnAddBlueHealth(SendEventToRegister action)
		{
			try
			{
				if (!Gate.Tools || !string.Equals((action.eventName != null) ? action.eventName.Value : null, "ADD BLUE HEALTH", StringComparison.Ordinal))
				{
					return;
				}
				Fsm fsm = ((FsmStateAction)action).Fsm;
				if (fsm != null && !(fsm.ActiveStateName != "Heal"))
				{
					PlayerData instance = PlayerData.instance;
					if (instance != null && instance.HasLifebloodSyringeGland)
					{
						_extraAddsRemaining = 2;
						_nextAddTime = Time.time + 0.1f;
						_queueExpiry = Time.time + 3f;
						RandomCrestModPlugin.Log($"[LifebloodSyringe] Plasmium Gland owned; queued {2} extra blue masks.");
					}
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[LifebloodSyringe] boost failed: " + ex.Message);
			}
		}

		internal static void Tick()
		{
			if (_extraAddsRemaining <= 0)
			{
				return;
			}
			if (Time.time > _queueExpiry)
			{
				_extraAddsRemaining = 0;
			}
			else
			{
				if (Time.time < _nextAddTime)
				{
					return;
				}
				PlayMakerFSM val = FindBlueHealthFsm();
				if (!((Object)(object)val == (Object)null) && val.Fsm != null && !(val.ActiveStateName != "Idle"))
				{
					try
					{
						EventRegister.SendEvent("ADD BLUE HEALTH", (GameObject)null);
						_extraAddsRemaining--;
						_nextAddTime = Time.time + 0.1f;
					}
					catch (Exception ex)
					{
						RandomCrestModPlugin.LogError("[LifebloodSyringe] extra add failed: " + ex.Message);
						_extraAddsRemaining = 0;
					}
				}
			}
		}

		private static PlayMakerFSM? FindBlueHealthFsm()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_blueHealthFsm != (Object)null && _blueHealthFsm.Fsm != null)
			{
				return _blueHealthFsm;
			}
			_blueHealthFsm = null;
			try
			{
				PlayMakerFSM[] array = Resources.FindObjectsOfTypeAll<PlayMakerFSM>();
				foreach (PlayMakerFSM val in array)
				{
					if ((Object)(object)val == (Object)null || val.Fsm == null || val.Fsm.Name != "Blue Health Control")
					{
						continue;
					}
					GameObject gameObject = ((Component)val).gameObject;
					Scene scene = gameObject.scene;
					if (((Scene)(ref scene)).IsValid())
					{
						scene = gameObject.scene;
						if (((Scene)(ref scene)).isLoaded)
						{
							_blueHealthFsm = val;
							break;
						}
					}
				}
			}
			catch
			{
				_blueHealthFsm = null;
			}
			return _blueHealthFsm;
		}
	}
	[HarmonyPatch]
	internal static class LifebloodSyringePatch
	{
		[HarmonyPatch(typeof(SendEventToRegister), "OnEnter")]
		[HarmonyPostfix]
		private static void SendEventToRegister_OnEnter_Postfix(SendEventToRegister __instance)
		{
			LifebloodSyringeService.OnAddBlueHealth(__instance);
		}
	}
	[HarmonyPatch]
	internal static class ParryAutoCounterPatch
	{
		[HarmonyPatch(typeof(HeroController), "SceneInit")]
		[HarmonyPostfix]
		private static void HeroController_SceneInit_Postfix(HeroController __instance)
		{
			try
			{
				ParryAutoCounterService.Apply(__instance);
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[ParryAutoCounter] scene init failed: " + ex.Message);
			}
		}
	}
	internal static class ParryAutoCounterService
	{
		private const string FsmName = "Silk Specials";

		private const string StanceStartState = "Parry Start";

		private const string StanceState = "Parry Stance";

		private const string ClashState = "Parry Clash";

		private const string CounterState = "Change Facing?";

		private const string FailState = "Parry Recover";

		private const string RecoverState = "Parry Recover";

		private const string FinishedEvent = "FINISHED";

		private const float AutoCounterSuccessChance = 0.5f;

		private static string? _lastState;

		private static bool _autoCounterHits = true;

		internal static void Tick()
		{
			Apply(HeroController.instance);
		}

		internal static void Apply(HeroController? hero)
		{
			PlayMakerFSM val = (((Object)(object)hero != (Object)null) ? hero.silkSpecialFSM : null);
			Fsm val2 = (((Object)(object)val != (Object)null) ? val.Fsm : null);
			if (val2 == null || val2.Name != "Silk Specials")
			{
				return;
			}
			string activeStateName = val.ActiveStateName;
			if (activeStateName != _lastState)
			{
				if (activeStateName == "Parry Start" || _lastState == "Parry Clash")
				{
					ParryClashTrigger.Reset();
				}
				if (activeStateName == "Parry Clash" && !ParryClashTrigger.Attacked)
				{
					_autoCounterHits = RandomCrestModPlugin.ParryAutoCounterAlwaysSucceeds.Value || Random.value < 0.5f;
					RandomCrestModPlugin.LogInfo("[ParryAutoCounter] auto counter roll -> " + (_autoCounterHits ? "hit" : "whiff") + ".");
				}
				else if (activeStateName != "Parry Clash")
				{
					_autoCounterHits = true;
				}
				_lastState = activeStateName;
			}
			bool redirect = RandomCrestModPlugin.EnableParryAutoCounter && CrestService.IsRandomCrestEquipped();
			SetRedirect(val2, redirect, _autoCounterHits);
		}

		private static void SetRedirect(Fsm fsm, bool redirect, bool autoCounterHits)
		{
			FsmState state = fsm.GetState("Parry Stance");
			FsmState state2 = fsm.GetState("Parry Clash");
			FsmState state3 = fsm.GetState("Parry Recover");
			if (state == null || state2 == null || state3 == null)
			{
				return;
			}
			FsmTransition[] transitions = state.Transitions;
			foreach (FsmTransition val in transitions)
			{
				if (val.EventName != "FINISHED")
				{
					continue;
				}
				if (redirect)
				{
					if (val.ToState == "Parry Recover")
					{
						val.ToState = "Parry Clash";
						val.ToFsmState = state2;
					}
				}
				else if (val.ToState == "Parry Clash")
				{
					val.ToState = "Parry Recover";
					val.ToFsmState = state3;
				}
			}
			FsmState state4 = fsm.GetState("Change Facing?");
			FsmState state5 = fsm.GetState("Parry Recover");
			if (state4 == null || state5 == null)
			{
				return;
			}
			transitions = state2.Transitions;
			foreach (FsmTransition val2 in transitions)
			{
				if (val2.EventName != "FINISHED")
				{
					continue;
				}
				if (redirect && !autoCounterHits)
				{
					if (val2.ToState == "Change Facing?")
					{
						val2.ToState = "Parry Recover";
						val2.ToFsmState = state5;
					}
				}
				else if (val2.ToState == "Parry Recover")
				{
					val2.ToState = "Change Facing?";
					val2.ToFsmState = state4;
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class ParryClashEffectPatch
	{
		private const string EffectObject = "Parry Clash Effect";

		[HarmonyPatch(typeof(ActivateGameObject), "OnEnter")]
		[HarmonyPrefix]
		private static bool ActivateGameObject_OnEnter_Prefix(ActivateGameObject __instance)
		{
			if (RandomCrestModPlugin.EnableParryClashEffect || ParryClashTrigger.Attacked || !CrestService.IsRandomCrestEquipped())
			{
				return true;
			}
			if (__instance.activate == null || !__instance.activate.Value)
			{
				return true;
			}
			FsmOwnerDefault gameObject = __instance.gameObject;
			FsmGameObject val = ((gameObject != null) ? gameObject.GameObject : null);
			if (val == null || ((NamedVariable)val).Name != "Parry Clash Effect")
			{
				return true;
			}
			((FsmStateAction)__instance).Finish();
			return false;
		}
	}
	internal static class ParryClashTrigger
	{
		internal static bool Attacked { get; private set; }

		internal static void NotifyAttacked()
		{
			if (!Attacked)
			{
				Attacked = true;
				ParrySilkRefund.OnParried();
			}
		}

		internal static void Reset()
		{
			Attacked = false;
		}
	}
	[HarmonyPatch]
	internal static class ParryTriggerPatches
	{
		[HarmonyPatch(typeof(PlayMakerFSM), "SendEvent", new Type[] { typeof(string) })]
		[HarmonyPrefix]
		private static void PlayMakerFSM_SendEvent_Prefix(PlayMakerFSM __instance, string eventName)
		{
			if (!(eventName != "PARRIED"))
			{
				HeroController instance = HeroController.instance;
				if ((Object)(object)instance != (Object)null && __instance == instance.silkSpecialFSM)
				{
					ParryClashTrigger.NotifyAttacked();
				}
			}
		}

		[HarmonyPatch(typeof(HeroController), "CheckParry")]
		[HarmonyPrefix]
		private static void CheckParry_Prefix(HeroController __instance, out bool __state)
		{
			__state = __instance.cState.parrying;
		}

		[HarmonyPatch(typeof(HeroController), "CheckParry")]
		[HarmonyPostfix]
		private static void CheckParry_Postfix(HeroController __instance, bool __state)
		{
			if (__state && !__instance.cState.parrying)
			{
				ParryClashTrigger.NotifyAttacked();
			}
		}

		[HarmonyPatch(typeof(HeroController), "TakeDamage", new Type[]
		{
			typeof(GameObject),
			typeof(CollisionSide),
			typeof(int),
			typeof(HazardType),
			typeof(DamagePropertyFlags)
		})]
		[HarmonyPrefix]
		private static void TakeDamage_Prefix(HeroController __instance, out bool __state)
		{
			__state = __instance.cState.parrying;
		}

		[HarmonyPatch(typeof(HeroController), "TakeDamage", new Type[]
		{
			typeof(GameObject),
			typeof(CollisionSide),
			typeof(int),
			typeof(HazardType),
			typeof(DamagePropertyFlags)
		})]
		[HarmonyPostfix]
		private static void TakeDamage_Postfix(HeroController __instance, bool __state)
		{
			if (__state && !__instance.cState.parrying)
			{
				ParryClashTrigger.NotifyAttacked();
			}
		}
	}
	internal static class ParrySilkRefund
	{
		private const string FsmName = "Silk Specials";

		private const string CastState = "Parry Start";

		private static int _pending;

		internal static void Capture(int amount)
		{
			if (RandomCrestModPlugin.EnableParrySilkRefund && Gate.Spells)
			{
				_pending = amount;
			}
		}

		internal static void OnParried()
		{
			if (_pending <= 0)
			{
				return;
			}
			int pending = _pending;
			_pending = 0;
			if (RandomCrestModPlugin.EnableParrySilkRefund && Gate.Spells)
			{
				HeroController instance = HeroController.instance;
				if (instance != null)
				{
					instance.AddSilk(pending, false);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class ParrySilkRefundPatch
	{
		[HarmonyPatch(typeof(HeroController), "TakeSilk", new Type[] { typeof(int) })]
		[HarmonyPostfix]
		private static void TakeSilk_Postfix(HeroController __instance, int amount)
		{
			try
			{
				PlayMakerFSM silkSpecialFSM = __instance.silkSpecialFSM;
				if ((Object)(object)silkSpecialFSM != (Object)null && silkSpecialFSM.Fsm != null && silkSpecialFSM.Fsm.Name == "Silk Specials" && silkSpecialFSM.ActiveStateName == "Parry Start")
				{
					ParrySilkRefund.Capture(amount);
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[ParrySilkRefund] capture failed: " + ex.Message);
			}
		}
	}
	[HarmonyPatch]
	internal static class ReaperPayoutPatch
	{
		private static string? _savedCrestId;

		[HarmonyPatch(typeof(HealthManager), "TakeDamage", new Type[] { typeof(HitInstance) })]
		[HarmonyPrefix]
		private static void TakeDamage_Prefix()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			_savedCrestId = null;
			try
			{
				if (RandomCrestModPlugin.EnableRandomBind)
				{
					HeroController instance = HeroController.instance;
					PlayerData instance2 = PlayerData.instance;
					if (!((Object)(object)instance == (Object)null) && instance2 != null && instance.ReaperState.IsInReaperMode && !(instance2.CurrentCrestID == "Reaper"))
					{
						_savedCrestId = instance2.CurrentCrestID;
						instance2.CurrentCrestID = "Reaper";
					}
				}
			}
			catch
			{
				_savedCrestId = null;
			}
		}

		[HarmonyPatch(typeof(HealthManager), "TakeDamage", new Type[] { typeof(HitInstance) })]
		[HarmonyPostfix]
		private static void TakeDamage_Postfix()
		{
			if (_savedCrestId == null)
			{
				return;
			}
			try
			{
				PlayerData.instance.CurrentCrestID = _savedCrestId;
			}
			catch
			{
			}
			finally
			{
				_savedCrestId = null;
			}
		}
	}
	internal static class RewardLedger
	{
		private const string StatueItemName = "Fixer Idol";

		internal const bool StatueRespectsCap = true;

		private static int _lastPouchLevel = -1;

		internal static void PayTaunt(int shardCost, int rosaries, TauntFlavour flavour)
		{
			try
			{
				CurrencyManager.TakeShards(shardCost);
				CurrencyManager.AddGeo(rosaries);
				RandomCrestModPlugin.Log($"[RandomTaunt] {shardCost} shards -> +{rosaries} rosaries ({flavour}).");
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[RandomTaunt] payout failed: " + ex.Message);
			}
		}

		internal static void PayCursedSilk(int silkTaken)
		{
			int num = Rules.CursedRosaries(silkTaken);
			if (num > 0)
			{
				CurrencyManager.AddGeo(num);
				RandomCrestModPlugin.LogInfo($"[CursedBind] refused bind took {silkTaken} silk -> +{num} rosaries.");
			}
		}

		internal static bool TryGrantStatue()
		{
			CollectableItem itemByName = CollectableItemManager.GetItemByName("Fixer Idol");
			if ((Object)(object)itemByName == (Object)null)
			{
				return false;
			}
			if (itemByName.IsAtMax())
			{
				return false;
			}
			CollectableItemManager.AddItem(itemByName, 1);
			CollectableUIMsg.Spawn((ICollectableUIMsgItem)(object)itemByName, (CollectableUIMsg)null, false);
			return true;
		}

		internal static void Tick()
		{
			PlayerData instance = PlayerData.instance;
			if (instance == null || (Object)(object)HeroController.instance == (Object)null)
			{
				_lastPouchLevel = -1;
				return;
			}
			int toolPouchUpgrades = instance.ToolPouchUpgrades;
			if (_lastPouchLevel < 0 || toolPouchUpgrades < _lastPouchLevel)
			{
				_lastPouchLevel = toolPouchUpgrades;
			}
			else if (toolPouchUpgrades != _lastPouchLevel)
			{
				int num = toolPouchUpgrades - _lastPouchLevel;
				_lastPouchLevel = toolPouchUpgrades;
				if (Gate.Crest)
				{
					int num2 = Rules.ShardsForPouchUpgrades(num);
					CurrencyManager.AddShards(num2);
					RandomCrestModPlugin.LogInfo($"[ToolPouch] +{num} upgrade(s) -> +{num2} shards (capped by the game).");
				}
			}
		}

		internal static void OnSaveLoaded()
		{
			_lastPouchLevel = -1;
		}
	}
	[HarmonyPatch]
	internal static class ExtractorRewardPatch
	{
		private const string ExtractorToolName = "Extractor";

		private static bool _awardedThisUse;

		[HarmonyPatch(typeof(PlayMakerFSM), "SendEvent", new Type[] { typeof(string) })]
		[HarmonyPrefix]
		private static void SendEvent_Prefix(string eventName)
		{
			if (eventName == "EXTRACTOR")
			{
				_awardedThisUse = false;
			}
		}

		[HarmonyPatch(typeof(HealthManager), "TakeDamage", new Type[] { typeof(HitInstance) })]
		[HarmonyPostfix]
		private static void TakeDamage_Postfix(HitInstance hitInstance)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			try
			{
				if (!_awardedThisUse && (int)hitInstance.AttackType == 14 && Gate.Tools && !ToolItemManager.IsToolEquipped("Extractor"))
				{
					_awardedThisUse = RewardLedger.TryGrantStatue();
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[ExtractorReward] failed: " + ex.Message);
			}
		}
	}
	internal static class RuneRageDamageService
	{
		internal const float DamageMultiplierBonus = 2f;

		private const string BlastRootName = "Weaver Bomb Blast";

		private static readonly FieldRef<HeroShamanRuneEffect, DamageEnemies> DamagerRef = AccessTools.FieldRefAccess<HeroShamanRuneEffect, DamageEnemies>("damager");

		internal static void ApplyAfterRefresh(HeroShamanRuneEffect effect)
		{
			if (!Gate.Spells || (Object)(object)effect == (Object)null)
			{
				return;
			}
			try
			{
				GameObject gameObject = ((Component)effect).gameObject;
				if (!((Object)(object)gameObject == (Object)null) && ((Object)gameObject).name.StartsWith("Weaver Bomb Blast", StringComparison.Ordinal))
				{
					DamageEnemies val = DamagerRef.Invoke(effect);
					if (!((Object)(object)val == (Object)null))
					{
						val.DamageMultiplier *= 2f;
					}
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[RuneRage] damage boost failed: " + ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(HeroShamanRuneEffect), "Refresh")]
	internal static class RuneRageDamagePatch
	{
		[HarmonyPostfix]
		private static void Postfix(HeroShamanRuneEffect __instance)
		{
			RuneRageDamageService.ApplyAfterRefresh(__instance);
		}
	}
	internal static class SpellSilkRefund
	{
		private const string FsmName = "Silk Specials";

		private const string IdleState = "Idle";

		private static readonly string[] SkillCostStates = new string[7] { "Start Throw", "A Sphere Start", "A Sphere Repeat", "Silk Bomb Start", "Silk Bomb Restart", "Silk Charge Begin", "BossNeedle Cast" };

		private static bool _rolledThisCast;

		internal static bool IsSkillCostState(string? state)
		{
			if (!string.IsNullOrEmpty(state))
			{
				return Array.IndexOf<string>(SkillCostStates, state) >= 0;
			}
			return false;
		}

		internal static void OnSilkSpent(int amount)
		{
			if (_rolledThisCast)
			{
				return;
			}
			_rolledThisCast = true;
			if (!RandomCrestModPlugin.EnableSpellSilkRefund || amount <= 0)
			{
				return;
			}
			float num = Rules.SpellRefundChance(CollectedSkills());
			if (!(num <= 0f) && !(Random.value >= num))
			{
				HeroController instance = HeroController.instance;
				if (instance != null)
				{
					instance.AddSilk(amount, false);
				}
			}
		}

		internal static void Tick(HeroController? hero)
		{
			if (!Gate.Spells)
			{
				_rolledThisCast = false;
				return;
			}
			PlayMakerFSM val = (((Object)(object)hero != (Object)null) ? hero.silkSpecialFSM : null);
			if ((Object)(object)val == (Object)null || val.Fsm == null || val.Fsm.Name != "Silk Specials" || val.ActiveStateName == "Idle")
			{
				_rolledThisCast = false;
			}
		}

		private static int CollectedSkills()
		{
			try
			{
				return Mathf.Max(0, ToolItemManager.GetOwnedToolsCount((OwnToolsCheckFlags)8));
			}
			catch
			{
				return 0;
			}
		}
	}
	[HarmonyPatch]
	internal static class SpellSilkRefundPatch
	{
		[HarmonyPatch(typeof(HeroController), "TakeSilk", new Type[] { typeof(int) })]
		[HarmonyPostfix]
		private static void TakeSilk_Postfix(HeroController __instance, int amount)
		{
			try
			{
				if (Gate.Spells)
				{
					PlayMakerFSM silkSpecialFSM = __instance.silkSpecialFSM;
					if (!((Object)(object)silkSpecialFSM == (Object)null) && silkSpecialFSM.Fsm != null && !(silkSpecialFSM.Fsm.Name != "Silk Specials") && SpellSilkRefund.IsSkillCostState(silkSpecialFSM.ActiveStateName))
					{
						SpellSilkRefund.OnSilkSpent(amount);
					}
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[SpellSilkRefund] capture failed: " + ex.Message);
			}
		}
	}
	internal static class StatuePickupService
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__MarkTaken;
		}

		private const string SceneName = "Belltown";

		private const string PickupId = "RandomCrestMod_HornetStatue";

		private const string ItemName = "Fixer Idol";

		private static readonly Vector2 PickupPosition = new Vector2(97.39893f, 22.56768f);

		private static GameObject? _spawned;

		private static bool _loggedUnavailable;

		internal static void Tick()
		{
			GameManager instance = GameManager.instance;
			if (!string.Equals(((Object)(object)instance != (Object)null) ? instance.sceneName : null, "Belltown", StringComparison.OrdinalIgnoreCase))
			{
				_spawned = null;
				_loggedUnavailable = false;
			}
			else if (!((Object)(object)instance == (Object)null) && instance.CanPickupsExist() && !((Object)(object)_spawned != (Object)null) && !AlreadyTaken())
			{
				Spawn();
			}
		}

		private static bool AlreadyTaken()
		{
			try
			{
				SceneData instance = SceneData.instance;
				return instance != null && instance.PersistentBools.GetValueOrDefault("Belltown", "RandomCrestMod_HornetStatue");
			}
			catch
			{
				return false;
			}
		}

		private static void MarkTaken()
		{
			try
			{
				SceneData instance = SceneData.instance;
				if (instance != null)
				{
					instance.PersistentBools.SetValue(new PersistentItemData<bool>
					{
						ID = "RandomCrestMod_HornetStatue",
						SceneName = "Belltown",
						Value = true
					});
				}
				RandomCrestModPlugin.LogInfo("[StatuePickup] Hornet Statuette taken.");
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[StatuePickup] persist failed: " + ex.Message);
			}
		}

		private static void Spawn()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			try
			{
				CollectableItemPickup collectableItemPickupPrefab = Gameplay.CollectableItemPickupPrefab;
				CollectableItem itemByName = CollectableItemManager.GetItemByName("Fixer Idol");
				if ((Object)(object)collectableItemPickupPrefab == (Object)null || (Object)(object)itemByName == (Object)null)
				{
					if (!_loggedUnavailable)
					{
						_loggedUnavailable = true;
						RandomCrestModPlugin.Log("[StatuePickup] pickup prefab / statue item not ready yet.");
					}
					return;
				}
				CollectableItemPickup val = Object.Instantiate<CollectableItemPickup>(collectableItemPickupPrefab);
				((Object)val).name = "RandomCrestMod Hornet Statue Pickup";
				Extensions.SetPosition2D(((Component)val).transform, PickupPosition);
				val.SetItem((SavedItem)(object)itemByName, false);
				FieldInfo field = typeof(CollectableItemPickup).GetField("spriteRenderer", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null)
				{
					object? value = field.GetValue(val);
					SpriteRenderer val2 = (SpriteRenderer)((value is SpriteRenderer) ? value : null);
					if (val2 != null)
					{
						Sprite icon = itemByName.GetIcon((ReadSource)0);
						if ((Object)(object)icon != (Object)null)
						{
							val2.sprite = icon;
						}
					}
				}
				UnityEvent onPickup = val.OnPickup;
				object obj = <>O.<0>__MarkTaken;
				if (obj == null)
				{
					UnityAction val3 = MarkTaken;
					<>O.<0>__MarkTaken = val3;
					obj = (object)val3;
				}
				onPickup.AddListener((UnityAction)obj);
				_spawned = ((Component)val).gameObject;
				RandomCrestModPlugin.LogInfo("[StatuePickup] Spawned the Hornet Statuette pickup in Belltown.");
			}
			catch (Exception ex)
			{
				if (!_loggedUnavailable)
				{
					_loggedUnavailable = true;
					RandomCrestModPlugin.LogError("[StatuePickup] spawn failed: " + ex.Message);
				}
			}
		}
	}
	internal static class Taunt
	{
		internal const string FsmName = "Silk Specials";

		internal const string VoiceState = "Voice Type";

		internal const string SilkCheckState = "Silk Check";

		internal const string TauntEndState = "Taunt End Wait";

		private const string IdleState = "Idle";

		private const string RingToolName = "Shakra Ring";

		private const float SettleDelay = 0.2f;

		private const float StaleTimeout = 5f;

		private static TauntFlavour _flavour;

		private static TauntFlavour _rolled;

		private static bool _completed;

		private static bool _paid;

		private static float _rollTime;

		private static PlayMakerFSM? _fsm;

		internal static bool IsActive => _flavour != TauntFlavour.None;

		internal static bool IsBeast => _flavour == TauntFlavour.Beast;

		internal static bool IsRings => _flavour == TauntFlavour.Rings;

		internal static ToolCrest? BeastCrest
		{
			get
			{
				try
				{
					return Gameplay.WarriorCrest;
				}
				catch
				{
					return null;
				}
			}
		}

		internal static bool IsInState(FsmStateAction? action, string stateName)
		{
			try
			{
				Fsm val = ((action != null) ? action.Fsm : null);
				return val != null && val.Name == "Silk Specials" && val.ActiveStateName == stateName;
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsShakraRing(ToolItem? tool)
		{
			if ((Object)(object)tool != (Object)null)
			{
				return string.Equals(tool.name, "Shakra Ring", StringComparison.Ordinal);
			}
			return false;
		}

		internal static void Roll(HeroController? hero)
		{
			Settle();
			if (!Gate.Taunts)
			{
				Clear();
				return;
			}
			try
			{
				_rolled = Rules.RollTaunt(Random.value);
				_flavour = _rolled;
				_rollTime = Time.time;
				_completed = false;
				_paid = false;
				if (_flavour == TauntFlavour.Beast && !TakeBeastConfig(hero))
				{
					_flavour = TauntFlavour.Standard;
				}
				if (_flavour != TauntFlavour.Beast && (Object)(object)hero != (Object)null)
				{
					ConfigLease.Release(hero, ConfigOwner.Taunt);
					if (ConfigLease.IsHeld && ConfigLease.SpoofCrest == BeastCrest)
					{
						ConfigLease.Release(hero, ConfigLease.Owner);
					}
				}
				RandomCrestModPlugin.Log($"[RandomTaunt] rolled '{_rolled}', playing '{_flavour}' " + $"(held={ConfigLease.IsHeld}, owner={ConfigLease.Owner}, " + "spoof=" + (((Object)(object)ConfigLease.SpoofCrest != (Object)null) ? ConfigLease.SpoofCrest.name : "null") + ").");
			}
			catch (Exception ex)
			{
				Clear();
				RandomCrestModPlugin.LogError("[RandomTaunt] Roll failed: " + ex.Message);
			}
		}

		internal static void Tick(HeroController? hero)
		{
			if (!IsActive)
			{
				return;
			}
			if (!Gate.Taunts)
			{
				Clear();
				return;
			}
			PlayMakerFSM fsm = GetFsm(hero);
			if ((Object)(object)fsm != (Object)null && fsm.ActiveStateName == "Idle" && Time.time - _rollTime > 0.2f)
			{
				Settle();
			}
			else if (Time.time - _rollTime > 5f)
			{
				Settle();
			}
		}

		internal static void NotifyCompleted()
		{
			if (IsActive)
			{
				_completed = true;
			}
		}

		internal static void Reset()
		{
			Clear();
			_fsm = null;
		}

		private static void Settle()
		{
			Pay();
			Clear();
		}

		private static void Pay()
		{
			if (!_paid)
			{
				_paid = true;
				if (_rolled != TauntFlavour.None && RandomCrestModPlugin.EnableTauntShardOffer && PlayerData.HasInstance && Rules.CanOfferTaunt(_completed, PlayerData.instance.ShellShards))
				{
					int standardRoll = Random.Range(0, 51);
					RewardLedger.PayTaunt(80, Rules.TauntRosaries(_rolled, standardRoll), _rolled);
				}
			}
		}

		private static void Clear()
		{
			_flavour = TauntFlavour.None;
			_rolled = TauntFlavour.None;
			_completed = false;
			HeroController instance = HeroController.instance;
			if ((Object)(object)instance != (Object)null)
			{
				ConfigLease.Release(instance, ConfigOwner.Taunt);
			}
		}

		private static bool TakeBeastConfig(HeroController? hero)
		{
			ToolCrest beastCrest = BeastCrest;
			if ((Object)(object)hero == (Object)null || (Object)(object)beastCrest == (Object)null)
			{
				return false;
			}
			if (ConfigLease.IsHeld)
			{
				if (ConfigLease.Owner != ConfigOwner.Dash || ConfigPool.IsSprintOrSkid(hero))
				{
					return false;
				}
				ConfigLease.Release(hero, ConfigOwner.Dash);
			}
			ConfigGroup val = ConfigPool.Find(hero, beastCrest);
			if (val != null)
			{
				return ConfigLease.Acquire(hero, ConfigOwner.Taunt, val, quiet: true);
			}
			return false;
		}

		private static PlayMakerFSM? GetFsm(HeroController? hero)
		{
			if ((Object)(object)_fsm != (Object)null)
			{
				return _fsm;
			}
			if ((Object)(object)hero != (Object)null)
			{
				_fsm = ConfigPool.LocateFsm(hero, "Silk Specials");
			}
			return _fsm;
		}
	}
	[HarmonyPatch]
	internal static class TauntPatches
	{
		private static FieldRef<ListenForTauntV2, InputHandler>? _inputHandlerRef;

		[HarmonyPatch(typeof(ListenForTauntV2), "OnUpdate")]
		[HarmonyPrefix]
		private static void ListenForTauntV2_OnUpdate_Prefix(ListenForTauntV2 __instance)
		{
			if (!RandomCrestModPlugin.EnableRandomTaunt)
			{
				return;
			}
			try
			{
				Fsm fsm = ((FsmStateAction)__instance).Fsm;
				if (fsm != null && !(fsm.Name != "Silk Specials"))
				{
					if (_inputHandlerRef == null)
					{
						_inputHandlerRef = AccessTools.FieldRefAccess<ListenForTauntV2, InputHandler>("inputHandler");
					}
					InputHandler val = _inputHandlerRef.Invoke(__instance);
					if ((Object)(object)val != (Object)null && ((OneAxisInputControl)val.inputActions.Taunt).WasPressed)
					{
						Taunt.Roll(HeroController.instance);
					}
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[RandomTaunt] listener prefix failed: " + ex.Message);
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void CheckIfCrestEquipped_IsTrue_Postfix(CheckIfCrestEquipped __instance, ref bool __result)
		{
			if (Taunt.IsActive && Taunt.IsInState((FsmStateAction?)(object)__instance, "Voice Type"))
			{
				ToolCrest val = (ToolCrest)((__instance.Crest != null) ? /*isinst with value type is only supported in some contexts*/: null);
				if ((Object)(object)val != (Object)null && val == Taunt.BeastCrest)
				{
					__result = Taunt.IsBeast;
				}
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void CheckIfToolEquipped_IsTrue_Postfix(CheckIfToolEquipped __instance, ref bool __result)
		{
			if (Taunt.IsActive && Taunt.IsInState((FsmStateAction?)(object)__instance, "Silk Check") && Taunt.IsShakraRing((ToolItem?)((__instance.Tool != null) ? /*isinst with value type is only supported in some contexts*/: null)))
			{
				__result = Taunt.IsRings;
			}
		}

		[HarmonyPatch(typeof(GetToolEquipInfo), "DoAction")]
		[HarmonyPostfix]
		private static void GetToolEquipInfo_DoAction_Postfix(GetToolEquipInfo __instance)
		{
			if (!Taunt.IsActive || !Taunt.IsInState((FsmStateAction?)(object)__instance, "Silk Check") || !Taunt.IsShakraRing((ToolItem?)((__instance.Tool != null) ? /*isinst with value type is only supported in some contexts*/: null)))
			{
				return;
			}
			if (!Taunt.IsRings)
			{
				if (__instance.StoreIsEquipped != null)
				{
					__instance.StoreIsEquipped.Value = false;
				}
				if (__instance.StoreAmountLeft != null)
				{
					__instance.StoreAmountLeft.Value = 0;
				}
				return;
			}
			if (__instance.StoreIsEquipped != null)
			{
				__instance.StoreIsEquipped.Value = true;
			}
			if (__instance.StoreIsUnlocked != null)
			{
				__instance.StoreIsUnlocked.Value = true;
			}
			if (__instance.StoreAmountLeft != null)
			{
				__instance.StoreAmountLeft.Value = Mathf.Max(__instance.StoreAmountLeft.Value, 10);
			}
			if (__instance.StoreMaxAmount != null)
			{
				__instance.StoreMaxAmount.Value = Mathf.Max(__instance.StoreMaxAmount.Value, 10);
			}
		}

		[HarmonyPatch(typeof(IntTestToBool), "DoCompare")]
		[HarmonyPostfix]
		private static void IntTestToBool_DoCompare_Postfix(IntTestToBool __instance)
		{
			if (Taunt.IsActive && Taunt.IsInState((FsmStateAction?)(object)__instance, "Silk Check"))
			{
				bool isRings = Taunt.IsRings;
				if (__instance.equalBool != null)
				{
					__instance.equalBool.Value = isRings;
				}
				if (__instance.lessThanBool != null)
				{
					__instance.lessThanBool.Value = isRings;
				}
				if (__instance.greaterThanBool != null)
				{
					__instance.greaterThanBool.Value = isRings;
				}
			}
		}

		[HarmonyPatch(typeof(Tk2dWatchAnimationEvents), "OnEnter")]
		[HarmonyPostfix]
		private static void Tk2dWatchAnimationEvents_OnEnter_Postfix(Tk2dWatchAnimationEvents __instance)
		{
			if (Taunt.IsInState((FsmStateAction?)(object)__instance, "Taunt End Wait"))
			{
				Taunt.NotifyCompleted();
			}
		}
	}
	[HarmonyPatch(typeof(HeroController), "GetWillThrowTool")]
	internal static class GetWillThrowToolWindowPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			ToolPool.BeginPick();
		}

		[HarmonyPostfix]
		private static void Postfix()
		{
			ToolPool.EndPick();
		}
	}
	[HarmonyPatch]
	internal static class GetBoundAttackToolPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(ToolItemManager), "GetBoundAttackTool", new Type[3]
			{
				typeof(AttackToolBinding),
				typeof(ToolEquippedReadSource),
				typeof(AttackToolBinding).MakeByRefType()
			}, (Type[])null);
		}

		[HarmonyPostfix]
		private static void Postfix(AttackToolBinding binding, ToolEquippedReadSource readSource, ref ToolItem? __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Invalid comparison between Unknown and I4
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if ((int)readSource == 0 && !((Object)(object)__result == (Object)null) && ToolPool.IsPicking && !ToolItemManager.IsCustomToolOverride && !ToolPool.IsVanillaEquip(__result))
			{
				ToolItem val = null;
				if (Gate.Tools && (int)__result.Type == 0)
				{
					val = ToolPool.PickRed(requireThrowPrefab: false);
				}
				else if (Gate.Spells && (int)__result.Type == 3)
				{
					val = ToolPool.PickSkill();
				}
				if (!((Object)(object)val == (Object)null))
				{
					ToolPool.SetSpoof(val, binding);
					__result = val;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ToolItemManager), "GetAttackToolBinding")]
	internal static class GetAttackToolBindingPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ToolItem tool, ref AttackToolBinding? __result)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (!__result.HasValue && !ToolItemManager.IsCustomToolOverride && ToolPool.IsSpoofed(tool))
			{
				__result = ToolPool.SpoofBinding;
			}
		}
	}
	[HarmonyPatch(typeof(ToolItemManager), "GetToolStorageAmount")]
	internal static class GetToolStorageAmountPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ToolItem tool, ref int __result)
		{
			if (Gate.Tools && ToolPool.IsRed(tool))
			{
				__result = ToolBudget.Capacity;
			}
		}
	}
	[HarmonyPatch(typeof(ToolItemManager), "IsToolEquipped", new Type[]
	{
		typeof(ToolItem),
		typeof(ToolEquippedReadSource)
	})]
	internal static class IsToolEquippedPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ToolItem tool, ToolEquippedReadSource readSource, ref bool __result)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if (!__result && (int)readSource == 0 && Gate.Tools && ToolPool.IsSpoofed(tool))
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(HeroController), "CanThrowTool", new Type[]
	{
		typeof(ToolItem),
		typeof(AttackToolBinding),
		typeof(bool)
	})]
	internal static class CanThrowToolPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(ToolItem tool, AttackToolBinding binding, bool reportFailure, ref bool __result)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (ToolItemManager.IsCustomToolOverride || !ToolBudget.IsOutOfUses(tool))
			{
				return true;
			}
			__result = false;
			if (reportFailure)
			{
				ToolItemManager.ReportBoundAttackToolFailed(binding);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(HeroController), "DidUseAttackTool")]
	internal static class DidUseAttackToolPatch
	{
		private static readonly FieldRef<HeroController, ToolItem> WillThrow = AccessTools.FieldRefAccess<HeroController, ToolItem>("willThrowTool");

		[HarmonyPostfix]
		private static void Postfix(HeroController __instance)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			ThrowBarrage.NotifyConsumed();
			ToolItem val = WillThrow.Invoke(__instance);
			if (!ToolItemManager.IsCustomToolOverride && (Object)(object)val != (Object)null && (int)val.Type == 0 && ToolPool.IsRed(val))
			{
				ToolBudget.Consume(val);
			}
		}
	}
	[HarmonyPatch(typeof(HeroController), "ThrowTool")]
	internal static class ThrowToolBarragePatch
	{
		[HarmonyPrefix]
		private static void Prefix(HeroController __instance)
		{
			ThrowBarrage.BeforeThrow(__instance);
		}

		[HarmonyPostfix]
		private static void Postfix(HeroController __instance, bool isAutoThrow)
		{
			ThrowBarrage.AfterThrow(__instance, isAutoThrow);
		}
	}
	[HarmonyPatch(typeof(ToolItemManager), "TryReplenishTools")]
	internal static class TryReplenishToolsPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			ToolBudget.BeginFreeRefill();
		}

		[HarmonyPostfix]
		private static void Postfix(bool doReplenish)
		{
			if (doReplenish && Gate.Tools)
			{
				ToolBudget.Reset();
			}
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception)
		{
			ToolBudget.EndFreeRefill();
			return __exception;
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class PlayerDataSilkSkillCostPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ref int __result)
		{
			if (Gate.Spells)
			{
				__result = Rules.SilkSkillCost(__result, 1);
			}
		}
	}
	[HarmonyPatch(typeof(GameManager), "SetLoadedGameData", new Type[]
	{
		typeof(SaveGameData),
		typeof(int)
	})]
	internal static class RandomToolSaveLoadedPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			Tools.OnSaveLoaded();
			RewardLedger.OnSaveLoaded();
		}
	}
	internal static class Tools
	{
		internal static void Tick(HeroController? hero)
		{
			if (!Gate.Tools)
			{
				ToolBudget.Invalidate();
				ThrowBarrage.End(hero);
				VoltToggle.Restore();
			}
			else
			{
				ThrowBarrage.Tick(hero);
				VoltToggle.Tick();
				ToolBudget.Tick();
			}
		}

		internal static void OnSaveLoaded()
		{
			ToolBudget.Invalidate();
			ThrowBarrage.End(null);
			VoltToggle.OnSaveLoaded();
			ToolPool.EnsureBuilt();
		}
	}
	internal static class ToolPool
	{
		private const string RosaryCannonName = "Rosary Cannon";

		private const string ToggleToolName = "Lightning Rod";

		private static readonly string[] VanillaEquipTools = new string[1] { "Extractor" };

		private static readonly string[] FreeThrowExcluded = new string[2] { "Extractor", "Silk Snare" };

		private static readonly List<ToolItem> RedPool = new List<ToolItem>();

		private static readonly List<ToolItem> SkillPool = new List<ToolItem>();

		private static readonly List<ToolItem> ThrowablePool = new List<ToolItem>();

		private static bool _built;

		private static bool _picking;

		private static ToolItem? _spoofTool;

		private static AttackToolBinding _spoofBinding;

		internal static ToolItem? RosaryCannon { get; private set; }

		internal static IReadOnlyList<ToolItem> RedTools => RedPool;

		internal static ToolItem? SpoofTool => _spoofTool;

		internal static AttackToolBinding SpoofBinding => _spoofBinding;

		internal static bool IsPicking => _picking;

		internal static void BeginPick()
		{
			_picking = true;
		}

		internal static void EndPick()
		{
			_picking = false;
		}

		internal static void EnsureBuilt()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Invalid comparison between Unknown and I4
			if ((_built && RedPool.Count > 0) || (Object)(object)ManagerSingleton<ToolItemManager>.UnsafeInstance == (Object)null)
			{
				return;
			}
			RedPool.Clear();
			SkillPool.Clear();
			RosaryCannon = null;
			foreach (ToolItem allTool in ToolItemManager.GetAllTools())
			{
				if ((Object)(object)allTool == (Object)null)
				{
					continue;
				}
				ToolItemType type = allTool.Type;
				if ((int)type != 0)
				{
					if ((int)type == 3)
					{
						SkillPool.Add(allTool);
					}
					continue;
				}
				RedPool.Add(allTool);
				if (string.Equals(allTool.name, "Rosary Cannon", StringComparison.Ordinal))
				{
					RosaryCannon = allTool;
				}
			}
			_built = true;
			RandomCrestModPlugin.Log($"[RandomTool] pools built: red={RedPool.Count}, skill={SkillPool.Count}.");
		}

		internal static void Invalidate()
		{
			_built = false;
		}

		internal static bool IsRed(ToolItem? tool)
		{
			if ((Object)(object)tool != (Object)null && !IsVanillaEquip(tool))
			{
				return RedPool.Contains(tool);
			}
			return false;
		}

		internal static bool IsVanillaEquip(ToolItem? tool)
		{
			if ((Object)(object)tool != (Object)null)
			{
				return Array.Exists(VanillaEquipTools, (string name) => string.Equals(tool.name, name, StringComparison.OrdinalIgnoreCase));
			}
			return false;
		}

		internal static bool IsFreeThrowExcluded(ToolItem tool)
		{
			return Array.Exists(FreeThrowExcluded, (string name) => string.Equals(tool.name, name, StringComparison.OrdinalIgnoreCase));
		}

		internal static bool IsSpoofed(ToolItem? tool)
		{
			if ((Object)(object)tool != (Object)null)
			{
				return tool == _spoofTool;
			}
			return false;
		}

		internal static void SetSpoof(ToolItem tool, AttackToolBinding binding)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			_spoofTool = tool;
			_spoofBinding = binding;
		}

		internal static void ClearSpoof()
		{
			_spoofTool = null;
		}

		internal static ToolItem? PickRed(bool requireThrowPrefab)
		{
			EnsureBuilt();
			ToolItem val;
			if (requireThrowPrefab)
			{
				ThrowablePool.Clear();
				foreach (ToolItem item in RedPool)
				{
					if (HasThrowPrefab(item))
					{
						ThrowablePool.Add(item);
					}
				}
				val = Pick(ThrowablePool);
			}
			else
			{
				val = Pick(RedPool);
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			ToolBudget.Mirror(val);
			if (val.name == "Lightning Rod")
			{
				VoltToggle.Roll();
			}
			return val;
		}

		internal static ToolItem? PickSkill()
		{
			EnsureBuilt();
			return Pick(SkillPool);
		}

		private static ToolItem? Pick(List<ToolItem> pool)
		{
			if (pool.Count != 0)
			{
				return pool[Random.Range(0, pool.Count)];
			}
			return null;
		}

		private static bool HasThrowPrefab(ToolItem tool)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return (Object)(object)tool.Usage.ThrowPrefab != (Object)null;
			}
			catch
			{
				return false;
			}
		}
	}
	internal static class ToolBudget
	{
		private static readonly Dictionary<ToolItem, ReplenishResources> SavedResources = new Dictionary<ToolItem, ReplenishResources>();

		private static FieldInfo? _replenishField;

		private static int _left;

		private static bool _initialized;

		internal static int Left => _left;

		internal static int Capacity => Rules.UsesPerBench(RandomCrestModPlugin.ToolUsesPerBench.Value, PouchLevel);

		internal static int PouchLevel
		{
			get
			{
				try
				{
					return (PlayerData.instance != null) ? Math.Max(0, PlayerData.instance.ToolPouchUpgrades) : 0;
				}
				catch
				{
					return 0;
				}
			}
		}

		internal static bool IsOutOfUses(ToolItem? tool)
		{
			if (Gate.Tools && _left <= 0)
			{
				return ToolPool.IsRed(tool);
			}
			return false;
		}

		internal static void Tick()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized)
			{
				_initialized = true;
				Reset();
			}
			ToolItem rosaryCannon = ToolPool.RosaryCannon;
			if ((Object)(object)rosaryCannon != (Object)null)
			{
				int amountLeft = rosaryCannon.SavedData.AmountLeft;
				if (amountLeft > _left)
				{
					_left = Math.Min(amountLeft, Capacity);
				}
				SetAmount(rosaryCannon, _left);
			}
		}

		internal static void Invalidate()
		{
			_initialized = false;
		}

		internal static void Reset()
		{
			ToolPool.EnsureBuilt();
			_left = Capacity;
			SyncAll();
		}

		internal static void Consume(ToolItem used)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (Gate.Tools && (int)used.Type == 0)
			{
				if (RollFreeThrow())
				{
					SyncAll();
					ToolItemManager.ReportAllBoundAttackToolsUpdated();
					return;
				}
				int amountLeft = used.SavedData.AmountLeft;
				_left = Math.Max(0, Math.Min(amountLeft, _left - 1));
				SyncAll();
				ToolItemManager.ReportAllBoundAttackToolsUpdated();
			}
		}

		internal static void SyncAll()
		{
			foreach (ToolItem redTool in ToolPool.RedTools)
			{
				if (ToolPool.IsRed(redTool))
				{
					SetAmount(redTool, _left);
				}
			}
		}

		internal static void Mirror(ToolItem tool)
		{
			if (ToolPool.IsRed(tool))
			{
				SetAmount(tool, _left);
			}
		}

		internal static void BeginFreeRefill()
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (!Gate.Tools || SavedResources.Count > 0)
			{
				return;
			}
			ToolPool.EnsureBuilt();
			if ((object)_replenishField == null)
			{
				_replenishField = AccessTools.Field(typeof(ToolItem), "replenishResource");
			}
			if (_replenishField == null)
			{
				return;
			}
			foreach (ToolItem redTool in ToolPool.RedTools)
			{
				if (!ToolPool.IsVanillaEquip(redTool))
				{
					try
					{
						SavedResources[redTool] = (ReplenishResources)_replenishField.GetValue(redTool);
						_replenishField.SetValue(redTool, (object)(ReplenishResources)(-1));
					}
					catch (Exception ex)
					{
						RandomCrestModPlugin.LogError("[RandomTool] BeginFreeRefill failed: " + ex.Message);
					}
				}
			}
		}

		internal static void EndFreeRefill()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if (SavedResources.Count == 0 || _replenishField == null)
			{
				return;
			}
			foreach (KeyValuePair<ToolItem, ReplenishResources> savedResource in SavedResources)
			{
				try
				{
					_replenishField.SetValue(savedResource.Key, savedResource.Value);
				}
				catch (Exception ex)
				{
					RandomCrestModPlugin.LogError("[RandomTool] EndFreeRefill failed: " + ex.Message);
				}
			}
			SavedResources.Clear();
		}

		private static bool RollFreeThrow()
		{
			float num = Rules.FreeThrowChance(CollectedRedTools(), CurveclawUpgraded());
			if (num > 0f)
			{
				return Random.value < num;
			}
			return false;
		}

		private static int CollectedRedTools()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			HashSet<SavedItem> hashSet = new HashSet<SavedItem>();
			try
			{
				foreach (ToolItem allTool in ToolItemManager.GetAllTools())
				{
					if ((Object)(object)allTool == (Object)null || (int)allTool.Type != 0 || ToolPool.IsFreeThrowExcluded(allTool))
					{
						continue;
					}
					try
					{
						if (allTool.SavedData.IsUnlocked)
						{
							hashSet.Add(allTool.CountKey);
						}
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[RandomTool] collected tool count failed: " + ex.Message);
			}
			return hashSet.Count;
		}

		private static bool CurveclawUpgraded()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ToolItem curveclawUpgradedTool = Gameplay.CurveclawUpgradedTool;
				return (Object)(object)curveclawUpgradedTool != (Object)null && curveclawUpgradedTool.SavedData.IsUnlocked;
			}
			catch
			{
				return false;
			}
		}

		private static void SetAmount(ToolItem tool, int amount)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Data savedData = tool.SavedData;
				if (savedData.AmountLeft != amount)
				{
					savedData.AmountLeft = amount;
					tool.SavedData = savedData;
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[RandomTool] SetAmount failed: " + ex.Message);
			}
		}
	}
	internal static class ThrowBarrage
	{
		private static FieldRef<HeroController, ToolItem>? _willThrowRef;

		private static FieldRef<HeroController, bool>? _queuedAutoThrowRef;

		private static int _remaining;

		private static AttackToolBinding _binding;

		private static bool _armed;

		private static ToolItem? _candidate;

		private static bool _consumed;

		internal static bool Armed => _armed;

		private static bool StatueHeld
		{
			get
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					PlayerData instance = PlayerData.instance;
					return instance != null && ((SerializableNamedList<Data, NamedData>)(object)instance.Collectables).GetData("Fixer Idol").Amount > 0;
				}
				catch
				{
					return false;
				}
			}
		}

		private static bool QuickSlingEquipped
		{
			get
			{
				try
				{
					return (Object)(object)Gameplay.QuickSlingTool != (Object)null && Gameplay.QuickSlingTool.Status.IsEquipped;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static void BeforeThrow(HeroController hero)
		{
			_consumed = false;
			_candidate = GetWillThrow(hero);
		}

		internal static void NotifyConsumed()
		{
			_consumed = true;
		}

		internal static void AfterThrow(HeroController hero, bool isAutoThrow)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Gate.Tools || ToolItemManager.IsCustomToolOverride)
				{
					return;
				}
				if (!_consumed || !hero.cState.isToolThrowing)
				{
					if (isAutoThrow)
					{
						End(hero);
					}
					return;
				}
				if ((Object)(object)_candidate == (Object)null || (int)_candidate.Type != 0)
				{
					End(hero);
					return;
				}
				if (!isAutoThrow)
				{
					_remaining = Rules.ExtraThrows(StatueHeld, ToolBudget.PouchLevel, QuickSlingEquipped);
					_binding = ToolPool.SpoofBinding;
					_armed = _remaining > 0;
					RandomCrestModPlugin.Log($"[RandomTool] barrage armed: +{_remaining} extra.");
				}
				else
				{
					_remaining--;
				}
				if (_remaining > 0 && ToolBudget.Left > 0)
				{
					ToolItem val = ToolPool.PickRed(requireThrowPrefab: true);
					if ((Object)(object)val != (Object)null)
					{
						SetWillThrow(hero, val);
						ToolPool.SetSpoof(val, _binding);
						SetQueuedAutoThrow(hero, value: true);
						return;
					}
				}
				End(hero);
			}
			catch (Exception ex)
			{
				End(hero);
				RandomCrestModPlugin.LogError("[RandomTool] barrage failed: " + ex.Message);
			}
		}

		internal static void Tick(HeroController? hero)
		{
			if (_armed)
			{
				HeroControllerStates val = (((Object)(object)hero != (Object)null) ? hero.cState : null);
				if ((Object)(object)hero == (Object)null || val == null || val.dead || val.hazardRespawning || val.transitioning || ToolBudget.Left <= 0 || !StatueHeld || ToolItemManager.IsCustomToolOverride)
				{
					End(hero);
				}
			}
		}

		internal static void End(HeroController? hero)
		{
			bool flag = _armed || _remaining > 0;
			_armed = false;
			_remaining = 0;
			if ((Object)(object)hero == (Object)null || !flag)
			{
				return;
			}
			try
			{
				SetQueuedAutoThrow(hero, value: false);
				SetWillThrow(hero, null);
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[RandomTool] EndChain failed: " + ex.Message);
			}
		}

		private static ToolItem? GetWillThrow(HeroController hero)
		{
			if (_willThrowRef == null)
			{
				_willThrowRef = AccessTools.FieldRefAccess<HeroController, ToolItem>("willThrowTool");
			}
			try
			{
				return _willThrowRef.Invoke(hero);
			}
			catch
			{
				return null;
			}
		}

		private static void SetWillThrow(HeroController hero, ToolItem? tool)
		{
			if (_willThrowRef == null)
			{
				_willThrowRef = AccessTools.FieldRefAccess<HeroController, ToolItem>("willThrowTool");
			}
			_willThrowRef.Invoke(hero) = tool;
		}

		private static void SetQueuedAutoThrow(HeroController hero, bool value)
		{
			if (_queuedAutoThrowRef == null)
			{
				_queuedAutoThrowRef = AccessTools.FieldRefAccess<HeroController, bool>("queuedAutoThrowTool");
			}
			_queuedAutoThrowRef.Invoke(hero) = value;
		}
	}
	internal static class VoltToggle
	{
		private const string ToggleStateField = "LightningToolToggle";

		private const float RestoreDelay = 0.5f;

		private static bool _tracked;

		private static bool _original;

		private static bool _pendingRestore;

		private static float _setTime;

		internal static void Roll()
		{
			try
			{
				PlayerData instance = PlayerData.instance;
				if (instance != null)
				{
					bool variable = VariableExtensions.GetVariable<bool>((IIncludeVariableExtensions)(object)instance, "LightningToolToggle");
					if (!_tracked)
					{
						_tracked = true;
						_original = variable;
					}
					bool flag = Random.value < 0.5f;
					if (variable != flag)
					{
						VariableExtensions.SetVariable<bool>((IIncludeVariableExtensions)(object)instance, "LightningToolToggle", flag);
					}
					_pendingRestore = true;
					_setTime = Time.time;
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[RandomTool] Voltvessels roll failed: " + ex.Message);
			}
		}

		internal static void Tick()
		{
			if (_pendingRestore && !ToolPool.IsPicking && Time.time - _setTime > 0.5f)
			{
				Restore();
			}
		}

		internal static void Restore()
		{
			if (!_tracked)
			{
				return;
			}
			try
			{
				PlayerData instance = PlayerData.instance;
				if (instance != null)
				{
					if (VariableExtensions.GetVariable<bool>((IIncludeVariableExtensions)(object)instance, "LightningToolToggle") != _original)
					{
						VariableExtensions.SetVariable<bool>((IIncludeVariableExtensions)(object)instance, "LightningToolToggle", _original);
					}
					_tracked = false;
					_pendingRestore = false;
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[RandomTool] Voltvessels restore failed: " + ex.Message);
			}
		}

		internal static void OnSaveLoaded()
		{
			_tracked = false;
			_pendingRestore = false;
		}
	}
	[HarmonyPatch]
	internal static class AnimationFallbackPatches
	{
		[HarmonyPatch(typeof(HeroAnimationController), "GetClip")]
		[HarmonyPostfix]
		private static void GetClip_Postfix(string clipName, ref tk2dSpriteAnimationClip __result)
		{
			if (__result == null)
			{
				tk2dSpriteAnimationClip val = RandomCrestAnimationLibrary.FindClip(clipName);
				if (val != null)
				{
					__result = val;
				}
			}
		}

		[HarmonyPatch(typeof(Debug), "LogError", new Type[]
		{
			typeof(object),
			typeof(Object)
		})]
		[HarmonyPrefix]
		private static bool Debug_LogError_Prefix(object message)
		{
			if (message is string text)
			{
				return !text.StartsWith("Could not resolve animation clip", StringComparison.Ordinal);
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class CrestPatches
	{
		[HarmonyPatch(typeof(GameManager), "SetLoadedGameData", new Type[]
		{
			typeof(SaveGameData),
			typeof(int)
		})]
		[HarmonyPostfix]
		private static void GameManager_SetLoadedGameData_Postfix()
		{
			CrestService.EnsureCreated();
			CrestService.EnsureUnlocked();
			HudFrameService.Reset();
			Taunt.Reset();
			Bind.Reset();
		}

		[HarmonyPatch(typeof(HeroController), "SceneInit")]
		[HarmonyPostfix]
		private static void HeroController_SceneInit_Postfix()
		{
			HudFrameService.Reset();
			Taunt.Reset();
			Bind.Reset();
		}

		[HarmonyPatch(typeof(InventoryToolCrestList), "Setup")]
		[HarmonyPrefix]
		private static void InventoryToolCrestList_Setup_Prefix()
		{
			CrestService.EnsureCreated();
			CrestService.EnsureUnlocked();
		}

		[HarmonyPatch(typeof(PlayerData), "CountGameCompletion")]
		[HarmonyPostfix]
		private static void PlayerData_CountGameCompletion_Postfix(PlayerData __instance)
		{
			if (CrestService.CountsTowardCompletion)
			{
				__instance.completionPercentage -= 1f;
			}
		}
	}
	internal static class CrestService
	{
		internal const string CrestName = "RandomCrest";

		private const string Sheet = "Tools";

		private const string NameKey = "CREST_RANDOMCREST_NAME";

		private const string DescKey = "CREST_RANDOMCREST_DESC";

		private static readonly (ToolItemType Type, AttackToolBinding Binding, float X, float Y, int Up, int Down, int Left, int Right)[] Slots = new(ToolItemType, AttackToolBinding, float, float, int, int, int, int)[6]
		{
			((ToolItemType)3, (AttackToolBinding)0, 0f, -0.9f, 1, 4, 3, 2),
			((ToolItemType)0, (AttackToolBinding)1, 0f, 0.95f, -1, 0, 3, 2),
			((ToolItemType)1, (AttackToolBinding)0, 2.5f, -1.8f, 1, 5, 0, -1),
			((ToolItemType)1, (AttackToolBinding)0, -2.5f, -1.8f, 1, 4, -1, 0),
			((ToolItemType)2, (AttackToolBinding)0, -1f, -2.7f, 3, -1, -1, 5),
			((ToolItemType)2, (AttackToolBinding)0, 1f, -2.7f, 2, -1, 4, -1)
		};

		private static float _nextCreationAttempt;

		private static LanguageCode _injectedLanguage = (LanguageCode)0;

		private static FieldInfo? _slotsField;

		private static FieldInfo? _displayNameField;

		private static FieldInfo? _descriptionField;

		private static FieldInfo? _spriteField;

		private static FieldInfo? _silhouetteField;

		private static FieldInfo? _glowField;

		private static FieldInfo? _isHiddenField;

		private static FieldInfo? _crestListField;

		internal static Sprite? CustomIcon { get; set; }

		internal static Sprite? CustomSilhouette { get; set; }

		internal static Sprite? CustomGlow { get; set; }

		internal static bool CountsTowardCompletion
		{
			get
			{
				ToolCrest crest = GetCrest();
				if ((Object)(object)crest != (Object)null && !crest.IsHidden && crest.IsBaseVersion)
				{
					return crest.IsUnlocked;
				}
				return false;
			}
		}

		internal static bool IsRandomCrestEquipped()
		{
			if (PlayerData.HasInstance)
			{
				return PlayerData.instance.CurrentCrestID == "RandomCrest";
			}
			return false;
		}

		internal static ToolCrest? GetCrest()
		{
			return ToolItemManager.GetCrestByName("RandomCrest");
		}

		internal static void EnsureCreated()
		{
			if (Time.unscaledTime < _nextCreationAttempt)
			{
				return;
			}
			try
			{
				ToolItemManager unsafeInstance = ManagerSingleton<ToolItemManager>.UnsafeInstance;
				if ((Object)(object)unsafeInstance == (Object)null)
				{
					return;
				}
				if ((object)_crestListField == null)
				{
					_crestListField = AccessTools.Field(typeof(ToolItemManager), "crestList");
				}
				object? obj = _crestListField?.GetValue(unsafeInstance);
				ToolCrestList val = (ToolCrestList)((obj is ToolCrestList) ? obj : null);
				if (val == null)
				{
					return;
				}
				if ((Object)(object)((NamedScriptableObjectList<ToolCrest>)(object)val).GetByName("RandomCrest") != (Object)null)
				{
					EnsureLocalisationCurrent();
					return;
				}
				ToolCrest crestByName = ToolItemManager.GetCrestByName("Hunter");
				if (!((Object)(object)crestByName == (Object)null))
				{
					ToolCrest val2 = Object.Instantiate<ToolCrest>(crestByName);
					val2.name = "RandomCrest";
					ApplySlots(val2);
					ApplyLocalisation(val2);
					ApplyVisuals(val2);
					((NamedScriptableObjectList<ToolCrest>)(object)val).Add(val2);
					UnlockForCurrentSave(val2);
					RandomCrestModPlugin.Log(string.Format("[CrestService] Registered crest '{0}' ({1} crests total).", "RandomCrest", ((NamedScriptableObjectList<ToolCrest>)(object)val).Count));
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[CrestService] EnsureCreated failed: " + ex);
				_nextCreationAttempt = Time.unscaledTime + 5f;
			}
		}

		internal static void EnsureUnlocked()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			ToolCrest crest = GetCrest();
			if (!((Object)(object)crest == (Object)null) && PlayerData.HasInstance)
			{
				Data data = ((SerializableNamedList<Data, NamedData>)(object)PlayerData.instance.ToolEquips).GetData("RandomCrest");
				SlotInfo[] slots = crest.Slots;
				int num = ((slots != null) ? slots.Length : 0);
				if (!data.IsUnlocked || data.Slots == null || data.Slots.Count != num)
				{
					UnlockForCurrentSave(crest);
				}
			}
		}

		private static void ApplySlots(ToolCrest crest)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			if ((object)_slotsField == null)
			{
				_slotsField = AccessTools.Field(typeof(ToolCrest), "slots");
			}
			if (!(_slotsField == null))
			{
				SlotInfo[] array = (SlotInfo[])(object)new SlotInfo[Slots.Length];
				for (int i = 0; i < Slots.Length; i++)
				{
					(ToolItemType Type, AttackToolBinding Binding, float X, float Y, int Up, int Down, int Left, int Right) tuple = Slots[i];
					ToolItemType item = tuple.Type;
					AttackToolBinding item2 = tuple.Binding;
					float item3 = tuple.X;
					float item4 = tuple.Y;
					int item5 = tuple.Up;
					int item6 = tuple.Down;
					int item7 = tuple.Left;
					int item8 = tuple.Rest.Item1;
					array[i] = new SlotInfo
					{
						Type = item,
						AttackBinding = item2,
						Position = new Vector2(item3, item4),
						NavUpIndex = item5,
						NavDownIndex = item6,
						NavLeftIndex = item7,
						NavRightIndex = item8,
						NavUpFallbackIndex = -1,
						NavDownFallbackIndex = -1,
						NavLeftFallbackIndex = -1,
						NavRightFallbackIndex = -1,
						IsLocked = false
					};
				}
				_slotsField.SetValue(crest, array);
			}
		}

		private static void ApplyLocalisation(ToolCrest crest)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			_injectedLanguage = (LanguageCode)0;
			EnsureLocalisationCurrent();
			if ((object)_displayNameField == null)
			{
				_displayNameField = AccessTools.Field(typeof(ToolCrest), "displayName");
			}
			if ((object)_descriptionField == null)
			{
				_descriptionField = AccessTools.Field(typeof(ToolCrest), "description");
			}
			_displayNameField?.SetValue(crest, (object)new LocalisedString("Tools", "CREST_RANDOMCREST_NAME"));
			_descriptionField?.SetValue(crest, (object)new LocalisedString("Tools", "CREST_RANDOMCREST_DESC"));
		}

		private static void ApplyVisuals(ToolCrest crest)
		{
			if ((object)_spriteField == null)
			{
				_spriteField = AccessTools.Field(typeof(ToolCrest), "crestSprite");
			}
			if ((object)_silhouetteField == null)
			{
				_silhouetteField = AccessTools.Field(typeof(ToolCrest), "crestSilhouette");
			}
			if ((object)_glowField == null)
			{
				_glowField = AccessTools.Field(typeof(ToolCrest), "crestGlow");
			}
			if ((object)_isHiddenField == null)
			{
				_isHiddenField = AccessTools.Field(typeof(ToolCrest), "isHidden");
			}
			if ((Object)(object)CustomIcon != (Object)null)
			{
				_spriteField?.SetValue(crest, CustomIcon);
			}
			if ((Object)(object)CustomSilhouette != (Object)null)
			{
				_silhouetteField?.SetValue(crest, CustomSilhouette);
			}
			if ((Object)(object)CustomGlow != (Object)null)
			{
				_glowField?.SetValue(crest, CustomGlow);
			}
			_isHiddenField?.SetValue(crest, false);
		}

		private static void UnlockForCurrentSave(ToolCrest crest)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (!PlayerData.HasInstance)
			{
				return;
			}
			try
			{
				PlayerData instance = PlayerData.instance;
				List<SlotData> list = new List<SlotData>();
				if (crest.Slots != null)
				{
					SlotInfo[] slots = crest.Slots;
					foreach (SlotInfo val in slots)
					{
						list.Add(new SlotData
						{
							IsUnlocked = !val.IsLocked,
							EquippedTool = string.Empty
						});
					}
				}
				Data data = ((SerializableNamedList<Data, NamedData>)(object)instance.ToolEquips).GetData("RandomCrest");
				data.IsUnlocked = true;
				data.Slots = list;
				((SerializableNamedList<Data, NamedData>)(object)instance.ToolEquips).SetData("RandomCrest", data);
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[CrestService] UnlockForCurrentSave failed: " + ex);
			}
		}

		private static void EnsureLocalisationCurrent()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			LanguageCode val = Language.CurrentLanguage();
			if (val != _injectedLanguage)
			{
				_injectedLanguage = val;
				bool flag = IsChinese(val);
				InjectLocalisation("CREST_RANDOMCREST_NAME", flag ? "纷乱" : "Chaos");
				InjectLocalisation("CREST_RANDOMCREST_DESC", flag ? "你永远不知道下一秒会发生什么。" : "You never know what will happen next.");
			}
		}

		private static bool IsChinese(LanguageCode code)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if ((int)code == 187 || code - 199 <= 4)
			{
				return true;
			}
			return false;
		}

		private static void InjectLocalisation(string key, string value)
		{
			try
			{
				Type type = typeof(LocalisedString).Assembly.GetType("TeamCherry.Localization.Language");
				if (((type != null) ? AccessTools.Field(type, "_currentEntrySheets") : null)?.GetValue(null) is Dictionary<string, Dictionary<string, string>> dictionary)
				{
					if (!dictionary.TryGetValue("Tools", out var value2) || value2 == null)
					{
						value2 = (dictionary["Tools"] = new Dictionary<string, string>());
					}
					value2[key] = value;
				}
			}
			catch (Exception ex)
			{
				RandomCrestModPlugin.LogError("[CrestService] InjectLocalisation failed: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(CountCrestUnlockPoints), "OnEnter")]
	internal static class CrestUpgraderPatches
	{
		[HarmonyPostfix]
		private static void CountCrestUnlockPoints_Postfix(CountCrestUnlockPoints __instance)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			FsmObject crestList = __instance.CrestList;
			Object obj = ((crestList != null) ? crestList.Value : null);
			ToolCrestList val = (ToolCrestList)(object)((obj is ToolCrestList) ? obj : null);
			if (val == null)
			{
				return;
			}
			ToolCrest byName = ((NamedScriptableObjectList<ToolCrest>)(object)val).GetByName("RandomCrest");
			if ((Object)(object)byName == (Object)null || byName.IsHidden || !byName.IsBaseVersion || byName.IsUpgradedVersionUnlocked)
			{
				return;
			}
			SlotInfo[] slots = byName.Slots;
			if (slots == null)
			{
				return;
			}
			if (__instance.StoreMaxPoints != null)
			{
				FsmInt storeMaxPoints = __instance.StoreMaxPoints;
				storeMaxPoints.Value -= slots.Length;
			}
			if (!byName.IsUnlocked || __instance.StoreCurrentPoints == null)
			{
				return;
			}
			Data val2 = (Data)(PlayerData.HasInstance ? ((SerializableNamedList<Data, NamedData>)(object)PlayerData.instance.ToolEquips).GetData("RandomCrest") : default(Data));
			int num = 0;
			for (int i = 0; i < slots.Length; i++)
			{
				if (!slots[i].IsLocked || (val2.Slots != null && i < val2.Slots.Count && val2.Slots[i].IsUnlocked))
				{
					num++;
				}
			}
			FsmInt storeCurrentPoints = __instance.StoreCurrentPoints;
			storeCurrentPoints.Value -= num;
		}
	}
	[HarmonyPatch]
	internal static class HeroHighlightPatch
	{
		[HarmonyPatch(typeof(HeroController), "SceneInit")]
		[HarmonyPostfix]
		private static void HeroController_SceneInit_Postfix(HeroController __instance)
		{
			HeroHighlight.Ensure(((Component)__instance).gameObject);
		}
	}
	internal sealed class HeroHighlight : MonoBehaviour
	{
		private const string FsmName = "Silk Specials";

		private const string RetreatState = "Parry Clash";

		private static readonly int FlashAmountId = Shader.PropertyToID("_FlashAmount");

		private static readonly int FlashColorId = Shader.PropertyToID("_FlashColor");

		private Renderer? _renderer;

		private MaterialPropertyBlock? _block;

		private bool _applied;

		private string? _colorHex;

		private Color _color = Color.white;

		internal static void Ensure(GameObject? hero)
		{
			if (!((Object)(object)hero == (Object)null) && !((Object)(object)hero.GetComponent<HeroHighlight>() != (Object)null))
			{
				hero.AddComponent<HeroHighlight>();
			}
		}

		private void Awake()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			_renderer = ((Component)this).GetComponent<Renderer>();
			_block = new MaterialPropertyBlock();
		}

		private void LateUpdate()
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			float num = 1f;
			if ((Object)(object)_renderer == (Object)null || _block == null)
			{
				return;
			}
			if (num > 0f && CrestService.IsRandomCrestEquipped() && !ParryClashTrigger.Attacked && IsInRetreatStep())
			{
				_renderer.GetPropertyBlock(_block);
				if (_block.GetFloat(FlashAmountId) < num)
				{
					_block.SetFloat(FlashAmountId, num);
					_block.SetColor(FlashColorId, GetColour());
					_renderer.SetPropertyBlock(_block);
				}
				_applied = true;
			}
			else if (_applied)
			{
				_applied = false;
				_renderer.GetPropertyBlock(_block);
				if (_block.GetFloat(FlashAmountId) <= num + 0.001f)
				{
					_block.SetFloat(FlashAmountId, 0f);
					_renderer.SetPropertyBlock(_block);
				}
			}
		}

		private Color GetColour()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			string text = "FFE0F0";
			if (text == _colorHex)
			{
				return _color;
			}
			_colorHex = text;
			string text2 = (string.IsNullOrEmpty(text) ? "#FFE0F0" : ((text[0] == '#') ? text : ("#" + text)));
			Color val = default(Color);
			_color = (Color)(ColorUtility.TryParseHtmlString(text2, ref val) ? val : new Color(1f, 0.88f, 0.94f, 1f));
			return _color;
		}

		private static bool IsInRetreatStep()
		{
			HeroController instance = HeroController.instance;
			PlayMakerFSM val = (((Object)(object)instance != (Object)null) ? instance.silkSpecialFSM : null);
			if ((Object)(object)val != (Object)null && val.Fsm != null && val.Fsm.Name == "Silk Specials")
			{
				return val.ActiveStateName == "Parry Clash";
			}
			return false;
		}
	}
	[HarmonyPatch]
	internal static class HudFramePatches
	{
		private static FieldInfo? _currentFrameAnimsField;

		[HarmonyPatch(typeof(BindOrbHudFrame), "FrameAppear")]
		[HarmonyPostfix]
		private static void FrameAppear_Postfix()
		{
			HudFrameService.NotifyFrameAppear();
		}

		[HarmonyPatch(typeof(BindOrbHudFrame), "FrameDisappear")]
		[HarmonyPostfix]
		private static void FrameDisappear_Postfix()
		{
			HudFrameService.NotifyFrameDisappear();
		}

		[HarmonyPatch(typeof(BindOrbHudFrame), "AlreadyAppeared")]
		[HarmonyPostfix]
		private static void AlreadyAppeared_Postfix()
		{
			HudFrameService.NotifyInstantAppear();
		}

		[HarmonyPatch(typeof(BindOrbHudFrame), "DoChangeFrame")]
		[HarmonyPrefix]
		private static void DoChangeFrame_Prefix(BindOrbHudFrame __instance)
		{
			try
			{
				if (!CrestService.IsRandomCrestEquipped())
				{
					ToolCrest hunterCrest = Gameplay.HunterCrest;
					if ((Object)(object)hunterCrest == (Object)null || !((ToolBase)hunterCrest).IsEquipped)
					{
						return;
					}
				}
				if ((object)_currentFrameAnimsField == null)
				{
					_currentFrameAnimsField = AccessTools.Field(typeof(BindOrbHudFrame), "currentFrameAnims");
				}
				_currentFrameAnimsField?.SetValue(__instance, null);
			}
			catch
			{
			}
		}
	}
	internal static class HudFrameService
	{
		private enum AnimMode
		{
			None,
			Appear,
			Disappear
		}

		private static Sprite? _sprite;

		private static Texture2D? _texture;

		private static BindOrbHudFrame? _hud;

		private static MeshRenderer? _gameRenderer;

		private static Vector3 _orbLocal = Vector3.zero;

		private static GameObject? _overlayGo;

		private static MeshFilter? _overlayFilter;

		private static MeshRenderer? _overlayRenderer;

		private static Mesh? _quad;

		private static Material? _quadMaterial;

		private static MaterialPropertyBlock? _propertyBlock;

		private static bool _showing;

		private static FieldRef<BindOrbHudFrame, Color>? _lifebloodTintRef;

		private static Color _lifebloodTint = new Color(0.5568628f, 0.8901961f, 1f, 1f);

		private static bool _tinted;

		private static bool _gameFrameRevealed;

		private static bool _weHidGameFrame;

		private static float _cachedScale = float.NaN;

		private static float _cachedOffsetX = float.NaN;

		private static float _cachedOffsetY = float.NaN;

		private const float AppearDuration = 0.5f;

		private const float DisappearDuration = 0.15f;

		private const float AppearStartReveal = 0f;

		private const float MaxRevealFrac = 1f;

		private const float DiskRevealFrac = 0.1464f;

		private const float MaskSoftness = 0.01f;

		private const int MaskGrid = 160;

		private const int MaskAngleBins = 720;

		private static AnimMode _animMode;

		private static float _animTime;

		private static float _animDuration;

		private static float _reveal = 1f;

		private static float _cachedReveal = float.NaN;

		private static bool _shown;

		private static float[]? _quadNorm;

		private static Color[]? _maskColors;

		private static Color _tintColor = Color.white;

		private static bool _tintDirty = true;

		private static bool _frameAppearPending;

		private static bool _instantAppearPending;

		private static float _waitingSince;

		private static bool _holdingForNextAppear;

		private static float _holdSince;

		private static float _disappearWaitSince;

		private static bool _sceneReset;

		private static float _sceneResetSince;

		private static float _suppressDisappearUntil;

		private static readonly bool Debug = false;

		private static float _nextDebug;

		private const float SpoolFracX = 0.14764f;

		private const float SpoolFracY = 0.52745f;

		private static bool Enab