Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of RandomCrestMod v0.2.6
RandomCrestMod.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
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