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 CrestsEvolve v0.58.9
CrestsEvolve/CrestsEvolve.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CrestsEvolve.Bootstrap; using CrestsEvolve.Core.Balance; using CrestsEvolve.Core.Buffs; using CrestsEvolve.Core.Charms; using CrestsEvolve.Core.Combat; using CrestsEvolve.Core.Events; using CrestsEvolve.Core.Infrastructure; using CrestsEvolve.Core.Input; using CrestsEvolve.Core.Random; using CrestsEvolve.Core.Time; using CrestsEvolve.Core.Tools; using CrestsEvolve.Core.UI; using CrestsEvolve.Features; using CrestsEvolve.Features.StatusIcons; using CrestsEvolve.Features.Tools; using CrestsEvolve.GameInterop; using CrestsEvolve.GameInterop.Binding; using CrestsEvolve.GameInterop.Input; using CrestsEvolve.GameInterop.Patches; using CrestsEvolve.GameInterop.Reflection; using CrestsEvolve.UI; using GlobalEnums; using GlobalSettings; using HarmonyLib; using HutongGames.PlayMaker; using InControl; using Microsoft.CodeAnalysis; using Needleforge; using Needleforge.Data; using Newtonsoft.Json; using TeamCherry.Localization; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("CrestsEvolve.LegendsNeverDie")] [assembly: InternalsVisibleTo("CrestsEvolve.RageOfTheGoner")] [assembly: InternalsVisibleTo("CrestsEvolve.ToolsAscension")] [assembly: InternalsVisibleTo("CrestsEvolve.InsectMayCry")] [assembly: InternalsVisibleTo("CrestsEvolve.Core.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CrestsEvolve")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+757e75a2d4534028308600b7117b47c56eb48037")] [assembly: AssemblyProduct("CrestsEvolve")] [assembly: AssemblyTitle("CrestsEvolve")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } internal static class IsExternalInit { } } namespace CrestsEvolve { [BepInPlugin("local.crestsevolve", "CrestsEvolve", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "local.crestsevolve"; public const string PluginName = "CrestsEvolve"; public const string PluginVersion = "0.1.0"; private Harmony? _harmony; private ModBootstrap? _bootstrap; private void Awake() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _harmony = new Harmony("local.crestsevolve"); _bootstrap = new ModBootstrap(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config, _harmony); _bootstrap.Start(); } private void OnDestroy() { try { _bootstrap?.Dispose(); } finally { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } } namespace CrestsEvolve.UI { public sealed class LogStatusIconPresenter : IStatusIconPresenter { private readonly ManualLogSource _log; public static bool Verbose { get; set; } public LogStatusIconPresenter(ManualLogSource log) { _log = log; } public void Present(StatusIconProjection projection) { if (Verbose) { _log.LogDebug((object)($"[StatusIcons] rev={projection.Revision} " + string.Join("; ", projection.Icons.Select((StatusIconState i) => $"{i.Key}={i.Current}/{i.Max}")))); } } public void Hide() { _log.LogDebug((object)"[StatusIcons] hidden"); } } public sealed class LogUiPresenter : IUiPresenter { private readonly ManualLogSource _log; public static bool Verbose { get; set; } public LogUiPresenter(ManualLogSource log) { _log = log; } public void Present(UiProjection projection) { if (Verbose) { _log.LogDebug((object)($"[UiProjection] crest={projection.CrestId} rev={projection.Revision} " + $"stacks={projection.EnhancementStacks} remaining={projection.RemainingSeconds:F1} " + $"rage={projection.Rage} superRage={projection.SuperRage} special={projection.SpecialMode} " + $"charmOffset={projection.CharmLevelOffset} shield={projection.SilkShieldRemaining} " + $"rageRemaining={projection.RageRemainingSeconds:F1}")); } } public void Hide() { if (Verbose) { _log.LogDebug((object)"[UiProjection] hidden"); } } } public sealed record StatusIconState(string Key, int Current, int Max, bool Visible = true, string? ColorOverride = null, string? CenterText = null, bool Dimmed = false); public sealed record StatusIconProjection(long Revision, IReadOnlyList<StatusIconState> Icons); public interface IStatusIconPresenter { void Present(StatusIconProjection projection); void Hide(); } public sealed class StatusIconProjector : IDisposable { private readonly StatusIconLedger _ledger; private readonly IStatusIconPresenter _presenter; private long _lastRevision = -1L; public static StatusIconProjector? Current { get; private set; } public StatusIconProjector(StatusIconLedger ledger, IStatusIconPresenter presenter) { _ledger = ledger ?? throw new ArgumentNullException("ledger"); _presenter = presenter ?? throw new ArgumentNullException("presenter"); if (Current != null) { throw new InvalidOperationException("A StatusIconProjector is already active."); } Current = this; _ledger.Changed += OnChanged; OnChanged(_ledger.Snapshot()); } public void Refresh() { StatusIconSnapshot statusIconSnapshot = _ledger.Snapshot(); _lastRevision = statusIconSnapshot.Revision; Present(statusIconSnapshot); } private void OnChanged(StatusIconSnapshot snapshot) { if (snapshot.Revision != _lastRevision) { _lastRevision = snapshot.Revision; Present(snapshot); } } private void Present(StatusIconSnapshot snapshot) { List<StatusIconState> list = new List<StatusIconState>(snapshot.Icons.Count); foreach (StatusIconValue icon in snapshot.Icons) { bool flag = icon.Visible; if (flag && icon.Current <= 0 && !StatusIconProjectionRules.ZeroValueVisible(icon.Key)) { flag = false; } list.Add(new StatusIconState(icon.Key, icon.Current, icon.Max, flag, icon.ColorOverride, icon.Text, icon.Dimmed)); } _presenter.Present(new StatusIconProjection(snapshot.Revision, list)); } public void Dispose() { if (Current == this) { Current = null; } _ledger.Changed -= OnChanged; _presenter.Hide(); } } public sealed class CompositeStatusIconPresenter : IStatusIconPresenter { private readonly IStatusIconPresenter _always; private readonly Func<IStatusIconPresenter?> _dynamic; public CompositeStatusIconPresenter(IStatusIconPresenter always, Func<IStatusIconPresenter?> dynamic) { _always = always ?? throw new ArgumentNullException("always"); _dynamic = dynamic ?? throw new ArgumentNullException("dynamic"); } public void Present(StatusIconProjection projection) { _always.Present(projection); _dynamic()?.Present(projection); } public void Hide() { _always.Hide(); _dynamic()?.Hide(); } } public sealed record UiProjection(string CrestId, int EnhancementStacks, double RemainingSeconds, double NormalAttackDamage, bool Rage, bool SuperRage, bool SpecialMode, int CharmLevelOffset, int SilkShieldRemaining, double RageRemainingSeconds, long Revision); public interface IUiPresenter { void Present(UiProjection projection); void Hide(); } } namespace CrestsEvolve.GameInterop { public static class ArchitectRuneBridge { public static Func<bool> IsToolRepairActive { get; set; } = () => false; public static Func<bool> IsToolTransformActive { get; set; } = () => false; } public static class AttackKindMapping { public static AttackKind FromAttackType(AttackTypes attackType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected I4, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 switch ((int)attackType) { default: if ((int)attackType != 16) { break; } return AttackKind.Charge; case 0: return AttackKind.Normal; case 1: return AttackKind.Normal; case 2: return AttackKind.Skill; case 10: return AttackKind.Skill; case 6: return AttackKind.Dash; case 7: return AttackKind.Projectile; case 11: return AttackKind.Additional; case 3: case 4: case 5: case 8: case 9: break; } return AttackKind.Additional; } } public static class BeastRuneBridge { public static Func<bool> IsEnhancementActive { get; set; } = () => false; public static Func<bool> IsDamageProtectActive { get; set; } = () => false; public static Func<bool> IsPredatorCharged { get; set; } = () => false; public static Action? AcquireChargeSlashInvulnerability { get; set; } } public sealed class BeastStateAccessor { private readonly ManualLogSource _log; private readonly FieldInfo? _warriorStateField; private bool _warned; public BeastStateAccessor(ManualLogSource log) { _log = log; _warriorStateField = AccessTools.Field(typeof(HeroController), "warriorState"); if (_warriorStateField == null) { _log.LogWarning((object)"[BeastState] warriorState field not found; kill duration extension disabled."); } } public void AddRageTime(float seconds, float max) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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) if (_warriorStateField == null || seconds <= 0f) { return; } try { HeroController instance = HeroController.instance; if (!((Object)(object)instance == (Object)null)) { WarriorCrestStateInfo val = (WarriorCrestStateInfo)_warriorStateField.GetValue(instance); if (val.IsInRageMode) { val.RageEffectTimeLeft = Math.Min(max, val.RageEffectTimeLeft + seconds); _warriorStateField.SetValue(instance, val); } } } catch (Exception ex) { WarnOnce(ex); } } public bool IsInRageMode() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (_warriorStateField == null) { return false; } HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return false; } try { return ((WarriorCrestStateInfo)_warriorStateField.GetValue(instance)).IsInRageMode; } catch (Exception ex) { WarnOnce(ex); return false; } } public void SetRageStateActive(bool active) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (!active || _warriorStateField == null) { return; } HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return; } try { WarriorCrestStateInfo val = (WarriorCrestStateInfo)_warriorStateField.GetValue(instance); val.IsInRageMode = true; val.RageEffectTimeLeft = Gameplay.WarriorRageDuration; val.RageHealTimeLeft = Gameplay.WarriorRageDuration + Gameplay.WarriorRageGraceTime; _warriorStateField.SetValue(instance, val); } catch (Exception ex) { WarnOnce(ex); } } public void SetRageHealBlocked(bool blocked) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (!blocked || _warriorStateField == null) { return; } HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return; } try { WarriorCrestStateInfo val = (WarriorCrestStateInfo)_warriorStateField.GetValue(instance); val.RageModeHealCount = instance.GetRageModeHealCap(); _warriorStateField.SetValue(instance, val); } catch (Exception ex) { WarnOnce(ex); } } private void WarnOnce(Exception ex) { if (!_warned) { _warned = true; _log.LogWarning((object)("[BeastState] access failed (" + ex.GetType().Name + ": " + ex.Message + "); suppressing further warnings.")); } } } public static class CloaklessRuneBridge { public static Func<bool> IsFreeBindCharged { get; set; } = () => false; public static Func<bool> TryConsumeFreeBind { get; set; } = () => false; public static Func<bool> IsGlacialAugmentCharged { get; set; } = () => false; public static Func<bool> TryConsumeGlacialAugment { get; set; } = () => false; } public static class CursedRuneBridge { private sealed class NullLease : IDisposable { public void Dispose() { } } public static Func<bool> IsBindEnabled { get; set; } = () => false; public static Func<bool> IsBindInvulnerabilityActive { get; set; } = () => false; public static Func<IDisposable> AcquireBindInvulnerability { get; set; } = () => new NullLease(); public static Func<double> BindInvulnerabilitySeconds { get; set; } = () => 2.0; } public sealed class DashStateAccessor { private readonly ManualLogSource _log; private bool _warned; public DashStateAccessor(ManualLogSource log) { _log = log ?? throw new ArgumentNullException("log"); } public bool IsDashSprintActive() { try { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null || instance.cState == null) { return false; } if (instance.cState.isSprinting || instance.cState.dashing) { return IsDashPressed(); } return false; } catch (Exception ex) { if (!_warned) { _warned = true; _log.LogWarning((object)("[DashState] dash sprint query failed: " + ex.Message)); } return false; } } private static bool IsDashPressed() { InputHandler instance = ManagerSingleton<InputHandler>.Instance; if ((Object)(object)instance == (Object)null || instance.inputActions == null || instance.inputActions.Dash == null) { return true; } return ((OneAxisInputControl)instance.inputActions.Dash).IsPressed; } } public sealed class FleaBrewApplicator { public const string FleaBrewToolId = "Flea Brew"; private readonly ManualLogSource _log; private readonly BuffState _buff; private bool _warned; public bool IsBrewActive => _buff.IsActive; public double BrewRemainingSeconds => _buff.RemainingSeconds; public FleaBrewApplicator(ManualLogSource log, IClock clock, double durationSeconds = 3.0) { _log = log ?? throw new ArgumentNullException("log"); if (durationSeconds <= 0.0) { throw new ArgumentOutOfRangeException("durationSeconds"); } _buff = new BuffState(BuffKind.Meade, 1, durationSeconds, clock ?? throw new ArgumentNullException("clock")); } public void Apply() { if (!TryApplyVanilla()) { _buff.Add(); WarnOnce("vanilla Flea Brew application path is unconfirmed; fell back to internal buff timer (U)."); } } private bool TryApplyVanilla() { try { ToolItem toolByName = ToolItemManager.GetToolByName("Flea Brew"); if ((Object)(object)toolByName == (Object)null) { return false; } _log.LogInfo((object)("[FleaBrew][probe] tool='" + toolByName.name + "' type='" + ((object)toolByName).GetType().FullName + "' (U; FSM/object path pending probe, no event sent until confirmed).")); return false; } catch (Exception ex) { WarnOnce(ex.Message); return false; } } private void WarnOnce(string message) { if (!_warned) { _warned = true; _log.LogWarning((object)("[FleaBrew] " + message)); } } } public static class FracturedMaskBridge { public static Func<bool>? IsEquipped { get; set; } = () => false; public static Func<bool>? TryRepair { get; set; } = () => false; } public sealed class GameEventPatches : IDisposable { private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly DynamicPatchResolver _resolver; private readonly Harmony _harmony; private readonly DamageNegotiationService _negotiation; private readonly IGameApi _game; private readonly List<string> _installed = new List<string>(); private static GameEventPatches? _active; private static readonly FieldInfo LastHitInstanceField = AccessTools.Field(typeof(HealthManager), "lastHitInstance"); private static bool _spellKillWarned; private static readonly (string BindingId, string? Prefix, string? Postfix)[] EventBindings = new(string, string, string)[14] { ("player.attack.resolved", null, "NailHitEnemyPostfix"), ("enemy.damage.resolved", null, "ApplyDamageScalingPostfix"), ("player.damage.before", "TakeDamagePrefix", null), ("player.damage.after", null, "TakeDamagePostfix"), ("player.heal.request", "AddHealthPrefix", null), ("player.heal.resolved", null, "AddHealthPostfix"), ("player.silk.get", "AddSilkPrefix", "AddSilkPostfix"), ("player.silk.spend", "TakeSilkPrefix", "TakeSilkPostfix"), ("player.crest.changed", null, "SendEquippedChangedEventPostfix"), ("enemy.killed", null, "SendDeathEventPostfix"), ("player.bench.rested", null, "SetOnBenchPostfix"), ("player.died", "DiePrefix", null), ("scene.changed", null, "SceneInitPostfix"), ("player.reaper.bind.completed", null, "BindCompletedPostfix") }; private static string? _lastCrestId; private static bool _benchWasOnBench; public GameEventPatches(ManualLogSource log, GameEventHub events, DynamicPatchResolver resolver, Harmony harmony, DamageNegotiationService negotiation, IGameApi game) { _log = log; _events = events; _resolver = resolver; _harmony = harmony; _negotiation = negotiation; _game = game; } public void Install() { _active = this; _benchWasOnBench = false; (string, string, string)[] eventBindings = EventBindings; for (int i = 0; i < eventBindings.Length; i++) { (string, string, string) tuple = eventBindings[i]; if (tuple.Item2 != null && tuple.Item3 != null) { PatchBoth(tuple.Item1, tuple.Item2, tuple.Item3); } else if (tuple.Item2 != null) { PatchPrefix(tuple.Item1, tuple.Item2); } else { PatchPostfix(tuple.Item1, tuple.Item3); } } _log.LogInfo((object)("Game event patches installed: " + string.Join(", ", _installed))); } public void Dispose() { _active = null; _benchWasOnBench = false; _installed.Clear(); } private void PatchPrefix(string bindingId, string patchMethod) { PatchWith(bindingId, delegate(MethodBase method) { _harmony.Patch(method, HarmonyMethod(patchMethod), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); }); } private void PatchPostfix(string bindingId, string patchMethod) { PatchWith(bindingId, delegate(MethodBase method) { _harmony.Patch(method, (HarmonyMethod)null, HarmonyMethod(patchMethod), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); }); } private void PatchBoth(string bindingId, string prefixMethod, string postfixMethod) { PatchWith(bindingId, delegate(MethodBase method) { _harmony.Patch(method, HarmonyMethod(prefixMethod), HarmonyMethod(postfixMethod), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); }); } private void PatchWith(string bindingId, Action<MethodBase> apply) { MethodBase methodBase = _resolver.Resolve(bindingId); if ((object)methodBase == null) { _log.LogWarning((object)("Event patch disabled (binding unresolved): " + bindingId)); return; } apply(methodBase); _installed.Add(bindingId); _log.LogInfo((object)("Event patch installed: " + bindingId + " -> " + GeneralExtensions.FullDescription(methodBase))); } private static HarmonyMethod HarmonyMethod(string name) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown return new HarmonyMethod(AccessTools.DeclaredMethod(typeof(GameEventPatches), name, (Type[])null, (Type[])null) ?? throw new MissingMethodException(typeof(GameEventPatches).FullName, name)); } private unsafe static void NailHitEnemyPostfix(HeroController __instance, HealthManager enemyHealth, HitInstance hitInstance) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) _active?._events.Publish(new AttackEvent(((long)Time.frameCount << 16) ^ ((Object)enemyHealth).GetInstanceID(), ((Object)__instance).GetInstanceID(), ((Object)enemyHealth).GetInstanceID(), AttackKindMapping.FromAttackType(hitInstance.AttackType), "attack." + ((object)(*(AttackTypes*)(&hitInstance.AttackType))/*cast due to .constrained prefix*/).ToString(), hitInstance.DamageDealt, (!hitInstance.IsFirstHit) ? 1 : 0, IsAdditionalDamage: false, IsHeroDamage: true, ((Component)enemyHealth).transform.position.x, ((Component)enemyHealth).transform.position.y, ((Component)enemyHealth).transform.position.z)); } private unsafe static void ApplyDamageScalingPostfix(HealthManager __instance, HitInstance hitInstance, HitInstance __result) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_006a: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) _active?._events.PublishEnemyDamageResolved(new AttackEvent(((long)Time.frameCount << 16) ^ ((Object)__instance).GetInstanceID(), ((Object)(object)hitInstance.Source != (Object)null) ? ((Object)hitInstance.Source).GetInstanceID() : 0, ((Object)__instance).GetInstanceID(), AttackKindMapping.FromAttackType(hitInstance.AttackType), "enemy.damage." + ((object)(*(AttackTypes*)(&hitInstance.AttackType))/*cast due to .constrained prefix*/).ToString(), __result.DamageDealt, (!__result.IsFirstHit) ? 1 : 0, IsAdditionalDamage: false, hitInstance.IsHeroDamage, ((Component)__instance).transform.position.x, ((Component)__instance).transform.position.y, ((Component)__instance).transform.position.z)); } private unsafe static bool TakeDamagePrefix(ref int damageAmount, HazardType hazardType) { GameEventPatches active = _active; if (active == null || damageAmount <= 0) { return true; } active._events.PublishBefore(new PlayerDamageEvent(damageAmount, "hazard." + ((object)(*(HazardType*)(&hazardType))/*cast due to .constrained prefix*/).ToString())); int num = damageAmount; DamageDecision damageDecision = active._negotiation.Negotiate(num, "hazard." + ((object)(*(HazardType*)(&hazardType))/*cast due to .constrained prefix*/).ToString()); if (damageDecision.SilkToSpend > 0) { active._game.TrySpendSilk(damageDecision.SilkToSpend, damageDecision.Reason ?? "damage-negotiation"); } damageAmount = damageDecision.RemainingMaskDamage; if (damageDecision.Verdict != DamageVerdict.Allow || damageAmount <= 0) { active._log.LogInfo((object)("Damage cancelled by negotiation: " + (damageDecision.Reason ?? "unknown") + " " + $"(incoming={num}, remaining={damageDecision.RemainingMaskDamage}, silk={damageDecision.SilkToSpend})")); return false; } return true; } private unsafe static void TakeDamagePostfix(int damageAmount, HazardType hazardType) { GameEventPatches active = _active; if (active != null && damageAmount > 0) { active._events.PublishAfter(new PlayerDamageEvent(damageAmount, "hazard." + ((object)(*(HazardType*)(&hazardType))/*cast due to .constrained prefix*/).ToString())); } } private static bool AddHealthPrefix(HeroController __instance, int amount, ref int __state) { GameEventPatches active = _active; if (active == null || amount <= 0) { return true; } PlayerData val = __instance?.playerData; int num = val?.health ?? 0; int missingWhiteHealth = Math.Max(0, Math.Max(1, val?.maxHealth ?? 0) - num); __state = num; HealEvent healEvent = new HealEvent(amount, 0, "heal.addhealth", missingWhiteHealth); active._events.PublishRequested(healEvent); if (active._events.InterceptHeal(healEvent)) { active._log.LogInfo((object)$"[Heal] consumed by interceptor (amount={amount})"); return false; } return true; } private static void AddHealthPostfix(HeroController __instance, int amount, ref int __state) { GameEventPatches active = _active; if (active != null && amount > 0) { PlayerData val = __instance?.playerData; int num = __state; int num2 = val?.health ?? num; int applied = Math.Max(0, num2 - num); int missingWhiteHealth = Math.Max(0, Math.Max(1, val?.maxHealth ?? 0) - num); active._events.PublishResolved(new HealEvent(amount, applied, "heal.addhealth", missingWhiteHealth)); } } private static void AddSilkPrefix(HeroController __instance, ref int __state) { __state = __instance.playerData?.silk ?? 0; } private unsafe static void AddSilkPostfix(HeroController __instance, SilkAddSource source, int __state) { GameEventPatches active = _active; int num = __instance.playerData?.silk ?? 0; if (active != null && num != __state) { active._events.Publish(new ResourceEvent(__state, num, num - __state, "silk.add." + ((object)(*(SilkAddSource*)(&source))/*cast due to .constrained prefix*/).ToString())); } } private static void TakeSilkPrefix(HeroController __instance, ref int __state) { __state = __instance.playerData?.silk ?? 0; } private unsafe static void TakeSilkPostfix(HeroController __instance, SilkTakeSource source, int __state) { GameEventPatches active = _active; int num = __instance.playerData?.silk ?? 0; if (active != null && num != __state) { active._events.Publish(new ResourceEvent(__state, num, num - __state, "silk.take." + ((object)(*(SilkTakeSource*)(&source))/*cast due to .constrained prefix*/).ToString())); } } private static void SendEquippedChangedEventPostfix() { GameEventPatches active = _active; if (active == null) { return; } active._events.PublishToolEquipsChanged(); string text = HeroController.instance?.playerData?.CurrentCrestID; if (!string.IsNullOrEmpty(text)) { string lastCrestId = _lastCrestId; _lastCrestId = text; if (lastCrestId == null) { active._events.Publish(new CrestChangedEvent("", text)); } else if (!(lastCrestId == text)) { active._events.Publish(new CrestChangedEvent(lastCrestId, text)); } } } private static void SendDeathEventPostfix(HealthManager __instance) { //IL_0070: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Invalid comparison between Unknown and I4 GameEventPatches active = _active; if (active == null) { return; } string enemyName; try { enemyName = (((Object)(object)((Component)__instance).gameObject != (Object)null) ? ((Object)((Component)__instance).gameObject).name : "unknown"); } catch { enemyName = "unknown"; } active._events.Publish(new EnemyKilledEvent(((Object)__instance).GetInstanceID(), enemyName)); if (!(LastHitInstanceField != null)) { return; } try { if (LastHitInstanceField.GetValue(__instance) is HitInstance val && val.IsHeroDamage && (int)val.AttackType == 2) { active._events.PublishSpellKilled(); } } catch (Exception ex) { if (!_spellKillWarned) { _spellKillWarned = true; active._log.LogWarning((object)("[SpellKill] lastHitInstance read failed: " + ex.Message)); } } } private static void SetOnBenchPostfix(bool onBench) { GameEventPatches active = _active; if (active == null) { _benchWasOnBench = onBench; return; } if (onBench && !_benchWasOnBench) { active._events.PublishBenchRested(); } _benchWasOnBench = onBench; } private static void DiePrefix(bool nonLethal) { _active?._events.PublishPlayerDied(); } private static void SceneInitPostfix() { //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) GameEventPatches active = _active; if (active != null) { GameEventHub events = active._events; Scene activeScene = SceneManager.GetActiveScene(); events.PublishSceneChanged(((Scene)(ref activeScene)).name); } } private static void BindCompletedPostfix() { _active?._events.PublishBindCompleted(); } } public static class HudIconCloneSanitizer { public enum ComponentDisposition { Keep, DisableThenDestroy, DestroyImmediate } public static ComponentDisposition Classify(Type type) { if (type == null) { return ComponentDisposition.DestroyImmediate; } if (typeof(RadialHudIcon).IsAssignableFrom(type)) { return ComponentDisposition.DisableThenDestroy; } if (typeof(Animator).IsAssignableFrom(type)) { return ComponentDisposition.DisableThenDestroy; } if (typeof(UnityMessageListener).IsAssignableFrom(type)) { return ComponentDisposition.DisableThenDestroy; } if (typeof(RadialHudSwitchModeSettings).IsAssignableFrom(type)) { return ComponentDisposition.DisableThenDestroy; } if (typeof(EventRegister).IsAssignableFrom(type)) { return ComponentDisposition.DestroyImmediate; } if (typeof(EventResponder).IsAssignableFrom(type)) { return ComponentDisposition.DestroyImmediate; } return ComponentDisposition.Keep; } public static bool IsArrowName(string? objectName) { if (objectName != null) { return objectName.IndexOf("Arrow", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } public static bool ShouldKeepRenderer(string? objectName, bool isCenterIcon, bool isArrow) { if (!(isCenterIcon || isArrow)) { return IsArrowName(objectName); } return true; } public static bool ShouldKeepGraphic(string? objectName, bool isFill, bool isBg) { return isFill || isBg; } public static int Sanitize(GameObject root, string key, ManualLogSource? log, bool immediate = false) { if ((Object)(object)root == (Object)null) { return 0; } MonoBehaviour[] componentsInChildren = root.GetComponentsInChildren<MonoBehaviour>(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { return 0; } Dictionary<Type, int> dictionary = new Dictionary<Type, int>(); int num = 0; foreach (MonoBehaviour item in new List<MonoBehaviour>(componentsInChildren)) { if ((Object)(object)item == (Object)null) { continue; } Type type = ((object)item).GetType(); ComponentDisposition componentDisposition = Classify(type); if (componentDisposition == ComponentDisposition.Keep) { continue; } if (componentDisposition == ComponentDisposition.DestroyImmediate) { Object.DestroyImmediate((Object)(object)item); } else { Behaviour val = (Behaviour)(object)item; if (val != null) { val.enabled = false; } if (immediate) { Object.DestroyImmediate((Object)(object)item); } else { Object.Destroy((Object)(object)item); } } dictionary[type] = ((!dictionary.TryGetValue(type, out var value)) ? 1 : (value + 1)); num++; } if (num > 0 && log != null) { foreach (KeyValuePair<Type, int> item2 in dictionary) { log.LogInfo((object)string.Format("[HudIconCloneSanitizer] '{0}' removed {1}x {2}{3}", key, item2.Value, item2.Key.FullName, immediate ? " (immediate)" : "")); } } return num; } } internal static class HudRootInstaller { private static StatusIconHostConfig? _config; private static StatusIconTextureService? _textures; private static ManualLogSource? _log; public static void Configure(StatusIconHostConfig config, StatusIconTextureService textures, ManualLogSource log) { _config = config; _textures = textures; _log = log; } public static void Postfix(BindOrbHudFrame __instance) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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) if (_config == null || _textures == null) { return; } Transform val = ToolRowLocator.Resolve(_config.RowAnchorPath, ((Component)__instance).transform); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.AddComponent<HudStatusIconHost>().Configure(_config, ((Component)__instance).transform, _textures, _log, val); ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)("[HudStatusIconHost] attached to row container: " + BuildPath(val))); } return; } GameObject val2 = new GameObject("CrestsEvolveHUDRoot") { layer = 5 }; val2.transform.SetParent(((Component)__instance).transform, false); val2.transform.localScale = Vector3.one; val2.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); Vector3 localPosition = val2.transform.localPosition; localPosition.z = -0.0001f; val2.transform.localPosition = localPosition; val2.AddComponent<HudStatusIconHost>().Configure(_config, ((Component)__instance).transform, _textures, _log, null); ManualLogSource? log2 = _log; if (log2 != null) { log2.LogInfo((object)"[HudStatusIconHost] no row container found; created CrestsEvolveHUDRoot fallback under BindOrbHudFrame."); } } private static string BuildPath(Transform transform) { List<string> list = new List<string>(); Transform val = transform; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list); } } public sealed class StatusIconHostConfig { public string RowAnchorPath { get; set; } = "auto"; public Vector3 FallbackOffset { get; set; } = new Vector3(0f, -2.2f, 0f); public float Spacing { get; set; } = 1.1f; public bool LogHierarchyProbe { get; set; } = true; public static bool TryParseOffset(string text, out Vector3 offset) { //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_005f: 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) offset = Vector3.zero; if (string.IsNullOrWhiteSpace(text)) { return false; } string[] array = text.Split(','); if (array.Length < 2 || !float.TryParse(array[0], out var result) || !float.TryParse(array[1], out var result2)) { return false; } float result3; float num = ((array.Length > 2 && float.TryParse(array[2], out result3)) ? result3 : 0f); offset = new Vector3(result, result2, num); return true; } } public sealed class HudStatusIconHost : MonoBehaviour, IStatusIconPresenter { private sealed class NativeIconTemplate { public Transform Root; public string IconPath; public string FillPath; public string BgPath; public Color? InactiveColor { get; set; } } private sealed class IconView { private readonly Color _iconTint; private readonly string _key; private readonly StatusIconTextureService? _textures; private readonly Color? _nativeInactiveColor; private Text? _centerLabel; private static Font? _centerFont; public GameObject Root { get; } public SpriteRenderer Icon { get; } public Image RingFill { get; } public Image RingBg { get; } public Color RingColor { get; } public IconView(GameObject root, SpriteRenderer icon, Image ringFill, Image ringBg, Color ringColor, Color iconTint, string key, StatusIconTextureService? textures, Color? nativeInactiveColor = null) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_002e: Unknown result type (might be due to invalid IL or missing references) Root = root; Icon = icon; RingFill = ringFill; RingBg = ringBg; RingColor = ringColor; _iconTint = iconTint; _key = key; _textures = textures; _nativeInactiveColor = nativeInactiveColor; } public void Set(StatusIconState state) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) Root.SetActive(state.Visible); if (!state.Visible) { return; } ((Renderer)Icon).enabled = true; if (!((Component)Icon).gameObject.activeSelf) { ((Component)Icon).gameObject.SetActive(true); } if (_textures != null && !string.IsNullOrEmpty(_key)) { Sprite iconSprite = _textures.GetIconSprite(_key); if ((Object)(object)iconSprite != (Object)null) { Icon.sprite = iconSprite; } float iconScale = _textures.GetIconScale(_key); ((Component)Icon).transform.localScale = new Vector3(iconScale, iconScale, 1f); } float num = ((state.Max <= 0) ? 1f : Mathf.Clamp01((float)state.Current / (float)state.Max)); RingFill.fillAmount = num; RingBg.fillAmount = 1f - num; bool flag = state.Dimmed || state.Current <= 0; Icon.color = (Color)((!flag) ? _iconTint : (((??)_nativeInactiveColor) ?? Dim(_iconTint, 0.45f))); Color val2 = default(Color); Color val = ((state.ColorOverride != null && ColorUtility.TryParseHtmlString(state.ColorOverride, ref val2)) ? val2 : RingColor); ((Graphic)RingFill).color = ((!flag) ? val : (_nativeInactiveColor.HasValue ? Multiply(val, _nativeInactiveColor.Value) : Dim(val, 0.45f))); ((Graphic)RingBg).color = Dim(val, 0.3f); if (state.CenterText != null) { Text val3 = EnsureCenterText(); if ((Object)(object)val3 != (Object)null) { val3.text = state.CenterText; } } else if ((Object)(object)_centerLabel != (Object)null) { ((Component)_centerLabel).gameObject.SetActive(false); } } private Text? EnsureCenterText() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_centerLabel != (Object)null) { ((Component)_centerLabel).gameObject.SetActive(true); return _centerLabel; } Font centerFont = GetCenterFont(); if ((Object)(object)centerFont == (Object)null) { return null; } GameObject val = new GameObject("CenterText", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Text) }) { layer = Root.layer }; val.transform.SetParent(Root.transform, false); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(1.8f, 1f); ((Transform)component).localPosition = Vector3.zero; Text component2 = val.GetComponent<Text>(); component2.font = centerFont; component2.fontSize = 42; component2.alignment = (TextAnchor)4; ((Graphic)component2).color = Color.white; ((Graphic)component2).raycastTarget = false; component2.horizontalOverflow = (HorizontalWrapMode)1; component2.verticalOverflow = (VerticalWrapMode)1; _centerLabel = component2; return component2; } private static Font? GetCenterFont() { if ((Object)(object)_centerFont != (Object)null) { return _centerFont; } try { _centerFont = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf"); } catch (Exception ex) { Debug.LogWarning((object)("[HudStatusIconHost] LegacyRuntime.ttf unavailable: " + ex.Message)); } if ((Object)(object)_centerFont == (Object)null) { try { _centerFont = Resources.GetBuiltinResource<Font>("Arial.ttf"); } catch (Exception ex2) { Debug.LogWarning((object)("[HudStatusIconHost] Arial.ttf unavailable; center text disabled. " + ex2.Message)); } } return _centerFont; } } private StatusIconHostConfig _config = new StatusIconHostConfig(); private Transform? _hudFrame; private Transform? _rowAnchor; private TransformLayoutGrid? _grid; private bool _gridManaged; private StatusIconTextureService? _textures; private ManualLogSource? _log; private readonly Dictionary<string, IconView> _icons = new Dictionary<string, IconView>(StringComparer.Ordinal); private HashSet<string> _visibleKeys = new HashSet<string>(StringComparer.Ordinal); private long _lastRevision = -1L; private Sprite? _nativeRingSprite; private readonly Dictionary<string, NativeIconTemplate> _nativeTemplates = new Dictionary<string, NativeIconTemplate>(StringComparer.Ordinal); private NativeIconTemplate? _defaultTemplate; private bool _rowProbeLogged; private bool _toolsAppeared; private static readonly HashSet<string> _nativeCloneHierarchyLogged = new HashSet<string>(StringComparer.Ordinal); public static HudStatusIconHost? Instance { get; private set; } private void Awake() { Instance = this; ((EventBase)EventRegister.GetRegisterGuaranteed(((Component)this).gameObject, "TOOL EQUIPS CHANGED")).ReceivedEvent += RebuildLayout; ((EventBase)EventRegister.GetRegisterGuaranteed(((Component)this).gameObject, "POST TOOL EQUIPS CHANGED")).ReceivedEvent += RebuildLayout; ((EventBase)EventRegister.GetRegisterGuaranteed(((Component)this).gameObject, "TOOLS APPEAR")).ReceivedEvent += OnToolsAppear; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } public void Configure(StatusIconHostConfig config, Transform hudFrame, StatusIconTextureService textures, ManualLogSource log, Transform? rowAnchor) { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown _config = config ?? new StatusIconHostConfig(); _hudFrame = hudFrame; _textures = textures; _log = log; if (_config.LogHierarchyProbe) { LogHierarchy(hudFrame, 0, 3); LogSiblingOverview(hudFrame.parent, "Bind Orb parent"); Transform parent = hudFrame.parent; LogSiblingOverview((parent != null) ? parent.parent : null, "Bind Orb grandparent"); } if ((Object)(object)rowAnchor != (Object)null) { AttachToRow(rowAnchor); LogRowStructure("hud-created"); foreach (Transform item in rowAnchor) { Transform val = item; if ((Object)(object)((Component)val).GetComponent<ToolHudIcon>() != (Object)null && ((Component)val).gameObject.activeInHierarchy) { _toolsAppeared = true; break; } } } else { ((Component)this).transform.localPosition = _config.FallbackOffset; ManualLogSource? log2 = _log; if (log2 != null) { log2.LogInfo((object)"[HudStatusIconHost][probe] no vanilla tool row detected; using fallback offset."); } } StatusIconProjector.Current?.Refresh(); } public void Present(StatusIconProjection projection) { if (projection.Revision == _lastRevision || !_toolsAppeared) { return; } _lastRevision = projection.Revision; List<StatusIconState> list = projection.Icons.Where((StatusIconState state) => _textures == null || _textures.IsIconEnabled(state.Key)).ToList(); HashSet<string> hashSet = new HashSet<string>(list.Select((StatusIconState i) => i.Key), StringComparer.Ordinal); foreach (string item in _icons.Keys.ToList()) { if (!hashSet.Contains(item)) { Object.Destroy((Object)(object)_icons[item].Root); _icons.Remove(item); } } HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); foreach (StatusIconState item2 in list) { IconView value2; if (item2.Visible) { hashSet2.Add(item2.Key); EnsureIcon(item2.Key); if (_icons.TryGetValue(item2.Key, out IconView value)) { value.Set(item2); } } else if (_icons.TryGetValue(item2.Key, out value2)) { Object.Destroy((Object)(object)value2.Root); _icons.Remove(item2.Key); } } if (!_visibleKeys.SetEquals(hashSet2)) { _visibleKeys = hashSet2; RebuildLayout(); } } public void Hide() { foreach (IconView value in _icons.Values) { Object.Destroy((Object)(object)value.Root); } _icons.Clear(); _visibleKeys.Clear(); _lastRevision = -1L; } private void OnToolsAppear() { _toolsAppeared = true; if (_nativeTemplates.Count == 0 && (Object)(object)_rowAnchor != (Object)null) { CaptureNativeRing(_rowAnchor); } StatusIconProjector.Current?.Refresh(); RebuildLayout(); } private void EnsureIcon(string key) { //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_005b: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) if (!_icons.ContainsKey(key) && _textures != null && !TryCreateNativeIcon(key)) { int layer = (((Object)(object)_hudFrame != (Object)null) ? ((Component)_hudFrame).gameObject.layer : 5); GameObject val = new GameObject("CE_Icon_" + key) { layer = layer }; val.transform.SetParent(((Component)this).transform, false); val.transform.localScale = Vector3.one; GameObject val2 = new GameObject("Icon") { layer = layer }; val2.transform.SetParent(val.transform, false); SpriteRenderer val3 = val2.AddComponent<SpriteRenderer>(); val3.sprite = _textures.GetIconSprite(key); ((Renderer)val3).sortingOrder = 10; float iconScale = _textures.GetIconScale(key); val2.transform.localScale = new Vector3(iconScale, iconScale, 1f); Image ringBg = CreateRingImage(val.transform, layer); Image ringFill = CreateRingImage(val.transform, layer); _icons[key] = new IconView(val, val3, ringFill, ringBg, _textures.GetRingColor(key), _textures.GetIconTint(key), key, _textures); } } private bool TryCreateNativeIcon(string key) { //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) NativeIconTemplate nativeIconTemplate = ResolveTemplate(key); if (nativeIconTemplate == null) { return false; } GameObject val = Object.Instantiate<GameObject>(((Component)nativeIconTemplate.Root).gameObject, ((Component)this).transform); ((Object)val).name = "CE_Icon_" + key; val.SetActive(true); HudIconCloneSanitizer.Sanitize(val, key, _log, immediate: true); Transform obj = val.transform.Find(nativeIconTemplate.IconPath); SpriteRenderer val2 = ((obj != null) ? ((Component)obj).GetComponent<SpriteRenderer>() : null); Transform obj2 = val.transform.Find(nativeIconTemplate.FillPath); Image val3 = ((obj2 != null) ? ((Component)obj2).GetComponent<Image>() : null); Transform obj3 = val.transform.Find(nativeIconTemplate.BgPath); Image val4 = ((obj3 != null) ? ((Component)obj3).GetComponent<Image>() : null); if ((Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null) { Object.Destroy((Object)(object)val); ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[HudStatusIconHost] native icon clone failed for '" + key + "'; falling back to custom build.")); } return false; } if ((Object)(object)val2 == (Object)null) { GameObject val5 = new GameObject("CE Center Icon") { layer = val.layer }; val5.transform.SetParent(val.transform, false); val2 = val5.AddComponent<SpriteRenderer>(); ((Renderer)val2).sortingOrder = 10; } SanitizeNativeCloneContent(val, val2, val3, val4); LogNativeCloneHierarchy(key, val, val2); ((Component)val2).gameObject.SetActive(true); ((Renderer)val2).enabled = true; val2.sprite = _textures.GetIconSprite(key); float iconScale = _textures.GetIconScale(key); ((Component)val2).transform.localScale = new Vector3(iconScale, iconScale, 1f); _icons[key] = new IconView(val, val2, val3, val4, _textures.GetRingColor(key), _textures.GetIconTint(key), key, _textures, nativeIconTemplate.InactiveColor); ManualLogSource? log2 = _log; if (log2 != null) { log2.LogInfo((object)("[HudStatusIconHost][probe] native icon cloned for '" + key + "' from " + BuildPath(nativeIconTemplate.Root))); } return true; } private static void SanitizeNativeCloneContent(GameObject cloneGo, SpriteRenderer icon, Image fill, Image bg) { Renderer[] componentsInChildren = cloneGo.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)icon)) { SpriteRenderer val2 = (SpriteRenderer)(object)((val is SpriteRenderer) ? val : null); if (val2 == null || !HudIconCloneSanitizer.ShouldKeepRenderer(((Object)val2).name, isCenterIcon: false, isArrow: false)) { val.enabled = false; } } } Graphic[] componentsInChildren2 = cloneGo.GetComponentsInChildren<Graphic>(true); foreach (Graphic val3 in componentsInChildren2) { if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3 == (Object)(object)fill) && !((Object)(object)val3 == (Object)(object)bg)) { ((Behaviour)val3).enabled = false; } } } private void LogNativeCloneHierarchy(string key, GameObject cloneGo, SpriteRenderer icon) { if (_log != null && _nativeCloneHierarchyLogged.Add(key)) { _log.LogInfo((object)("[HudStatusIconHost][probe] native clone hierarchy '" + key + "' (center=" + ((Object)((Component)icon).transform).name + "):")); LogCloneNode(cloneGo.transform, 1); } } private void LogCloneNode(Transform node, int depth) { //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Expected O, but got Unknown if ((Object)(object)node == (Object)null) { return; } List<string> list = new List<string>(); Component[] components = ((Component)node).GetComponents<Component>(); foreach (Component val in components) { if ((Object)(object)val == (Object)null) { continue; } SpriteRenderer val2 = (SpriteRenderer)(object)((val is SpriteRenderer) ? val : null); if (val2 == null) { Image val3 = (Image)(object)((val is Image) ? val : null); if (val3 == null) { RawImage val4 = (RawImage)(object)((val is RawImage) ? val : null); if (val4 == null) { Text val5 = (Text)(object)((val is Text) ? val : null); if (val5 == null) { MeshRenderer val6 = (MeshRenderer)(object)((val is MeshRenderer) ? val : null); if (val6 != null) { list.Add($"MeshRenderer(enabled={((Renderer)val6).enabled})"); } else { list.Add(((object)val).GetType().Name); } } else { list.Add($"Text(enabled={((Behaviour)val5).enabled})"); } } else { object arg = ((Behaviour)val4).enabled; Texture texture = val4.texture; list.Add(string.Format("RawImage(enabled={0},tex={1})", arg, ((texture != null) ? ((Object)texture).name : null) ?? "null")); } } else { object arg2 = ((Behaviour)val3).enabled; Sprite sprite = val3.sprite; list.Add(string.Format("Image(enabled={0},sprite={1})", arg2, ((sprite != null) ? ((Object)sprite).name : null) ?? "null")); } } else { object arg3 = ((Renderer)val2).enabled; Sprite sprite2 = val2.sprite; list.Add(string.Format("SpriteRenderer(enabled={0},sprite={1})", arg3, ((sprite2 != null) ? ((Object)sprite2).name : null) ?? "null")); } } ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)("[HudStatusIconHost][probe] " + new string(' ', (depth - 1) * 2) + ((Object)node).name + " " + string.Format("active={0} [{1}]", ((Component)node).gameObject.activeSelf, string.Join(",", list)))); } foreach (Transform item in node) { Transform node2 = item; LogCloneNode(node2, depth + 1); } } private Image CreateRingImage(Transform parent, int layer) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Ring", new Type[2] { typeof(RectTransform), typeof(Image) }) { layer = layer }; val.transform.SetParent(parent, false); val.GetComponent<RectTransform>().sizeDelta = new Vector2(1f, 1f); Image component = val.GetComponent<Image>(); component.sprite = _nativeRingSprite ?? _textures.RingSprite; component.type = (Type)3; component.fillMethod = (FillMethod)4; component.fillOrigin = 2; ((Graphic)component).raycastTarget = false; return component; } private void RebuildLayout() { //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) if (_icons.Count == 0) { return; } if (_gridManaged) { TransformLayoutGrid? grid = _grid; if (grid != null) { ((TransformLayout)grid).UpdatePositions(); } LogRowStructureCompact(null); return; } List<float> list = new List<float>(); float? referenceY = null; if ((Object)(object)_rowAnchor != (Object)null) { foreach (Transform item in _rowAnchor) { Transform val = item; if (!((Object)(object)val == (Object)(object)((Component)this).transform) && !val.IsChildOf(((Component)this).transform) && ((Component)val).gameObject.activeInHierarchy && (!((Object)(object)((Component)val).GetComponent<ToolHudIcon>() == (Object)null) || !((Object)(object)((Component)val).GetComponent<Image>() == (Object)null) || !((Object)(object)((Component)val).GetComponent<SpriteRenderer>() == (Object)null))) { list.Add(val.localPosition.x); if (!referenceY.HasValue && (Object)(object)((Component)val).GetComponent<ToolHudIcon>() != (Object)null) { referenceY = val.localPosition.y; } } } } ToolRowLayout toolRowLayout = ToolRowLayoutCalculator.Calculate(list, referenceY, _config.Spacing, _config.FallbackOffset.x); int num = 0; foreach (IconView value in _icons.Values) { value.Root.transform.localPosition = new Vector3(toolRowLayout.StartX + (float)num * toolRowLayout.Spacing, toolRowLayout.RowY, 0f); num++; } LogRowStructureCompact(toolRowLayout); } private void AttachToRow(Transform anchor) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) _rowAnchor = anchor; _grid = ((Component)anchor).GetComponent<TransformLayoutGrid>(); _gridManaged = (Object)(object)_grid != (Object)null; if ((Object)(object)anchor != (Object)(object)((Component)this).transform) { ((Component)this).transform.SetParent(anchor, false); ((Component)this).transform.localPosition = Vector3.zero; } ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)$"[HudStatusIconHost][probe] row anchor: {BuildPath(anchor)} gridManaged={_gridManaged}"); } CaptureNativeRing(anchor); } private void CaptureNativeRing(Transform anchor) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)anchor == (Object)null) { return; } foreach (Transform item in anchor) { Transform val = item; ToolHudIcon component = ((Component)val).GetComponent<ToolHudIcon>(); if ((Object)(object)component == (Object)null) { continue; } string text = ResolveTemplateKey(val, component); if (text == null || _nativeTemplates.ContainsKey(text)) { continue; } FieldInfo fieldInfo = AccessTools.Field(typeof(RadialHudIcon), "icon"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(RadialHudIcon), "radialImage"); FieldInfo fieldInfo3 = AccessTools.Field(typeof(RadialHudIcon), "radialImageBg"); FieldInfo fieldInfo4 = AccessTools.Field(typeof(RadialHudIcon), "inactiveColor"); object? obj = fieldInfo?.GetValue(component); SpriteRenderer val2 = (SpriteRenderer)((obj is SpriteRenderer) ? obj : null); if (val2 == null) { continue; } object? obj2 = fieldInfo2?.GetValue(component); Image val3 = (Image)((obj2 is Image) ? obj2 : null); if (val3 == null) { continue; } object? obj3 = fieldInfo3?.GetValue(component); Image val4 = (Image)((obj3 is Image) ? obj3 : null); if (val4 != null) { Color? inactiveColor = ((fieldInfo4?.GetValue(component) is Color value) ? new Color?(value) : ((Color?)null)); if ((Object)(object)_nativeRingSprite == (Object)null && (Object)(object)val3.sprite != (Object)null) { _nativeRingSprite = val3.sprite; } GameObject val5 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)this).transform); ((Object)val5).name = "CE_OwnedTemplate_" + text; val5.SetActive(false); HudIconCloneSanitizer.Sanitize(val5, "<template>", _log); NativeIconTemplate nativeIconTemplate = new NativeIconTemplate { Root = val5.transform, IconPath = GetRelativePath(val, ((Component)val2).transform), FillPath = GetRelativePath(val, ((Component)val3).transform), BgPath = GetRelativePath(val, ((Component)val4).transform), InactiveColor = inactiveColor }; _nativeTemplates[text] = nativeIconTemplate; if (_defaultTemplate == null || text == "N") { _defaultTemplate = nativeIconTemplate; } ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)("[HudStatusIconHost][probe] owned icon template captured (" + text + ") from " + BuildPath(val))); } } } if (_defaultTemplate == null && _nativeTemplates.Count > 0) { _defaultTemplate = _nativeTemplates.Values.First(); } } private static string? ResolveTemplateKey(Transform child, ToolHudIcon toolHudIcon) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Invalid comparison between Unknown and I4 //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Invalid comparison between Unknown and I4 string name = ((Object)child).name; if (name.EndsWith("U", StringComparison.Ordinal) || name.IndexOf(" Up", StringComparison.Ordinal) >= 0) { return "U"; } if (name.EndsWith("D", StringComparison.Ordinal) || name.IndexOf(" Down", StringComparison.Ordinal) >= 0) { return "D"; } if (name.EndsWith("N", StringComparison.Ordinal) || name.IndexOf(" Neutral", StringComparison.Ordinal) >= 0) { return "N"; } if (AccessTools.Field(typeof(ToolHudIcon), "binding")?.GetValue(toolHudIcon) is AttackToolBinding val) { if ((int)val != 1) { if ((int)val == 2) { return "D"; } return "N"; } return "U"; } return null; } private NativeIconTemplate? ResolveTemplate(string key) { if (key.EndsWith(".up", StringComparison.OrdinalIgnoreCase) && _nativeTemplates.TryGetValue("U", out NativeIconTemplate value)) { return value; } if (key.EndsWith(".down", StringComparison.OrdinalIgnoreCase) && _nativeTemplates.TryGetValue("D", out NativeIconTemplate value2)) { return value2; } if (key.EndsWith(".neutral", StringComparison.OrdinalIgnoreCase) && _nativeTemplates.TryGetValue("N", out NativeIconTemplate value3)) { return value3; } return _defaultTemplate; } private static string? GetRelativePath(Transform root, Transform target) { if ((Object)(object)root == (Object)null || (Object)(object)target == (Object)null) { return null; } List<string> list = new List<string>(); Transform val = target; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add(((Object)val).name); val = val.parent; } if ((Object)(object)val != (Object)(object)root) { return null; } list.Reverse(); return string.Join("/", list); } private void LogHierarchy(Transform parent, int depth, int maxDepth) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)parent == (Object)null || depth > maxDepth) { return; } foreach (Transform item in parent) { Transform val = item; string arg = string.Join(",", from c in ((Component)val).GetComponents<Component>() select ((object)c).GetType().Name); ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)$"[HudStatusIconHost][probe] {BuildPath(val)} layer={((Component)val).gameObject.layer} [{arg}]"); } LogHierarchy(val, depth + 1, maxDepth); } } private void LogSiblingOverview(Transform? parent, string label) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown if ((Object)(object)parent == (Object)null) { return; } ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)("[HudStatusIconHost][probe] " + label + " sibling overview:")); } foreach (Transform item in parent) { Transform val = item; ManualLogSource? log2 = _log; if (log2 != null) { log2.LogInfo((object)$"[HudStatusIconHost][probe] {((Object)val).name} layer={((Component)val).gameObject.layer}"); } } } private void LogRowStructure(string reason) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Expected O, but got Unknown if ((Object)(object)_rowAnchor == (Object)null || _log == null || !_config.LogHierarchyProbe) { return; } string text = string.Join(",", from c in ((Component)_rowAnchor).GetComponents<Component>() select ((object)c).GetType().Name); _log.LogInfo((object)("[HudStatusIconHost][probe] row structure (" + reason + ") anchor=" + BuildPath(_rowAnchor) + " [" + text + "]")); foreach (Transform item in _rowAnchor) { Transform val = item; string text2 = string.Join(",", from c in ((Component)val).GetComponents<Component>() select ((object)c).GetType().Name); _log.LogInfo((object)($"[HudStatusIconHost][probe] {((Object)val).name} layer={((Component)val).gameObject.layer} " + $"active={((Component)val).gameObject.activeSelf} pos={val.localPosition} scale={val.localScale} [{text2}]")); } Transform parent = _rowAnchor.parent; if (!((Object)(object)parent != (Object)null)) { return; } string text3 = string.Join(",", from c in ((Component)parent).GetComponents<Component>() select ((object)c).GetType().Name); _log.LogInfo((object)("[HudStatusIconHost][probe] row parent '" + ((Object)parent).name + "' [" + text3 + "] siblings:")); foreach (Transform item2 in parent) { Transform val2 = item2; string arg = string.Join(",", from c in ((Component)val2).GetComponents<Component>() select ((object)c).GetType().Name); _log.LogInfo((object)$"[HudStatusIconHost][probe] {((Object)val2).name} layer={((Component)val2).gameObject.layer} [{arg}]"); } } private void LogRowStructureCompact(ToolRowLayout? layout) { if (!((Object)(object)_rowAnchor == (Object)null) && _log != null && _config.LogHierarchyProbe && !_rowProbeLogged) { _rowProbeLogged = true; string text = string.Join("; ", from Transform c in (IEnumerable)_rowAnchor where (Object)(object)c != (Object)(object)((Component)this).transform select $"{((Object)c).name}(active={((Component)c).gameObject.activeSelf},x={c.localPosition.x:F2})"); string text2 = ((layout == null) ? "grid-managed" : $"startX={layout.StartX:F2} y={layout.RowY:F2} spacing={layout.Spacing:F2}"); _log.LogInfo((object)("[HudStatusIconHost][probe] row relayout " + text2 + " children=[" + text + "]")); } } private static string BuildPath(Transform transform) { List<string> list = new List<string>(); Transform val = transform; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list); } private static Color Dim(Color color, float factor) { //IL_0000: 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_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) return new Color(color.r * factor, color.g * factor, color.b * factor, color.a); } private static Color Multiply(Color a, Color b) { //IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) return new Color(a.r * b.r, a.g * b.g, a.b * b.b, a.a * b.a); } } public sealed class HunterStateAccessor { private readonly ManualLogSource _log; private readonly FieldInfo? _hunterStateField; private int? _savedMeterHits; private bool _forceActive; private bool _warned; public bool IsReady => _hunterStateField != null; public HunterStateAccessor(ManualLogSource log) { _log = log; _hunterStateField = AccessTools.Field(typeof(HeroController), "hunterUpgState"); if (_hunterStateField == null) { _log.LogWarning((object)"[HunterState] hunterUpgState field not found; rage focus forcing disabled."); } } public int CurrentMeterHits() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (_hunterStateField == null) { return 0; } HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return 0; } try { return ((HunterUpgCrestStateInfo)_hunterStateField.GetValue(instance)).CurrentMeterHits; } catch (Exception ex) { WarnOnce(ex); return 0; } } public void SetFocusActive(bool active, int forcedMeterHits) { //IL_0030: 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) //IL_0042: 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) if (_hunterStateField == null || forcedMeterHits <= 0) { return; } HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return; } try { HunterUpgCrestStateInfo val = (HunterUpgCrestStateInfo)_hunterStateField.GetValue(instance); if (active) { if (!_forceActive) { _savedMeterHits = val.CurrentMeterHits; _forceActive = true; } val.CurrentMeterHits = forcedMeterHits; } else { if (!_forceActive) { return; } val.CurrentMeterHits = _savedMeterHits.GetValueOrDefault(); _forceActive = false; _savedMeterHits = null; } _hunterStateField.SetValue(instance, val); } catch (Exception ex) { WarnOnce(ex); } } private void WarnOnce(Exception ex) { if (!_warned) { _warned = true; _log.LogWarning((object)("[HunterState] access failed (" + ex.GetType().Name + ": " + ex.Message + "); suppressing further warnings.")); } } } public interface IGameApi : IDisposable { string CurrentCrestId { get; } int CurrentSilk { get; } int CurrentHealth { get; } int MaxHealth { get; } bool TryHeal(int amount, string reason); bool TrySpendSilk(int amount, string reason); void AddSilk(int amount, string reason); void AddShards(int amount, string reason); void SetTemporaryCharmLevelOffset(int offset, string source); void SetTemporaryMaxHealth(int? maxHealth); void ClearBlueHealth(string reason); IDisposable AcquireInvulnerability(string source, double maxDurationSeconds); bool TryInvokeVanillaAction(string bindingId); void SpawnAdditionalDamage(AdditionalDamageCommand command); bool SpawnPlayerExplosion(double damage, string sourceTag, float radius); bool SpawnPlayerExplosionAt(double damage, string sourceTag, float radius, Vector3 position); bool SpawnNativeExplosion(); bool SpawnNativeExplosionAt(Vector3 position); bool TryGetToolColor(string toolId, out ToolColorKind color); } public static class MaxHealthHudSync { private static ManualLogSource? _log; private static bool _warned; public static void Configure(ManualLogSource log) { _log = log; _warned = false; } public static void Apply(int? projectedMax) { try { Transform val = FindHealthRoot(); if ((Object)(object)val == (Object)null) { WarnOnce("[MaxHealthHudSync] 'Hud Canvas/Health' not found; mask sync skipped (HEALTH UPDATE fallback still sent)."); return; } PlayMakerFSM[] componentsInChildren = ((Component)val).GetComponentsInChildren<PlayMakerFSM>(true); foreach (PlayMakerFSM val2 in componentsInChildren) { if ((Object)(object)val2 == (Object)null || !string.Equals(val2.FsmName, "health_display", StringComparison.OrdinalIgnoreCase)) { continue; } FsmVariables fsmVariables = val2.FsmVariables; FsmInt val3 = ((fsmVariables != null) ? fsmVariables.FindFsmInt("Health Number") : null); if (val3 != null && val3.Value > 1) { bool flag = !projectedMax.HasValue || val3.Value <= projectedMax.Value; GameObject gameObject = ((Component)val2).gameObject; if (!((Object)(object)gameObject == (Object)null) && gameObject.activeSelf != flag) { gameObject.SetActive(flag); } } } } catch (Exception ex) { WarnOnce("[MaxHealthHudSync] Apply failed: " + ex.Message); } } private static Transform? FindHealthRoot() { GameObject val = GameObject.Find("Hud Canvas"); if ((Object)(object)val != (Object)null) { Transform val2 = val.transform.Find("Health"); if ((Object)(object)val2 != (Object)null) { return val2; } } GameObject obj = GameObject.Find("Hud Canvas/Health"); if (obj == null) { return null; } return obj.transform; } private static void WarnOnce(string message) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)message); } } } } public sealed class NeedleforgeAdapter : IDisposable { public const string PluginGuid = "io.github.needleforge"; private readonly ManualLogSource _log; public bool IsAvailable { get; private set; } public string? InstalledVersion { get; private set; } public ColorData? WhiteColorData { get; private set; } public NeedleforgeAdapter(ManualLogSource log) { _log = log; } public void Initialize() { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if (!(value.Metadata.GUID != "io.github.needleforge")) { IsAvailable = true; InstalledVersion = value.Metadata.Version.ToString(); _log.LogInfo((object)("Needleforge adapter bound: version=" + InstalledVersion)); return; } } _log.LogWarning((object)"Needleforge (io.github.needleforge) not found; custom crest/tool registration is disabled."); } public bool TryAddCrest(string name) { if (!IsAvailable || string.IsNullOrWhiteSpace(name)) { return false; } try { NeedleforgePlugin.AddCrest(name); _log.LogInfo((object)("Needleforge crest registered: " + name)); return true; } catch (Exception arg) { _log.LogError((object)$"Needleforge AddCrest failed for '{name}': {arg}"); return false; } } public bool TryAddTool(string name, ToolItemType type) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!IsAvailable || string.IsNullOrWhiteSpace(name)) { return false; } try { NeedleforgePlugin.AddTool(name, type); _log.LogInfo((object)$"Needleforge tool registered: {name} ({type})"); return true; } catch (Exception arg) { _log.LogError((object)$"Needleforge AddTool failed for '{name}': {arg}"); return false; } } public bool TryAddToolColor(string name, Color color, bool isAttackType = false) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!IsAvailable || string.IsNullOrWhiteSpace(name)) { return false; } try { NeedleforgePlugin.AddToolColor(name, color, isAttackType); _log.LogInfo((object)("Needleforge tool color registered: " + name)); return true; } catch (Exception arg) { _log.LogError((object)$"Needleforge AddToolColor failed for '{name}': {arg}"); return false; } } public bool TryAddWhiteToolColor(out ToolItemType type) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected I4, but got Unknown type = (ToolItemType)2; if (!IsAvailable) { return false; } try { ColorData val = NeedleforgePlugin.AddToolColor("White", Color.white, false); type = (ToolItemType)(int)val.Type; WhiteColorData = val; val.AddValidTypes((ToolItemType[])(object)new ToolItemType[2] { (ToolItemType)1, (ToolItemType)2 }); _log.LogInfo((object)$"Needleforge white tool color registered: type={type}"); return true; } catch (Exception arg) { _log.LogError((object)$"Needleforge AddToolColor(White) failed: {arg}"); return false; } } public bool TrySetWhiteToolHeader(Sprite sprite) { if (!IsAvailable || WhiteColorData == null || (Object)(object)sprite == (Object)null) { return false; } try { WhiteColorData.header = sprite; _log.LogInfo((object)"Needleforge white tool section header set."); return true; } catch (Exception arg) { _log.LogError((object)$"Needleforge white tool header failed: {arg}"); return false; } } public ToolData? GetToolDataByName(string name) { if (!IsAvailable) { return null; } return NeedleforgePlugin.GetToolDataByName(name); } public void Dispose() { } } public sealed class PimpiloExplosionSpawner { public const string PrefabName = "Tool_pinpilo_explosion"; private readonly ManualLogSource _log; private GameObject? _prefab; private bool _warnedResolve; private bool _warnedSpawn; public PimpiloExplosionSpawner(ManualLogSource log) { _log = log ?? throw new ArgumentNullException("log"); } public bool Spawn(Vector3 position) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) GameObject val = ResolvePrefab(); if ((Object)(object)val == (Object)null) { if (!_warnedResolve) { _warnedResolve = true; _log.LogWarning((object)"[PimpiloExplosion] Tool_pinpilo_explosion prefab not resolved; shared native explosion disabled."); } return false; } try { GameObject val2 = ObjectPoolExtensions.Spawn(val, position, Quaternion.identity); if ((Object)(object)val2 == (Object)null) { return false; } val2.SetActive(true); return true; } catch (Exception ex) { if (!_warnedSpawn) { _warnedSpawn = true; _log.LogWarning((object)("[PimpiloExplosion] spawn failed: " + ex.Message)); } return false; } } private GameObject? ResolvePrefab() { if ((Object)(object)_prefab != (Object)null) { return _prefab; } _prefab = LoadViaAddressables(); if ((Object)(object)_prefab == (Object)null) { _prefab = FindUniqueRuntimeObject(); } if ((Object)(object)_prefab != (Object)null) { _log.LogInfo((object)("[PimpiloExplosion] prefab resolved: " + ((Object)_prefab).name)); } return _prefab; } private GameObject? LoadViaAddressables() { try { object? obj = LoadAddressableObject(typeof(GameObject), "Tool_pinpilo_explosion"); return (GameObject?)((obj is GameObject) ? obj : null); } catch (Exception ex) { _log.LogWarning((object)("[PimpiloExplosion] Addressables load failed for 'Tool_pinpilo_explosion': " + ex.Message)); return null; } } private object? LoadAddressableObject(Type assetType, string key) { try { Type type = (from a in AppDomain.CurrentDomain.GetAssemblies() select a.GetType("UnityEngine.AddressableAssets.Addressables", throwOnError: false)).FirstOrDefault((Type t) => t != null); if (type == null) { return null; } MethodInfo[] array = (from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public) where m.Name == "LoadAssetAsync" && m.IsGenericMethodDefinition && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(string) select m).Take(2).ToArray(); if (array.Length != 1) { return null; } object obj = array[0].MakeGenericMethod(assetType).Invoke(null, new object[1] { key }); if (obj == null) { return null; } MethodInfo method = obj.GetType().GetMethod("WaitForCompletion", Type.EmptyTypes); if (method == null) { return null; } return method.Invoke(obj, null); } catch (Exception ex) { _log.LogWarning((object)("[PimpiloExplosion] Addressables load failed for '" + key + "': " + ex.Message)); return null; } } private GameObject? FindUniqueRuntimeObject() { try { return ((IEnumerable<GameObject>)Resources.FindObjectsOfTypeAll<GameObject>()).FirstOrDefault((Func<GameObject, bool>)((GameObject go) => (Object)(object)go != (Object)null && ((Object)go).name == "Tool_pinpilo_explosion")); } catch (Exception ex) { _log.LogWarning((object)("[PimpiloExplosion] runtime object search failed: " + ex.Message)); return null; } } } public sealed class PlayerExplosionSpawner { private readonly ManualLogSource _log; private readonly MethodInfo? _hit; private bool _warned; public PlayerExplosionSpawner(ManualLogSource log, DynamicPatchResolver resolver) { _log = log; _hit = resolver.Resolve("player.explosion.apply") as MethodInfo; if (_hit == null) { _log.LogWarning((object)"[Explosion] HealthManager.Hit(HitInstance) unresolved; explosions are log-only."); } } public bool Spawn(double damage, float radius) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return false; } return SpawnAt(damage, radius, ((Component)instance).transform.position); } public bool SpawnAt(double damage, float radius, Vector3 position) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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) if (_hit == null || damage <= 0.0) { return false; } try { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return false; } Collider2D[] array = Physics2D.OverlapCircleAll(new Vector2(position.x, position.y), radius); bool result = false; Collider2D[] array2 = array; foreach (Collider2D val in array2) { if (!((Object)(object)val == (Object)null)) { HealthManager componentInParent = ((Component)val).GetComponentInParent<HealthManager>(); if (!((Object)(object)componentInParent == (Object)null) && !((Object)(object)((Component)componentInParent).gameObject == (Object)(object)((Component)instance).gameObject)) { HitInstance val2 = new HitInstance { Source = ((Component)instance).gameObject, AttackType = (AttackTypes)11, DamageDealt = (int)Math.Round(damage), IsHeroDamage = true, IsFirstHit = true, DamageScalingLevel = 1, MagnitudeMultiplier = 1f, Multiplier = 1f }; _hit.Invoke(componentInParent, new object[1] { val2 }); result = true; } } } return result; } catch (Exception ex) { WarnOnce(ex); return false; } } private void WarnOnce(Exception ex) { if (!_warned) { _warned = true; _log.LogWarning((object)("[Explosion] spawn failed (" + ex.GetType().Name + ": " + ex.Message + "); suppressing further warnings.")); } } } public static class ReaperRuneBridge { public static Func<bool> IsDropRateOverrideActive { get; set; } = () => false; public static Func<bool> IsAutoCollectEnabled { get; set; } = () => false; public static Func<int> SubBladeChargedAttacksRemaining { get; set; } = () => 0; public static Func<bool> IsHailOfBladesFullLayers { get; set; } = () => false; public static Func<bool> IsChargeSlashEnhanced { get; set; } = () => false; public static bool IsDropRateWindowActive() { return IsDropRateOverrideActive(); } public static bool IsSubBladeCharged() { return SubBladeChargedAttacksRemaining() > 0; } } public sealed class ReaperStateAccessor { private readonly ManualLogSource _log; private readonly MethodInfo? _stateGetter; private readonly PropertyInfo? _modeProperty; private readonly FieldInfo? _modeField; private readonly PropertyInfo? _durationProperty; private readonly FieldInfo? _durationField; private readonly FieldInfo? _stateField; private bool _rageForced; private bool _disabled; private bool _warned; private float _extraDuration; public ReaperStateAccessor(ManualLogSource log, DynamicPatchResolver resolver) { _log = log; _stateGetter = resolver.Resolve("player.reaper.state.mode") as MethodInfo; _stateField = AccessTools.Field(typeof(HeroController), "reaperState"); if (_stateGetter == null) { _disabled = true; _log.LogWarning((object)"[ReaperState] binding 'player.reaper.state.mode' unresolved; strong icon disabled."); return; } Type returnType = _stateGetter.ReturnType; _modeProperty = AccessTools.Property(returnType, "IsInReaperMode"); _modeField = ((_modeProperty == null) ? AccessTools.Field(returnType, "IsInReaperMode") : null); _durationProperty = AccessTools.Property(returnType, "ReaperModeDurationLeft"); _durationField = ((_durationProperty == null) ? AccessTools.Field(returnType, "ReaperModeDurationLeft") : null); if (_modeProperty == null && _modeField == null) { _disabled = true; _log.LogWarning((object)("[ReaperState] member 'IsInReaperMode' not found on " + returnType.FullName + "; strong icon disabled.")); } else if (_durationProperty == null && _durationField == null) { _disabled = true; _log.LogWarning((object)("[ReaperState] member 'ReaperModeDurationLeft' not found on " + returnType.FullName + "; countdown disabled.")); } else { _disabled = false; _log.LogInfo((object)("[ReaperState] accessor ready: " + returnType.FullName + ".")); } } public bool IsInReaperMode() { if (_disabled || _stateGetter == null) { return false; } object state = GetState(); if (state == null) { return false; } try { return Convert.ToBoolean(GetValue(_modeProperty, _modeField, state)); } catch (Exception ex) { WarnOnce(ex); return false; } } public float SecondsLeft() { if (_disabled || _stateGetter == null) { return 0f; } object state = GetState(); if (state == null) { return 0f; } try { return Convert.ToSingle(GetValue(_durationProperty, _durationField, state)); } catch (Exception ex) { WarnOnce(ex); return 0f; } } public void AddReaperModeDuration(float seconds) { if (_disabled || seconds <= 0f) { return; } _extraDuration += seconds; WriteState(delegate(ReaperCrestStateInfo state) { //IL_0000: 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) if (state.IsInReaperMode) { state.ReaperModeDurationLeft = Math.Max(0f, state.ReaperModeDurationLeft) + seconds; } }); _log.LogDebug((object)$"[ReaperState] extra duration += {seconds} (total extra {_extraDuration})."); } public void ResetExtraDuration() { if (_extraDuration != 0f) { _extraDuration = 0f; _log.LogDebug((object)"[ReaperState] extra duration reset."); } } public void SetRageModeActive(bool active) { if (active) { _rageForced = true; WriteState(delegate(ReaperCrestStateInfo state) { state.IsInReaperMode = true; state.ReaperModeDurationLeft = Gameplay.ReaperModeDuration + _extraDuration; }); } else { _rageForced = false; } } public void RefreshRageDuration() { if (_rageForced) { WriteState(delegate(ReaperCrestStateInfo state) { state.ReaperModeDurationLeft = Gameplay.ReaperModeDuration + _extraDuration; }); } } private void WriteState(Action<ReaperCrestStateInfo> change) { //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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (_stateField == null) { return; } HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return; } try { ReaperCrestStateInfo val = (ReaperCrestStateInfo)_stateField.GetValue(instance); change(val); _stateField.SetValue(instance, val); } catch (Exception ex) { WarnOnce(ex); } } private object? GetState() { try { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return null; } return _stateGetter.Invoke(instance, null); } catch (Exception ex) { WarnOnce(ex); return null; } } private static object? GetValue(PropertyInfo? property, FieldInfo? field, object instance) { if (!(property != null)) { return field.GetValue(instance); } return property.GetValue(instance, null); } private void WarnOnce(Exception ex) { if (!_warned) { _warned = true; _log.LogWarning((object)("[ReaperState] access failed (" + ex.GetType().Name + ": " + ex.Message + "); suppressing further warnings.")); } } } public sealed class ReflectionGameApi : IGameApi, IDisposable { private sealed class NoopDisposable : IDisposable { public void Dispose() { } } private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly DynamicPatchResolver _resolver; private readonly InvulnerabilityLeaseService _leases; private readonly FieldInfo? _healthField; private readonly FieldInfo? _maxHealthField; private readonly PlayerExplosionSpawner? _explosions; private readonly PimpiloExplosionSpawner? _nativeExplosions; private int _trackedHealth; private int _trackedMaxHealth = 1; private int _lastPublishedHealth = -1; private int _lastPublishedMaxHealth = -1; private int? _originalMaxHealthBase; private int? _originalHealth; private bool _maxHealthProjected; public string CurrentCrestId => HeroController.instance?.playerData?.CurrentCrestID ?? "unbound"; public int CurrentSilk => (HeroController.instance?.playerData?.silk).GetValueOrDefault(); public int CurrentHealth => ReadHealth(_healthField, _trackedHealth, isHealth: true); public int MaxHealth => Math.Max(1, ReadHealth(_maxHealthField, _trackedMaxHealth, isHealth: false)); public ReflectionGameApi(ManualLogSource log, GameEventHub events, DynamicPatchResolver resolver, InvulnerabilityLeaseService leases) { _log = log; _events = events; _resolver = resolver; _leases = leases; _healthField = _resolver.ResolveField("player.health.get"); _maxHealthField = _resolver.ResolveField("player.maxhealth.get"); _explosions = new PlayerExplosionSpawner(log, resolver); _nativeExplosions = new PimpiloExplosionSpawner(log); MaxHealthHudSync.Configure(log); if (_healthField == null || _maxHealthField == null) { _log.LogWarning((object)"Health field binding is not fully resolved; rage UI will fall back to event-tracked health (unverified)."); _events.PlayerDamageAfter += OnTrackedDamage; _events.HealResolved += OnTrackedHeal; _events.BenchRested += OnTrackedBench; _events.PlayerDied += OnTrackedDeath; } _events.PlayerDamageAfter += OnHealthSource; _events.HealResolved += OnHealthSource; _events.BenchRested += OnHealthSource; _events.PlayerDied += OnHealthSource; _events.SceneChanged += OnHealthSource; } public bool TryHeal(int amount, string reason) { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null || amount <= 0) { return false; } try { instance.AddHealth(amount); return true; } catch (Exception arg) { _log.LogError((object)$"TryHeal failed: amount={amount}, reason={reason}, error={arg}"); return false; } } public bool TrySpendSilk(int amount, string reason) { HeroController instance = HeroController.instance; if (instance?.playerData == null) { return false; } if (amount < 0) { return false; } if (instance.playerData.silk < amount) { return false; } try { instance.TakeSilk(amount, (SilkTakeSource)4); return true; } catch (Exception arg) { _log.LogError((object)$"TrySpendSilk failed: amount={amount}, reason={reason}, error={arg}"); return false; } } public void AddSilk(int amount, string reason) { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null || amount <= 0) { return; } try { instance.AddSilk(amount, false, (SilkAddSource)0, false); } catch (Exception arg) { _log.LogError((object)$"AddSilk failed: amount={amount}, reason={reason}, error={arg}"); } } public void AddShards(int amount, string reason) { if (amount <= 0) { return; } PlayerData instance = PlayerData.instance; if (instance == null) { return; } try { instance.AddShards(amount); } catch (Exception arg) { _l
CrestsEvolve.InsectMayCry/CrestsEvolve.InsectMayCry.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CrestsEvolve.Core.CrestSwitching; using CrestsEvolve.Core.Events; using CrestsEvolve.Core.State; using CrestsEvolve.Core.UI; using CrestsEvolve.Features; using CrestsEvolve.Features.CrestSwitching; using CrestsEvolve.Features.StatusIcons; using CrestsEvolve.GameInterop; using CrestsEvolve.GameInterop.CrestSwitching; using CrestsEvolve.GameInterop.Input; using CrestsEvolve.InsectMayCry.Features.AttackModules; using CrestsEvolve.InsectMayCry.Features.CrossStitch; using CrestsEvolve.InsectMayCry.Features.SecondToolSet; using CrestsEvolve.InsectMayCry.Features.Vergil; using CrestsEvolve.InsectMayCry.Features.WindBlade; using CrestsEvolve.InsectMayCry.GameInterop; using CrestsEvolve.Patches; using GlobalSettings; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using InControl; using Microsoft.CodeAnalysis; using Needleforge; using Needleforge.Data; using Silksong.FsmUtil; using TeamCherry.Localization; using TeamCherry.SharedUtils; using UnityEngine; using UnityEngine.Events; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("CrestsEvolve.Core.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CrestsEvolve.InsectMayCry")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+757e75a2d4534028308600b7117b47c56eb48037")] [assembly: AssemblyProduct("CrestsEvolve.InsectMayCry")] [assembly: AssemblyTitle("CrestsEvolve.InsectMayCry")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } internal static class IsExternalInit { } } namespace CrestsEvolve.Patches { [HarmonyPatch(typeof(HeroController), "Awake")] internal static class CrestQuickSwitchHeroAwakePatch { [HarmonyPostfix] private static void Postfix(HeroController __instance) { CrestQuickSwitchIntegration.Instance?.AttachHero(__instance); } } [HarmonyPatch(typeof(HeroController), "Update")] internal static class CrestQuickSwitchHeroUpdatePatch { [HarmonyPostfix] private static void Postfix(HeroController __instance) { CrestQuickSwitchIntegration.Instance?.OnHeroUpdate(__instance); } } [HarmonyPatch(typeof(ToolItemManager), "TryReplenishTools")] internal static class CrestQuickSwitchBenchReplenishPatch { [HarmonyPrefix] private static bool Prefix(ref bool doReplenish, ReplenishMethod method) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) return CrestQuickSwitchIntegration.Instance?.TryReplenishAllCrestsAtBench(ref doReplenish, method) ?? true; } } } namespace CrestsEvolve.GameInterop.CrestSwitching { public sealed class CrestNameProvider { private readonly ConfigEntry<string> _queenCrestId; public string? Toolmaster => SafeCrestName(() => Gameplay.ToolmasterCrest); public string Queen => _queenCrestId.Value; public string? Spell => SafeCrestName(() => Gameplay.SpellCrest); public string? Hunter => GetBestHunterName(); public string? Reaper => SafeCrestName(() => Gameplay.ReaperCrest); public string? Warrior => SafeCrestName(() => Gameplay.WarriorCrest); public string? Wanderer => SafeCrestName(() => Gameplay.WandererCrest); public string? Witch => SafeCrestName(() => Gameplay.WitchCrest); public CrestNameProvider(ConfigEntry<string> queenCrestId) { _queenCrestId = queenCrestId; } public string[] AllCrestNames() { return new string[8] { Toolmaster, Queen, Spell, Hunter, Reaper, Warrior, Wanderer, Witch }.Where((string x) => !string.IsNullOrWhiteSpace(x)).Distinct<string>(StringComparer.Ordinal).ToArray(); } public string? ResolveDirectionTarget(string current, int directionMask, bool isLong) { return directionMask switch { 1 => ToggleLogic(current, isLong, Toolmaster, Queen), 2 => ToggleLogic(current, isLong, Spell, Hunter), 4 => ToggleLogic(current, isLong, Reaper, Warrior), 8 => ToggleLogic(current, isLong, Wanderer, Witch), _ => null, }; } private static string? ToggleLogic(string current, bool isLong, string? primary, string? secondary) { if (string.IsNullOrWhiteSpace(primary) || string.IsNullOrWhiteSpace(secondary)) { return null; } if (!isLong) { if (!(current == primary)) { return primary; } return secondary; } if (!(current == secondary)) { return secondary; } return primary; } private static string? SafeCrestName(Func<ToolCrest> getter) { try { ToolCrest obj = getter(); return (obj != null) ? obj.name : null; } catch { return null; } } private string? GetBestHunterName() { try { if ((Object)(object)Gameplay.HunterCrest3 != (Object)null && Gameplay.HunterCrest3.IsUnlocked) { return Gameplay.HunterCrest3.name; } } catch { } try { if ((Object)(object)Gameplay.HunterCrest2 != (Object)null && Gameplay.HunterCrest2.IsUnlocked) { return Gameplay.HunterCrest2.name; } } catch { } return SafeCrestName(() => Gameplay.HunterCrest); } } public sealed class CrestSwitchService { private readonly ManualLogSource _log; private readonly HeroReflectionCache _heroReflection; private readonly ConfigEntry<float> _invulnerabilitySeconds; private readonly ConfigEntry<bool> _replenishAllCrestsAtBench; private readonly ConfigEntry<string> _queenCrestId; private readonly ConfigEntry<int> _postSwapRecoveryFrames; public bool IsApplyingSwap { get; private set; } public bool IsReplenishLooping { get; private set; } public bool PostSwapWasFromDash { get; private set; } public int PostSwapRecoveryUntilFrame { get; private set; } = -1; public CrestSwitchService(ManualLogSource log, HeroReflectionCache heroReflection, ConfigEntry<float> invulnerabilitySeconds, ConfigEntry<bool> replenishAllCrestsAtBench, ConfigEntry<string> queenCrestId, ConfigEntry<int> postSwapRecoveryFrames) { _log = log; _heroReflection = heroReflection; _invulnerabilitySeconds = invulnerabilitySeconds; _replenishAllCrestsAtBench = replenishAllCrestsAtBench; _queenCrestId = queenCrestId; _postSwapRecoveryFrames = postSwapRecoveryFrames; } public bool IsSafeToSwapNow(HeroController hero) { if ((Object)(object)hero == (Object)null || hero.playerData == null) { return false; } if (_heroReflection.IsDashLikeActive(hero)) { return false; } if (_heroReflection.IsAttackOrChargeActive(hero)) { return false; } if (!_heroReflection.HasControl(hero)) { return false; } return true; } public bool TryApply(string targetName, string reason, bool fromDash, HeroController hero) { if (IsApplyingSwap || hero?.playerData == null) { return false; } IsApplyingSwap = true; try { ToolCrest crestByName = ToolItemManager.GetCrestByName(targetName); if ((Object)(object)crestByName == (Object)null) { _log.LogWarning((object)("Crest quick switch target not found: " + targetName)); return false; } int silk = hero.playerData.silk; if (fromDash) { _heroReflection.NormalizeDashExit(hero, true); } ToolEquipsSnapshot.Log(_log, "switch-before:" + reason + ":" + crestByName.name); _heroReflection.ApplyInvincibility(hero, Math.Max(0f, _invulnerabilitySeconds.Value)); hero.ResetAllCrestState(); ToolItemManager.SetEquippedCrest(crestByName.name); ToolItemManager.SendEquippedChangedEvent(true); ToolEquipsSnapshot.Log(_log, "switch-after:" + reason + ":" + crestByName.name); hero.playerData.silk = silk; if (fromDash) { PostSwapWasFromDash = true; PostSwapRecoveryUntilFrame = Time.frameCount + Math.Max(1, _postSwapRecoveryFrames.Value); _heroReflection.NormalizeDashExit(hero, true); } _log.LogInfo((object)("Crest switched -> " + crestByName.name + " [" + reason + "]")); return true; } catch (Exception arg) { _log.LogError((object)$"Crest switch failed: target={targetName}, reason={reason}, error={arg}"); return false; } finally { IsApplyingSwap = false; } } public void EndPostSwapRecovery() { PostSwapWasFromDash = false; PostSwapRecoveryUntilFrame = -1; } public unsafe bool TryReplenishAllCrestsAtBench(ref bool doReplenish, ReplenishMethod method, HeroController hero, CrestNameProvider names) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (!_replenishAllCrestsAtBench.Value) { return true; } if (hero?.playerData == null) { return true; } if (((object)(*(ReplenishMethod*)(&method))/*cast due to .constrained prefix*/).ToString().IndexOf("Bench", StringComparison.OrdinalIgnoreCase) < 0) { return true; } if (IsReplenishLooping) { return true; } string currentCrestID = hero.playerData.CurrentCrestID; string[] array = names.AllCrestNames(); try { IsReplenishLooping = true; string[] array2 = array; for (int i = 0; i < array2.Length; i++) { ToolCrest crestByName = ToolItemManager.GetCrestByName(array2[i]); if (!((Object)(object)crestByName == (Object)null) && crestByName.IsUnlocked && !crestByName.IsHidden) { hero.ResetAllCrestState(); ToolItemManager.SetEquippedCrest(crestByName.name); ToolItemManager.TryReplenishTools(true, method); } } } catch (Exception arg) { _log.LogError((object)$"Replenish-all-crests-at-bench failed: {arg}"); } finally { try { hero.ResetAllCrestState(); ToolItemManager.SetEquippedCrest(currentCrestID); ToolItemManager.SendEquippedChangedEvent(true); } catch (Exception arg2) { _log.LogError((object)$"Failed to restore crest after bench replenishment: {arg2}"); } IsReplenishLooping = false; } return true; } } } namespace CrestsEvolve.Features.CrestSwitching { public sealed class CrestQuickSwitchIntegration : IDisposable { private const int UpMask = 1; private const int DownMask = 2; private const int LeftMask = 4; private const int RightMask = 8; private static CrestQuickSwitchIntegration? _instance; private readonly ManualLogSource _log; private readonly Harmony _harmony; private readonly NativeInputAdapter _input; private readonly HeroReflectionCache _heroReflection; private readonly DashSafetyPolicy _safety; private readonly CrestSwapQueue _queue = new CrestSwapQueue(); private readonly CrestNameProvider _names; private readonly CrestSwitchService _switchService; private readonly ConfigEntry<bool> _enabled; private readonly ConfigEntry<float> _longPressThreshold; private readonly ConfigEntry<float> _specialPressThreshold; private readonly ConfigEntry<float> _cooldownSeconds; private readonly ConfigEntry<int> _dashSettleFrames; private readonly ConfigEntry<string> _mainActionCandidates; private readonly ConfigEntry<bool> _verboseLogging; private HeroController? _hero; private bool _disposed; private float _nextSwapTime; private float _mainKeyDownTime = -1f; private int _lastChosenDirMask; private bool _swappedThisPress; private bool _specialTriggeredThisPress; private int _lastTriggeredDirMask; private bool _lastTriggeredLong; private bool _previousMainHeld; private int _previousDirectionMask; private long _nextRequestId = 1L; internal static CrestQuickSwitchIntegration? Instance => _instance; private CrestQuickSwitchIntegration(ManualLogSource log, ConfigFile config, Harmony harmony) { //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Expected O, but got Unknown _log = log; _harmony = harmony; _enabled = config.Bind<bool>("CrestQuickSwitch", "Enabled", true, "Enable directional crest quick switching."); _longPressThreshold = config.Bind<float>("CrestQuickSwitch", "LongPressThresholdSeconds", 0.2f, "Holding the main action for at least this duration selects the secondary crest."); _specialPressThreshold = config.Bind<float>("CrestQuickSwitch", "SpecialPressThresholdSeconds", 3f, "Holding the main action without a direction toggles Cursed/Cloakless crest."); _cooldownSeconds = config.Bind<float>("CrestQuickSwitch", "CooldownSeconds", 0.3f, "Minimum time between two crest switches."); ConfigEntry<float> invulnerabilitySeconds = config.Bind<float>("CrestQuickSwitch", "SwapInvulnerabilitySeconds", 0.1f, "Short protection applied when a queued switch is committed."); _dashSettleFrames = config.Bind<int>("CrestQuickSwitch", "DashSettleFrames", 3, "Frames to wait after the final dash frame before changing crest. Increase to 4-5 if another movement mod still leaves dash velocity behind."); ConfigEntry<int> postSwapRecoveryFrames = config.Bind<int>("CrestQuickSwitch", "PostSwapRecoveryFrames", 2, "Frames after a dash-origin switch during which stale dash flags and horizontal velocity are cleared."); ConfigEntry<string> queenCrestId = config.Bind<string>("CrestQuickSwitch", "QueenCrestId", "Vergil", "Internal ID/name used by the custom Silk Mother/Queen crest."); _mainActionCandidates = config.Bind<string>("CrestQuickSwitch", "MainActionCandidates", "taunt,ringTaunt,quickMap,dreamNail,inventory,cast", "Comma-separated game inputActions member names. The first existing action is used as the quick-switch modifier."); ConfigEntry<bool> replenishAllCrestsAtBench = config.Bind<bool>("CrestQuickSwitch", "ReplenishAllCrestsAtBench", false, "Legacy behavior from the standalone mod. Disabled by default because the overhaul should eventually replenish virtual tool inventories without temporarily changing crests."); _verboseLogging = config.Bind<bool>("CrestQuickSwitch", "VerboseLogging", false, "Write input binding, queue, dash recovery, and switch diagnostics."); _heroReflection = new HeroReflectionCache(log); _input = new NativeInputAdapter(log); _safety = new DashSafetyPolicy(() => Math.Max(1, _dashSettleFrames.Value)); _names = new CrestNameProvider(queenCrestId); _switchService = new CrestSwitchService(log, _heroReflection, invulnerabilitySeconds, replenishAllCrestsAtBench, queenCrestId, postSwapRecoveryFrames); } public static CrestQuickSwitchIntegration Install(ManualLogSource log, ConfigFile config, Harmony harmony) { if (_instance != null) { return _instance; } CrestQuickSwitchIntegration? crestQuickSwitchIntegration = (_instance = new CrestQuickSwitchIntegration(log, config, harmony)); harmony.CreateClassProcessor(typeof(CrestQuickSwitchHeroAwakePatch)).Patch(); harmony.CreateClassProcessor(typeof(CrestQuickSwitchHeroUpdatePatch)).Patch(); harmony.CreateClassProcessor(typeof(CrestQuickSwitchBenchReplenishPatch)).Patch(); crestQuickSwitchIntegration.AttachHero(HeroController.instance); log.LogInfo((object)"Crest quick switch integrated into CrestsEvolve."); return crestQuickSwitchIntegration; } public void Dispose() { if (!_disposed) { _disposed = true; _input.Reset(); _queue.Cancel(); _safety.Reset(); _hero = null; _instance = null; } } internal void AttachHero(HeroController? hero) { if (!_disposed && !((Object)(object)hero == (Object)null) && _hero != hero) { _hero = hero; _heroReflection.Initialize(hero); _input.Bind(hero, (IReadOnlyList<string>)ParseCandidates(_mainActionCandidates.Value)); ResetPressState(); _previousMainHeld = false; _previousDirectionMask = 0; Trace("Attached HeroController and refreshed native input bindings."); } } internal void OnHeroUpdate(HeroController hero) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (_disposed || !_enabled.Value) { return; } AttachHero(hero); if (!((Object)(object)_hero == (Object)null) && _hero.playerData != null) { _input.RefreshIfActionSetChanged(_hero, (IReadOnlyList<string>)ParseCandidates(_mainActionCandidates.Value)); TrackUnsafeFrames(); RunPostSwapRecovery(); TryConsumeQueuedSwap(); if (!_switchService.IsReplenishLooping && !_switchService.IsApplyingSwap && _input.IsReady) { InputFrame frame = _input.ReadFrame(); ProcessInputFrame(frame); } } } internal bool TryReplenishAllCrestsAtBench(ref bool doReplenish, ReplenishMethod method) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_hero == (Object)null)) { return _switchService.TryReplenishAllCrestsAtBench(ref doReplenish, method, _hero, _names); } return true; } private void ProcessInputFrame(InputFrame frame) { bool num = ((InputFrame)(ref frame)).MainPressed || (((InputFrame)(ref frame)).MainHeld && !_previousMainHeld); bool flag = ((InputFrame)(ref frame)).MainReleased || (!((InputFrame)(ref frame)).MainHeld && _previousMainHeld); int downMask = ((InputFrame)(ref frame)).DirectionMask & ~_previousDirectionMask; _previousMainHeld = ((InputFrame)(ref frame)).MainHeld; _previousDirectionMask = ((InputFrame)(ref frame)).DirectionMask; if (num) { _mainKeyDownTime = Time.unscaledTime; ResetPressState(); } if (flag || (_mainKeyDownTime >= 0f && !((InputFrame)(ref frame)).MainHeld)) { _mainKeyDownTime = -1f; ResetPressState(); } else { if (!((InputFrame)(ref frame)).MainHeld) { return; } if (_mainKeyDownTime < 0f) { _mainKeyDownTime = Time.unscaledTime; } int num2 = ChooseDirection(((InputFrame)(ref frame)).DirectionMask, downMask); float num3 = Time.unscaledTime - _mainKeyDownTime; if (num2 == 0 && !_specialTriggeredThisPress && num3 >= Math.Max(0.1f, _specialPressThreshold.Value)) { HandleSpecialSwap(); _specialTriggeredThisPress = true; _nextSwapTime = Time.unscaledTime + Math.Max(0f, _cooldownSeconds.Value); } else if (num2 != 0 && !(Time.unscaledTime < _nextSwapTime)) { bool flag2 = num3 >= Math.Max(0.01f, _longPressThreshold.Value); bool num4 = num2 != _lastTriggeredDirMask; bool flag3 = !num4 && flag2 != _lastTriggeredLong; if (num4 || (!_swappedThisPress && flag3)) { PerformDirectionalSwap(num2, flag2); } } } } private void ResetPressState() { _lastChosenDirMask = 0; _lastTriggeredDirMask = 0; _lastTriggeredLong = false; _swappedThisPress = false; _specialTriggeredThisPress = false; } private void HandleSpecialSwap() { if (_hero?.playerData != null) { string currentCrestID = _hero.playerData.CurrentCrestID; string text = SafeCrestName(() => Gameplay.CursedCrest); string text2 = SafeCrestName(() => Gameplay.CloaklessCrest) ?? "Cloakless Crest"; if (!string.IsNullOrWhiteSpace(text)) { RequestSwap((currentCrestID == text) ? text2 : text, "SPECIAL_3S"); } } } private void PerformDirectionalSwap(int directionMask, bool isLong) { if (_hero?.playerData != null) { string currentCrestID = _hero.playerData.CurrentCrestID; string text = _names.ResolveDirectionTarget(currentCrestID, directionMask, isLong); if (!string.IsNullOrWhiteSpace(text) && !(text == currentCrestID)) { RequestSwap(text, isLong ? "LONG" : "SHORT"); _lastTriggeredDirMask = directionMask; _lastTriggeredLong = isLong; _swappedThisPress = true; _nextSwapTime = Time.unscaledTime + Math.Max(0f, _cooldownSeconds.Value); } } } private void RequestSwap(string targetName, string reason) { if (_hero?.playerData != null && !string.IsNullOrWhiteSpace(targetName)) { bool flag = _heroReflection.IsDashLikeActive(_hero); bool num = _switchService.IsSafeToSwapNow(_hero); bool flag2 = _safety.HasDashSettled(Time.frameCount); if (num && flag2) { _switchService.TryApply(targetName, reason, fromDash: false, _hero); return; } SwapOrigin origin = ((flag || Time.frameCount - _safety.LastDashFrame <= Math.Max(1, _dashSettleFrames.Value)) ? SwapOrigin.Dash : (_heroReflection.IsAttackOrChargeActive(_hero) ? SwapOrigin.Attack : SwapOrigin.Unknown)); _queue.Enqueue(new SwapRequest(_nextRequestId++, _hero.playerData.CurrentCrestID, targetName, origin, Time.frameCount, Time.unscaledTime, _safety.EarliestFrame(Time.frameCount), 3600.0, PreserveSilk: true, PreserveMarkedHealth: false, reason)); Trace("Queued crest switch -> " + targetName + "; reason=" + reason + "; " + $"dash={_queue.IsDashOrigin}; earliestFrame={_safety.EarliestFrame(Time.frameCount)}"); } } private void TryConsumeQueuedSwap() { if (_hero?.playerData != null) { SwapRequest swapRequest = _queue.TryDequeue(Time.frameCount, Time.unscaledTime, (SwapRequest _) => _switchService.IsSafeToSwapNow(_hero) && _safety.HasDashSettled(Time.frameCount)); if ((object)swapRequest != null && !string.IsNullOrWhiteSpace(swapRequest.ToCrest) && !(swapRequest.ToCrest == _hero.playerData.CurrentCrestID)) { SwapOrigin origin = swapRequest.Origin; bool flag = (uint)(origin - 5) <= 2u; bool flag2 = flag; string reason = (swapRequest.Reason ?? "QUEUED") + (flag2 ? "_DASH_SAFE" : "_SAFE"); _switchService.TryApply(swapRequest.ToCrest, reason, flag2, _hero); } } } private void TrackUnsafeFrames() { if (!((Object)(object)_hero == (Object)null)) { bool flag = _heroReflection.IsDashLikeActive(_hero); bool unsafeNow = flag || _heroReflection.IsAttackOrChargeActive(_hero) || !_heroReflection.HasControl(_hero); _safety.RecordFrame(Time.frameCount, flag, unsafeNow); } } private void RunPostSwapRecovery() { if (_switchService.PostSwapWasFromDash && !((Object)(object)_hero == (Object)null)) { if (Time.frameCount > _switchService.PostSwapRecoveryUntilFrame) { _switchService.EndPostSwapRecovery(); return; } _heroReflection.NormalizeDashExit(_hero, true); Trace($"Dash post-swap recovery frame {Time.frameCount}/{_switchService.PostSwapRecoveryUntilFrame}"); } } private static string? SafeCrestName(Func<ToolCrest> getter) { try { ToolCrest obj = getter(); return (obj != null) ? obj.name : null; } catch { return null; } } private int ChooseDirection(int heldMask, int downMask) { if (BitCount(downMask) == 1) { _lastChosenDirMask = downMask; return downMask; } if (BitCount(heldMask) == 1) { _lastChosenDirMask = heldMask; return heldMask; } if (heldMask != 0 && _lastChosenDirMask != 0 && (heldMask & _lastChosenDirMask) != 0) { return _lastChosenDirMask; } return 0; } private static int BitCount(int value) { int num = 0; while (value != 0) { value &= value - 1; num++; } return num; } private static string[] ParseCandidates(string value) { return (from x in (value ?? string.Empty).Split(new char[3] { ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries) select x.Trim() into x where x.Length > 0 select x).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); } private void Trace(string message) { if (_verboseLogging.Value) { _log.LogInfo((object)("[CrestQuickSwitch] " + message)); } } } } namespace CrestsEvolve.InsectMayCry { [BepInPlugin("local.crestsevolve.insectmaycry", "Insect May Cry", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "local.crestsevolve.insectmaycry"; public const string PluginName = "Insect May Cry"; public const string PluginVersion = "0.1.0"; private Harmony? _harmony; private CrestQuickSwitchIntegration? _quickSwitch; private AttackModuleCatalog? _attackModules; private VergilCrestFeature? _vergil; private SecondToolSetFeature? _secondToolSet; private void Awake() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown if (FoundationServices.Current == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[InsectMayCry] foundation not loaded; quick switch disabled."); return; } _harmony = new Harmony("local.crestsevolve.insectmaycry"); _quickSwitch = CrestQuickSwitchIntegration.Install(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config, _harmony); AttackModuleOptions options = new AttackModuleOptions(((BaseUnityPlugin)this).Config); _attackModules = new AttackModuleCatalog(((BaseUnityPlugin)this).Logger, _harmony, options); _vergil = VergilCrestFeature.Install(((BaseUnityPlugin)this).Logger, _harmony, options); _secondToolSet = new SecondToolSetFeature(((BaseUnityPlugin)this).Logger, _harmony, FoundationServices.Current); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[InsectMayCry] loaded: crest quick switch + 11 attack modules + Dante/Vergil/Nero crest family."); } private void OnDestroy() { _secondToolSet?.Dispose(); _vergil?.Dispose(); _attackModules?.Dispose(); _quickSwitch?.Dispose(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace CrestsEvolve.InsectMayCry.GameInterop { public static class SecondToolSetGameBridge { public static string? CurrentCrestId() { try { return PlayerData.instance?.CurrentCrestID; } catch (Exception ex) { Debug.LogWarning((object)("[SecondToolSet] current crest query failed: " + ex.Message)); return null; } } public static string? GetEquippedToolName(string crestId, int slotIndex) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) try { PlayerData instance = PlayerData.instance; if (instance == null || instance.ToolEquips == null) { return null; } Data data = ((SerializableNamedList<Data, NamedData>)(object)instance.ToolEquips).GetData(crestId); if (data.Slots == null || slotIndex < 0 || slotIndex >= data.Slots.Count) { return null; } string equippedTool = data.Slots[slotIndex].EquippedTool; return string.IsNullOrWhiteSpace(equippedTool) ? null : equippedTool; } catch (Exception ex) { Debug.LogWarning((object)("[SecondToolSet] equipped tool query failed: " + ex.Message)); return null; } } public static SecondToolSnapshot? GetToolSnapshot(string toolName) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_00ae: Unknown result type (might be due to invalid IL or missing references) try { ToolItem toolByName = ToolItemManager.GetToolByName(toolName); if ((Object)(object)toolByName == (Object)null) { return null; } bool flag = (int)toolByName.Type == 3; PlayerData instance = PlayerData.instance; int num = ((flag && instance != null) ? instance.SilkSkillCost : 0); int num2 = ((flag && instance != null) ? instance.silk : 0); bool flag2 = !flag || num2 >= num; ToolItemSkill val = (ToolItemSkill)(object)((toolByName is ToolItemSkill) ? toolByName : null); Sprite val2 = ((val != null && flag2 && (Object)(object)val.HudGlowSprite != (Object)null) ? val.HudGlowSprite : toolByName.HudSpriteModified); if ((Object)(object)val2 == (Object)null) { val2 = toolByName.InventorySpriteModified; } int amountLeft = 0; int storage = 0; if (!flag) { amountLeft = ((instance != null) ? instance.GetToolData(toolName).AmountLeft : 0); storage = ToolItemManager.GetToolStorageAmount(toolByName); } return new SecondToolSnapshot(val2, amountLeft, storage, flag, flag2); } catch (Exception ex) { Debug.LogWarning((object)("[SecondToolSet] tool snapshot failed for '" + toolName + "': " + ex.Message)); return null; } } } public static class SecondToolSetPatches { private sealed class NeroShamanBindMarker : MonoBehaviour { } private sealed class NeroShamanBindGateAction : FsmStateAction { public override void OnEnter() { try { HeroController instance = HeroController.instance; if (instance?.playerData == null || !VergilCrestRules.IsNeroCrest(instance.playerData.CurrentCrestID)) { ((FsmStateAction)this).Finish(); return; } if (SecondToolSetRules.FindSpecialAction(SecondToolSetRegistry.TryGet(instance.playerData.CurrentCrestID), GetPressedBinding()) != SecondSpecialAction.ShamanBind) { ((FsmStateAction)this).Finish(); return; } Fsm fsm = ((FsmStateAction)this).Fsm; object obj; if (fsm == null) { obj = null; } else { FsmVariables variables = fsm.Variables; obj = ((variables != null) ? variables.GetFsmBool("Is Shaman Equipped") : null); } FsmBool val = (FsmBool)obj; if (val != null) { val.Value = true; } } catch (Exception ex) { WarnOnce(ex, "shaman bind gate action"); } ((FsmStateAction)this).Finish(); } } private sealed record ResolvedSlot(int Index, AttackToolBinding Binding) { [CompilerGenerated] public void Deconstruct(out int Index, out AttackToolBinding Binding) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown Index = this.Index; Binding = (AttackToolBinding)(int)this.Binding; } } private static ManualLogSource? _log; private static bool _installed; private static bool _ok; private static bool _warned; private static FieldInfo? _acceptingInputField; private static FieldInfo? _queueStepsLimitField; private static FieldInfo? _customOverrideField; private static MethodInfo? _throwToolMethod; private static HeroController? _queueHero; private static bool _castQueueing; private static int _castQueueSteps; public static bool Install(Harmony harmony, ManualLogSource log) { //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Expected O, but got Unknown //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Expected O, but got Unknown //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Expected O, but got Unknown //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Expected O, but got Unknown //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Expected O, but got Unknown if (_installed) { return _ok; } _installed = true; _log = log; MethodInfo methodInfo = AccessTools.Method(typeof(ToolItemManager), "GetBoundAttackTool", new Type[3] { typeof(AttackToolBinding), typeof(ToolEquippedReadSource), typeof(AttackToolBinding).MakeByRefType() }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(HeroController), "CanBind", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(HeroController), "LookForQueueInput", (Type[])null, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(typeof(HeroController), "ResetInputQueues", (Type[])null, (Type[])null); MethodInfo methodInfo5 = AccessTools.Method(typeof(PlayMakerFSM), "Start", (Type[])null, (Type[])null); _acceptingInputField = AccessTools.Field(typeof(HeroController), "acceptingInput"); _queueStepsLimitField = AccessTools.Field(typeof(HeroController), "TOOLTHROW_QUEUE_STEPS"); _customOverrideField = AccessTools.Field(typeof(ToolItemManager), "customToolOverride"); _throwToolMethod = AccessTools.Method(typeof(HeroController), "ThrowTool", new Type[1] { typeof(bool) }, (Type[])null); if (methodInfo == null || methodInfo2 == null || methodInfo3 == null || _acceptingInputField == null || _queueStepsLimitField == null || _throwToolMethod == null) { _log.LogWarning((object)("[InsectMayCry][SecondToolSet] required members missing; second tool set disabled " + $"(GetBoundAttackTool={methodInfo != null}, CanBind={methodInfo2 != null}, " + $"LookForQueueInput={methodInfo3 != null}, acceptingInput={_acceptingInputField != null}, " + $"TOOLTHROW_QUEUE_STEPS={_queueStepsLimitField != null}, ThrowTool={_throwToolMethod != null}).")); return false; } _ok = true; harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(Declared("OnGetBoundAttackTool")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(Declared("OnCanBind")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(Declared("OnLookForQueueInput")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); if (methodInfo5 != null) { harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, new HarmonyMethod(Declared("OnFsmStart")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { _log.LogWarning((object)"[InsectMayCry][SecondToolSet] PlayMakerFSM.Start not found; Shaman-bind special action will fall back to normal bind."); } if (methodInfo4 != null) { harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(Declared("OnResetInputQueues")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } _log.LogInfo((object)"[InsectMayCry][SecondToolSet] input routing + bind suppression installed (dormant until a crest layout is registered)."); return true; } public static void Reset() { _queueHero = null; _castQueueing = false; _castQueueSteps = 0; } private static MethodInfo Declared(string name) { return AccessTools.DeclaredMethod(typeof(SecondToolSetPatches), name, (Type[])null, (Type[])null); } private static bool IsCastPressed() { try { InputHandler instance = ManagerSingleton<InputHandler>.Instance; return (Object)(object)instance != (Object)null && instance.inputActions != null && instance.inputActions.Cast != null && ((OneAxisInputControl)instance.inputActions.Cast).IsPressed; } catch (Exception ex) { WarnOnce(ex, "cast input query"); return false; } } private static bool IsCustomToolOverrideActive() { if (_customOverrideField == null) { WarnOnce(new InvalidOperationException("customToolOverride field not resolved"), "custom override check"); return true; } try { return _customOverrideField.GetValue(ManagerSingleton<ToolItemManager>.Instance) != null; } catch (Exception ex) { WarnOnce(ex, "custom override query"); return true; } } public static bool OnGetBoundAttackTool(AttackToolBinding binding, ToolEquippedReadSource readSource, ref AttackToolBinding usedBinding, ref ToolItem __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected I4, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected I4, but got Unknown try { if ((int)readSource != 0 && (int)readSource != 1) { return true; } if ((Object)(object)ManagerSingleton<ToolItemManager>.Instance == (Object)null) { return true; } ToolsActiveStates activeState = ToolItemManager.ActiveState; if ((int)activeState == 2) { return true; } if ((int)activeState == 1 && (int)readSource == 0) { return true; } if (CollectableItemManager.IsInHiddenMode()) { return true; } PlayerData instance = PlayerData.instance; if (instance == null || string.IsNullOrEmpty(instance.CurrentCrestID)) { return true; } SecondToolSetLayout secondToolSetLayout = SecondToolSetRegistry.TryGet(instance.CurrentCrestID); if (secondToolSetLayout == null || secondToolSetLayout.SecondSlots.Count == 0) { return true; } if (IsCustomToolOverrideActive()) { return true; } bool second = (int)readSource == 0 && IsCastPressed(); ResolvedSlot resolvedSlot = ResolveSlot(instance.CurrentCrestID, secondToolSetLayout, binding, second); if (resolvedSlot == null) { usedBinding = (AttackToolBinding)(int)binding; __result = null; return false; } usedBinding = (AttackToolBinding)(int)resolvedSlot.Binding; __result = GetEquippedTool(instance, resolvedSlot.Index); return false; } catch (Exception ex) { WarnOnce(ex, "bound tool resolve"); return true; } } public static bool OnCanBind(ref bool __result) { try { HeroController instance = HeroController.instance; if (instance?.playerData == null) { return true; } SecondToolSetLayout layout = SecondToolSetRegistry.TryGet(instance.playerData.CurrentCrestID); if (!SecondToolSetRules.ShouldSuppressBind(layout)) { return true; } SecondToolBinding pressedBinding = GetPressedBinding(); if (SecondToolSetRules.FindSpecialAction(layout, pressedBinding) == SecondSpecialAction.ShamanBind) { __result = true; return false; } __result = false; return false; } catch (Exception ex) { WarnOnce(ex, "bind suppression"); return true; } } public static void OnLookForQueueInput(HeroController __instance) { try { if ((Object)(object)__instance == (Object)null) { return; } if ((Object)(object)_queueHero != (Object)(object)__instance) { _queueHero = __instance; _castQueueing = false; _castQueueSteps = 0; } SecondToolSetLayout secondToolSetLayout = SecondToolSetRegistry.TryGet(__instance.playerData?.CurrentCrestID); if (secondToolSetLayout == null || secondToolSetLayout.SecondSlots.Count == 0) { _castQueueing = false; return; } if (_castQueueing) { _castQueueSteps++; } PlayerAction val = (ManagerSingleton<InputHandler>.Instance?.inputActions)?.Cast; if (val == null) { return; } if (((OneAxisInputControl)val).WasPressed) { if (HasSpecialForPressed(secondToolSetLayout)) { _castQueueing = false; return; } if (ReadAcceptingInput(__instance) && __instance.CanThrowTool(false)) { if (__instance.GetWillThrowTool(true)) { InvokeThrowTool(__instance, isAutoThrow: false); } } else { _castQueueSteps = 0; _castQueueing = true; } } if (ReadAcceptingInput(__instance) && ((OneAxisInputControl)val).IsPressed && _castQueueing && _castQueueSteps <= ReadQueueStepsLimit(__instance) && __instance.CanThrowTool()) { InvokeThrowTool(__instance, isAutoThrow: false); } if (!((OneAxisInputControl)val).IsPressed) { _castQueueing = false; } } catch (Exception ex) { WarnOnce(ex, "cast tool queue"); } } public static void OnResetInputQueues() { _castQueueing = false; _castQueueSteps = 0; } public static void OnFsmStart(PlayMakerFSM __instance) { try { if ((Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).gameObject == (Object)null) { return; } string fsmName = __instance.FsmName; if ((string.Equals(fsmName, "Bind", StringComparison.Ordinal) || string.Equals(fsmName, "Spell Control", StringComparison.Ordinal)) && !((Object)(object)((Component)__instance).GetComponent<NeroShamanBindMarker>() != (Object)null)) { FsmState state = FsmUtil.GetState(__instance, "Bind Type"); if (state == null) { WarnOnce(new InvalidOperationException("Bind FSM state 'Bind Type' missing"), "shaman bind fsm gate"); return; } FsmUtil.InsertAction(state, 0, (FsmStateAction)(object)new NeroShamanBindGateAction()); ((Component)__instance).gameObject.AddComponent<NeroShamanBindMarker>(); } } catch (Exception ex) { WarnOnce(ex, "shaman bind fsm gate install"); } } public static SecondToolBinding GetPressedBinding() { try { HeroActions val = ManagerSingleton<InputHandler>.Instance?.inputActions; if (val != null && val.Up != null && ((OneAxisInputControl)val.Up).IsPressed) { return SecondToolBinding.Up; } if (val != null && val.Down != null && ((OneAxisInputControl)val.Down).IsPressed) { return SecondToolBinding.Down; } } catch (Exception ex) { WarnOnce(ex, "pressed binding"); } return SecondToolBinding.Neutral; } private static bool HasSpecialForPressed(SecondToolSetLayout layout) { return SecondToolSetRules.FindSpecialAction(layout, GetPressedBinding()).HasValue; } private static ResolvedSlot? ResolveSlot(string crestId, SecondToolSetLayout layout, AttackToolBinding binding, bool second) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) ToolCrest crestByName = ToolItemManager.GetCrestByName(crestId); if ((Object)(object)crestByName == (Object)null || crestByName.Slots == null) { return null; } PlayerData instance = PlayerData.instance; if (instance == null || instance.ToolEquips == null) { return null; } Data data = ((SerializableNamedList<Data, NamedData>)(object)instance.ToolEquips).GetData(crestId); if (data.Slots == null) { return null; } int num = Math.Min(crestByName.Slots.Length, data.Slots.Count); if (second) { SecondToolBinding? secondToolBinding = MapBinding(binding); if (!secondToolBinding.HasValue) { return null; } SecondSlotSpec secondSlotSpec = SecondToolSetRules.FindSecondSlot(layout, secondToolBinding.Value); if (secondSlotSpec == null) { return null; } int num2 = secondSlotSpec.Index - 1; if (num2 >= num) { return null; } if (string.IsNullOrEmpty(data.Slots[num2].EquippedTool)) { return null; } ToolItem toolByName = ToolItemManager.GetToolByName(data.Slots[num2].EquippedTool); if ((Object)(object)toolByName == (Object)null || !ToolItemTypeExtensions.IsAttackType(toolByName.Type)) { return null; } return new ResolvedSlot(num2, binding); } for (int i = 0; i < num && i < 3; i++) { if (crestByName.Slots[i].AttackBinding == binding && ToolItemTypeExtensions.IsAttackType(crestByName.Slots[i].Type) && !string.IsNullOrEmpty(data.Slots[i].EquippedTool)) { ToolItem toolByName2 = ToolItemManager.GetToolByName(data.Slots[i].EquippedTool); if (!((Object)(object)toolByName2 == (Object)null) && ToolItemTypeExtensions.IsAttackType(toolByName2.Type)) { return new ResolvedSlot(i, binding); } } } return null; } private static SecondToolBinding? MapBinding(AttackToolBinding binding) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown return (int)binding switch { 1 => SecondToolBinding.Up, 0 => SecondToolBinding.Neutral, 2 => SecondToolBinding.Down, _ => null, }; } private static ToolItem? GetEquippedTool(PlayerData playerData, int index) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (playerData == null || playerData.ToolEquips == null) { return null; } Data data = ((SerializableNamedList<Data, NamedData>)(object)playerData.ToolEquips).GetData(playerData.CurrentCrestID); if (data.Slots == null || index < 0 || index >= data.Slots.Count) { return null; } string equippedTool = data.Slots[index].EquippedTool; if (!string.IsNullOrEmpty(equippedTool)) { return ToolItemManager.GetToolByName(equippedTool); } return null; } private static bool ReadAcceptingInput(HeroController hero) { if (_acceptingInputField == null) { return false; } try { object value = _acceptingInputField.GetValue(hero); return value is bool && (bool)value; } catch (Exception ex) { WarnOnce(ex, "acceptingInput"); return false; } } private static int ReadQueueStepsLimit(HeroController hero) { if (_queueStepsLimitField == null) { return 5; } try { return (_queueStepsLimitField.GetValue(hero) is int num) ? num : 5; } catch (Exception ex) { WarnOnce(ex, "TOOLTHROW_QUEUE_STEPS"); return 5; } } private static void InvokeThrowTool(HeroController hero, bool isAutoThrow) { if (!(_throwToolMethod == null)) { _throwToolMethod.Invoke(hero, new object[1] { isAutoThrow }); } } private static void WarnOnce(Exception ex, string context) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[InsectMayCry][SecondToolSet] " + context + " failed: " + ex.GetType().Name + ": " + ex.Message)); } } } } } namespace CrestsEvolve.InsectMayCry.Features.WindBlade { public sealed class HunterWindBladeFeature : IDisposable { private sealed class NailArtsFsmMarker : MonoBehaviour { } private sealed class SpawnWindBladeAction : FsmStateAction { public override void OnEnter() { try { _instance?.SpawnBarrage(); } catch (Exception ex) { HunterWindBladeFeature? instance = _instance; if (instance != null) { instance._log.LogWarning((object)("[WindBlade] action threw: " + ex.Message)); } } ((FsmStateAction)this).Finish(); } } private sealed class WindBladeTimeout : MonoBehaviour { private float _lifetime; private float _age; public void Init(float lifetime) { _lifetime = Mathf.Max(0.1f, lifetime); _age = 0f; } private void OnEnable() { _age = 0f; } private void Update() { _age += Time.deltaTime; if (_age >= _lifetime) { _age = 0f; ObjectPool.Recycle(((Component)this).gameObject); } } } private readonly ManualLogSource _log; private readonly Harmony _harmony; private readonly AttackModuleOptions _options; private static HunterWindBladeFeature? _instance; private bool _disposed; private GameObject? _projectilePrefab; private AssetBundle? _projectileBundle; private bool _prefabTried; private bool _warnedNoPrefab; private const string ProjectilePrefabName = "Song Knight Projectile"; private const string ProjectilePrefabPath = "Assets/Prefabs/Hornet Enemies/Song Knight Projectile.prefab"; private const string ProjectileBundleFileName = "localpoolprefabs_assets_areahangareasong.bundle"; private const string ProjectileBundleRelPath = "aa/StandaloneWindows64/localpoolprefabs_assets_areahangareasong.bundle"; public HunterWindBladeFeature(ManualLogSource log, Harmony harmony, AttackModuleOptions options) { _log = log ?? throw new ArgumentNullException("log"); _harmony = harmony ?? throw new ArgumentNullException("harmony"); _options = options ?? throw new ArgumentNullException("options"); } public static HunterWindBladeFeature Install(ManualLogSource log, Harmony harmony, AttackModuleOptions options) { if (_instance != null) { return _instance; } HunterWindBladeFeature hunterWindBladeFeature = new HunterWindBladeFeature(log, harmony, options); hunterWindBladeFeature.InstallPatch(); _instance = hunterWindBladeFeature; log.LogInfo((object)"[InsectMayCry][WindBlade] hunter charge-slash wind-blade feature installed."); return hunterWindBladeFeature; } private void InstallPatch() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(PlayMakerFSM), "Start", (Type[])null, (Type[])null); if (methodInfo == null) { _log.LogWarning((object)"[WindBlade] PlayMakerFSM.Start not found; feature disabled."); return; } _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(HunterWindBladeFeature), "OnFsmStart", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _log.LogInfo((object)("[WindBlade] patched " + GeneralExtensions.FullDescription((MethodBase)methodInfo) + ".")); } public static void OnFsmStart(PlayMakerFSM __instance) { HunterWindBladeFeature instance = _instance; if (instance == null || !instance._options.WindBladeEnabled || (Object)(object)__instance == (Object)null || __instance.Fsm == null || __instance.FsmName != "Nail Arts" || (Object)(object)((Component)__instance).gameObject == (Object)null || !((Object)((Component)__instance).gameObject).name.StartsWith("Hero_Hornet", StringComparison.Ordinal) || (Object)(object)((Component)__instance).GetComponent<NailArtsFsmMarker>() != (Object)null) { return; } try { FsmState state = FsmUtil.GetState(__instance, "Do Slash"); if (state == null) { instance._log.LogWarning((object)"[WindBlade] Nail Arts FSM 'Do Slash' state not found."); return; } FsmUtil.InsertAction(state, state.Actions.Length, (FsmStateAction)(object)new SpawnWindBladeAction()); ((Component)__instance).gameObject.AddComponent<NailArtsFsmMarker>(); instance._log.LogInfo((object)"[WindBlade] Nail Arts FSM hook installed (Do Slash -> spawn wind blades)."); } catch (Exception ex) { instance._log.LogWarning((object)("[WindBlade] FSM hook failed: " + ex.GetType().Name + ": " + ex.Message)); } } private void SpawnBarrage() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_00a6: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null || instance.playerData == null) { return; } if (!VergilChargeVariantState.IsImcEquipped() || !VergilCrestRules.IsDanteCrest(instance.playerData.CurrentCrestID) || VergilChargeVariantState.Current != ChargeVariant.Up) { _log.LogDebug((object)"[WindBlade] skipped: not D up-variant charge."); return; } int num; float num2; if (instance.cState != null) { num = (instance.cState.facingRight ? 1 : 0); if (num != 0) { num2 = 0f; goto IL_006f; } } else { num = 0; } num2 = 180f; goto IL_006f; IL_006f: float baseAngle = num2; int windBladeCount = _options.WindBladeCount; Vector3 position = ((Component)instance).transform.position; Vector2 val = ((num != 0) ? Vector2.right : Vector2.left); position += Vector2.op_Implicit(val * _options.WindBladeSpawnForwardOffset); if (windBladeCount > 1 && _options.WindBladeBladeDelaySeconds > 0f) { ((MonoBehaviour)instance).StartCoroutine(SpawnBladeSequence(position, baseAngle, windBladeCount)); return; } Vector2 dir = default(Vector2); for (int i = 0; i < windBladeCount; i++) { float num3 = SpreadAngle(baseAngle, windBladeCount, i); ((Vector2)(ref dir))..ctor(Mathf.Cos(num3 * (MathF.PI / 180f)), Mathf.Sin(num3 * (MathF.PI / 180f))); SpawnBlade(position, dir); } _log.LogInfo((object)$"[WindBlade] hunter charge slash spawned {windBladeCount} wind blade(s)."); } private IEnumerator SpawnBladeSequence(Vector3 origin, float baseAngle, int count) { //IL_000e: 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) Vector2 dir = default(Vector2); for (int i = 0; i < count; i++) { float num = SpreadAngle(baseAngle, count, i); ((Vector2)(ref dir))..ctor(Mathf.Cos(num * (MathF.PI / 180f)), Mathf.Sin(num * (MathF.PI / 180f))); SpawnBlade(origin, dir); if (i < count - 1) { yield return (object)new WaitForSeconds(_options.WindBladeBladeDelaySeconds); } } _log.LogInfo((object)$"[WindBlade] hunter charge slash spawned {count} wind blade(s) sequentially."); } private float SpreadAngle(float baseAngle, int count, int index) { if (count <= 1) { return baseAngle; } float windBladeSpreadDegrees = _options.WindBladeSpreadDegrees; if (windBladeSpreadDegrees <= 0f) { return baseAngle; } float num = baseAngle - windBladeSpreadDegrees / 2f; float num2 = ((count > 1) ? (windBladeSpreadDegrees / (float)(count - 1)) : 0f); return num + num2 * (float)index; } private void SpawnBlade(Vector3 origin, Vector2 dir) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) try { EnsurePrefabLoaded(); if ((Object)(object)_projectilePrefab == (Object)null) { if (!_warnedNoPrefab) { _warnedNoPrefab = true; _log.LogWarning((object)"[WindBlade] Song Knight Projectile prefab unavailable; charge-slash wind blades skipped."); } } else { SpawnFromSongKnightPrefab(origin, dir); } } catch (Exception ex) { _log.LogWarning((object)("[WindBlade] blade spawn failed: " + ex.Message)); } } private void EnsurePrefabLoaded() { if (_prefabTried) { return; } _prefabTried = true; try { _projectileBundle = FindLoadedBundle("localpoolprefabs_assets_areahangareasong.bundle"); if ((Object)(object)_projectileBundle == (Object)null) { string text = Path.Combine(Application.streamingAssetsPath, "aa/StandaloneWindows64/localpoolprefabs_assets_areahangareasong.bundle"); if (File.Exists(text)) { _projectileBundle = AssetBundle.LoadFromFile(text); } else { _log.LogWarning((object)("[WindBlade] bundle not found at " + text)); } } if ((Object)(object)_projectileBundle == (Object)null) { _log.LogWarning((object)"[WindBlade] Song Knight Projectile bundle unavailable; wind blades disabled."); return; } _projectilePrefab = _projectileBundle.LoadAsset<GameObject>("Assets/Prefabs/Hornet Enemies/Song Knight Projectile.prefab") ?? _projectileBundle.LoadAsset<GameObject>("Song Knight Projectile"); if ((Object)(object)_projectilePrefab != (Object)null) { _log.LogInfo((object)("[WindBlade] loaded Song Knight Projectile prefab from " + ((Object)_projectileBundle).name + " (" + ((Object)_projectilePrefab).name + ").")); } else { _log.LogWarning((object)("[WindBlade] 'Assets/Prefabs/Hornet Enemies/Song Knight Projectile.prefab' not found in " + ((Object)_projectileBundle).name + "; wind blades disabled.")); } } catch (Exception ex) { _log.LogWarning((object)("[WindBlade] prefab lookup failed: " + ex.Message)); } } private AssetBundle? FindLoadedBundle(string fileName) { try { foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { if ((Object)(object)allLoadedAssetBundle != (Object)null && string.Equals(((Object)allLoadedAssetBundle).name, fileName, StringComparison.OrdinalIgnoreCase)) { return allLoadedAssetBundle; } } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); foreach (AssetBundle allLoadedAssetBundle2 in AssetBundle.GetAllLoadedAssetBundles()) { if ((Object)(object)allLoadedAssetBundle2 != (Object)null && ((Object)allLoadedAssetBundle2).name.IndexOf(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase) >= 0) { return allLoadedAssetBundle2; } } } catch (Exception ex) { _log.LogWarning((object)("[WindBlade] loaded-bundle search failed: " + ex.Message)); } return null; } private void SpawnFromSongKnightPrefab(Vector3 origin, Vector2 dir) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_projectilePrefab == (Object)null) { return; } GameObject val = ObjectPoolExtensions.Spawn(_projectilePrefab, origin, Quaternion.identity); if (!((Object)(object)val == (Object)null)) { ((Object)val).name = "HunterWindBlade"; tk2dSprite[] componentsInChildren = val.GetComponentsInChildren<tk2dSprite>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((tk2dBaseSprite)componentsInChildren[i]).ForceBuild(); } Vector3 localScale = val.transform.localScale; localScale.x = Mathf.Abs(localScale.x) * ((dir.x >= 0f) ? (-1f) : 1f); val.transform.localScale = localScale; val.layer = 17; Transform val2 = val.transform.Find("Terrain Detector"); if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(false); } DamageHero component = val.GetComponent<DamageHero>(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } val.tag = "Nail Attack"; DamageEnemies val3 = val.GetComponent<DamageEnemies>(); if ((Object)(object)val3 == (Object)null) { val3 = val.AddComponent<DamageEnemies>(); } ConfigurePlayerDamager(val3); Rigidbody2D component2 = val.GetComponent<Rigidbody2D>(); if ((Object)(object)component2 != (Object)null) { component2.linearVelocity = dir * _options.WindBladeSpeed; } WindBladeTimeout windBladeTimeout = val.GetComponent<WindBladeTimeout>(); if ((Object)(object)windBladeTimeout == (Object)null) { windBladeTimeout = val.AddComponent<WindBladeTimeout>(); } windBladeTimeout.Init(_options.WindBladeLifetime); } } private void ConfigurePlayerDamager(DamageEnemies damager) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown if ((Object)(object)damager == (Object)null) { return; } damager.useNailDamage = true; damager.nailDamageMultiplier = (float)_options.WindBladeDamage; damager.attackType = (AttackTypes)7; damager.stunDamage = 1f; damager.canWeakHit = false; damager.magnitudeMult = 1f; damager.direction = 0f; damager.moveDirection = false; damager.contactFSMEvent = ""; damager.damageFSMEvent = ""; damager.slashEffectOverrides = Array.Empty<GameObject>(); damager.corpseDirection = new OverrideFloat(); damager.corpseMagnitudeMult = new OverrideFloat(); damager.currencyMagnitudeMult = new OverrideFloat(); damager.DealtDamage = new UnityEvent(); damager.Tinked = new UnityEvent(); try { typeof(DamageEnemies).GetField("isHeroDamage", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(damager, true); } catch (Exception) { } } public void Dispose() { if (!_disposed) { _disposed = true; _harmony.UnpatchSelf(); if (_instance == this) { _instance = null; } } } } } namespace CrestsEvolve.InsectMayCry.Features.Vergil { public static class DanteCrossStitchRuneRules { public static float FirstCastMinSpawnRadius => 3.5f; public static float FirstCastMaxSpawnRadius => 8f; public static bool ShouldRequestRuneRage(bool enabled, string? crestId, ChargeVariant variant) { if (enabled && VergilCrestRules.IsDanteCrest(crestId)) { return variant == ChargeVariant.Default; } return false; } public static bool ShouldRedirect(bool enabled, string? crestId, bool requested, double now, double until) { if (enabled && VergilCrestRules.IsDanteCrest(crestId) && requested) { return now <= until; } return false; } public static int ClampCasts(int casts) { return Math.Clamp(casts, 1, 3); } public static double ClampInvulnerabilitySeconds(double seconds) { return Math.Max(0.0, seconds); } } public static class DvnSlotLayouts { public static SecondToolSetLayout Dante { get; } = new SecondToolSetLayout("Dante", new SecondSlotSpec[3] { Attack(4, SecondToolBinding.Up, -1.2f, 1.6f), Attack(5, SecondToolBinding.Neutral, -1.6f, -0.7f), Attack(6, SecondToolBinding.Down, -1.5f, -1.6f) }, new SecondSlotSpec[3] { Attack(1, SecondToolBinding.Up, 1.2f, 1.8f), Attack(2, SecondToolBinding.Neutral, 1.5f, -0.8f), Attack(3, SecondToolBinding.Down, 1.2f, -1.6f) }, null, new UtilitySlotSpec[3] { Utility(7, UtilitySlotKind.Yellow, -0.8f, 0.5f), Utility(8, UtilitySlotKind.Yellow, 0.8f, 0.5f), Utility(9, UtilitySlotKind.Blue, 0f, -2.1f) }); public static SecondToolSetLayout Vergil { get; } = new SecondToolSetLayout("Vergil", new SecondSlotSpec[3] { Attack(4, SecondToolBinding.Up, -1.8f, 0.2f, SecondSlotKind.Skill), Attack(5, SecondToolBinding.Neutral, -1.5f, -0.7f, SecondSlotKind.Skill), Attack(6, SecondToolBinding.Down, -0.8f, -1.5f, SecondSlotKind.Skill) }, new SecondSlotSpec[3] { Attack(1, SecondToolBinding.Up, 1.5f, 0.2f, SecondSlotKind.Skill), Attack(2, SecondToolBinding.Neutral, 1.1f, -0.7f, SecondSlotKind.Skill), Attack(3, SecondToolBinding.Down, 0.7f, -1.5f, SecondSlotKind.Skill) }, null, new UtilitySlotSpec[4] { Utility(7, UtilitySlotKind.Yellow, 1.6f, 1.1f), Utility(8, UtilitySlotKind.Blue, 0.5f, 1.6f), Utility(9, UtilitySlotKind.Blue, -1f, 1.6f), Utility(10, UtilitySlotKind.Yellow, -2f, 1.1f) }); public static SecondToolSetLayout Nero { get; } = new SecondToolSetLayout("Nero", new SecondSlotSpec[2] { Attack(4, SecondToolBinding.Up, -0.7f, 1.2f, SecondSlotKind.Skill), Attack(5, SecondToolBinding.Neutral, -1.6f, 0.4f, SecondSlotKind.Skill) }, new SecondSlotSpec[3] { Attack(1, SecondToolBinding.Up, 1.3f, 0.4f), Attack(2, SecondToolBinding.Neutral, 0f, 0f), Attack(3, SecondToolBinding.Down, -1.2f, -0.4f) }, new SecondSpecialBinding[1] { new SecondSpecialBinding(SecondToolBinding.Down, SecondSpecialAction.ShamanBind) }, new UtilitySlotSpec[4] { Utility(7, UtilitySlotKind.Yellow, 1.5f, -0.8f), Utility(8, UtilitySlotKind.Blue, 0.6f, -1.1f), Utility(9, UtilitySlotKind.Blue, -0.5f, -1.5f), Utility(10, UtilitySlotKind.Yellow, -1.5f, -1.2f) }); public static void RegisterAll(Action<string>? warn = null) { SecondToolSetRegistry.Register(Dante, warn); SecondToolSetRegistry.Register(Vergil, warn); SecondToolSetRegistry.Register(Nero, warn); } private static SecondSlotSpec Attack(int index, SecondToolBinding binding, float x, float y, SecondSlotKind kind = SecondSlotKind.Red) { return new SecondSlotSpec(index, binding, kind, x, y, IsLocked: false); } private static UtilitySlotSpec Utility(int index, UtilitySlotKind kind, float x, float y) { return new UtilitySlotSpec(index, kind, x, y, IsLocked: false); } } public static class VergilChargeToolPatches { private static ManualLogSource? _log; private static Harmony? _harmony; private static bool _installed; private static bool _warned; public static void Install(Harmony harmony, ManualLogSource log) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Expected O, but got Unknown //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Expected O, but got Unknown //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Expected O, but got Unknown //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Expected O, but got Unknown //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Expected O, but got Unknown //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Expected O, but got Unknown if (!_installed) { _installed = true; _harmony = harmony; _log = log; MethodInfo methodInfo = AccessTools.Method(typeof(HeroController), "CanThrowTool", new Type[3] { typeof(ToolItem), typeof(AttackToolBinding), typeof(bool) }, (Type[])null); if (methodInfo == null) { Warn("HeroController.CanThrowTool not found; free charge throw may fail when silk/ammo is empty."); } else { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(VergilChargeToolPatches), "OnCanThrowTool", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo2 = AccessTools.PropertyGetter(typeof(PlayerData), "SilkSkillCost"); if (methodInfo2 == null) { Warn("PlayerData.SilkSkillCost getter not found; free skill charge may consume silk."); } else { harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(VergilChargeToolPatches), "OnSilkSkillCost", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo3 = AccessTools.Method(typeof(ToolItemManager), "GetAttackToolBinding", new Type[1] { typeof(ToolItem) }, (Type[])null); if (methodInfo3 == null) { Warn("ToolItemManager.GetAttackToolBinding(ToolItem) not found; free charge throw may fail when tool not equipped."); } else { harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(VergilChargeToolPatches), "OnGetAttackToolBinding", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo4 = AccessTools.Method(typeof(HeroController), "DidUseAttackTool", new Type[1] { typeof(Data) }, (Type[])null); if (methodInfo4 == null) { Warn("HeroController.DidUseAttackTool not found; free charge may consume tool ammo."); } else { harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(VergilChargeToolPatches), "OnDidUseAttackTool", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo5 = AccessTools.Method(typeof(ToolItemManager), "SetEquippedTools", new Type[2] { typeof(string), typeof(List<string>) }, (Type[])null); if (methodInfo5 == null) { Warn("ToolItemManager.SetEquippedTools(string,List<string>) not found; free charge slot guard disabled."); } else { harmony.Patch((MethodBase)methodInfo5, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(VergilChargeToolPatches), "OnSetEquippedTools", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo6 = AccessTools.Method(typeof(ToolItemManager), "SetExtraEquippedTool", new Type[2] { typeof(string), typeof(string) }, (Type[])null); if (methodInfo6 == null) { Warn("ToolItemManager.SetExtraEquippedTool(string,string) not found; free charge slot guard (extra) disabled."); } else { harmony.Patch((MethodBase)methodInfo6, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(VergilChargeToolPatches), "OnSetExtraEquippedTool", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo7 = AccessTools.Method(typeof(ToolItemManager), "ReplaceToolEquips", new Type[2] { typeof(string), typeof(string) }, (Type[])null); if (methodInfo7 == null) { Warn("ToolItemManager.ReplaceToolEquips(string,string) not found; free charge slot guard (replace) disabled."); } else { harmony.Patch((MethodBase)methodInfo7, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(VergilChargeToolPatches), "OnReplaceToolEquips", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo8 = AccessTools.Method(typeof(HeroController), "TakeSilk", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo8 == null) { Warn("HeroController.TakeSilk(int) not found; free silk-shot may consume 1 silk."); } else { harmony.Patch((MethodBase)methodInfo8, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(VergilChargeToolPatches), "OnTakeSilk", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo9 = AccessTools.Method(typeof(DamageEnemies), "DoDamage", new Type[2] { typeof(GameObject), typeof(bool) }, (Type[])null); if (methodInfo9 == null) { Warn("DamageEnemies.DoDamage(GameObject,bool) not found; silk-shot explosion disabled."); } else { harmony.Patch((MethodBase)methodInfo9, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(VergilChargeToolPatches), "OnDoDamage", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } log.LogInfo((object)"[InsectMayCry][Vergil] charge tool patches installed (free throw + silk-shot explosion)."); } } private static bool IsFreeWindowActive() { if (VergilChargeVariantState.FreeChargeToolName == null) { return false; } if (VergilChargeVariantState.FreeChargeToolUntil > 0.0 && Time.timeAsDouble > VergilChargeVariantState.FreeChargeToolUntil) { VergilChargeVariantState.FreeChargeToolName = null; VergilChargeVariantState.FreeChargeToolUntil = 0.0; return false; } return true; } public static bool OnCanThrowTool(ToolItem tool, ref bool __result) { if (!IsFreeWindowActive()) { return true; } string freeChargeToolName = VergilChargeVariantState.FreeChargeToolName; if ((Object)(object)tool == (Object)null || !string.Equals(tool.name, freeChargeToolName, StringComparison.Ordinal)) { return true; } __result = true; return false; } public static bool OnSilkSkillCost(ref int __result) { if (!IsFreeWindowActive()) { return true; } __result = 0; return false; } public static bool OnGetAttackToolBinding(ToolItem tool, ref AttackToolBinding? __result) { if (!IsFreeWindowActive()) { return true; } string freeChargeToolName = VergilChargeVariantState.FreeChargeToolName; if ((Object)(object)tool == (Object)null || !string.Equals(tool.name, freeChargeToolName, StringComparison.Ordinal)) { return true; } __result = (AttackToolBinding)0; return false; } public static bool OnDidUseAttackTool() { return !IsFreeWindowActive(); } public static void OnSetEquippedTools(string crestId, List<string> equippedTools) { try { string freeToolName = GetFreeToolName(); if (freeToolName != null && equippedTools != null) { List<string> collection = VergilFreeToolSlotGuardRules.SanitizeEquippedList(ReadCurrentSlots(crestId), equippedTools, freeToolName); equippedTools.Clear(); equippedTools.AddRange(collection); } } catch (Exception ex) { WarnOnce(ex); } } public static void OnSetExtraEquippedTool(string slotId, ref string toolName) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) try { string freeToolName = GetFreeToolName(); if (freeToolName != null && PlayerData.instance != null) { string equippedTool = ((SerializableNamedList<SlotData, NamedData>)(object)PlayerData.instance.ExtraToolEquips).GetData(slotId).EquippedTool; toolName = VergilFreeToolSlotGuardRules.SanitizeSlotValue(equippedTool, toolName, freeToolName); } } catch (Exception ex) { WarnOnce(ex); } } public static bool OnReplaceToolEquips(string newToolName) { string freeToolName = GetFreeToolName(); if (freeToolName == null) { return true; } return !string.Equals(newToolName, freeToolName, StringComparison.Ordinal); } public static bool OnTakeSilk() { return !IsFreeWindowActive(); } public static void OnDoDamage(DamageEnemies __instance, GameObject target) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance == (Object)null) { return; } string chargeExplosionToolName = VergilChargeVariantState.ChargeExplosionToolName; if (chargeExplosionToolName == null || Time.timeAsDouble > VergilChargeVariantState.ChargeExplosionUntil) { return; } ToolItem representingTool = __instance.RepresentingTool; if ((Object)(object)representingTool == (Object)null || !string.Equals(representingTool.name, chargeExplosionToolName, StringComparison.Ordinal)) { return; } Vector3 val = (((Object)(object)target != (Object)null) ? target.transform.position : ((Component)__instance).transform.position); FoundationServices current = FoundationServices.Current; if (current != null) { IGameApi game = current.Game; if (game != null) { game.SpawnNativeExplosionAt(val); } } VergilChargeVariantState.ChargeExplosionToolName = null; } catch (Exception ex) { ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[InsectMayCry][Vergil] silk-shot explosion failed: " + ex.GetType().Name + ": " + ex.Message)); } } } private static void Warn(string message) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[InsectMayCry][Vergil] " + message)); } } } private static string? GetFreeToolName() { if (!IsFreeWindowActive()) { return null; } return VergilChargeVariantState.FreeChargeToolName; } private static List<string?>? ReadCurrentSlots(string crestId) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (PlayerData.instance == null) { return null; } Data data = ((SerializableNamedList<Data, NamedData>)(object)PlayerData.instance.ToolEquips).GetData(crestId); if (data.Slots == null) { return null; } List<string> list = new List<string>(data.Slots.Count); for (int i = 0; i < data.Slots.Count; i++) { list.Add(data.Slots[i].EquippedTool); } return list; } private static void WarnOnce(Exception ex) { ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[InsectMayCry][Vergil] free charge slot guard failed: " + ex.GetType().Name + ": " + ex.Message)); } } } public static class VergilChargeVariantState { public static ChargeVariant Current { get; set; } public static bool LungeActive { get; set; } public static bool ReleaseWasDashing { get; set; } public static string? FreeChargeToolName { get; set; } public static double FreeChargeToolUntil { get; set; } public static string? ChargeExplosionToolName { get; set; } public static double ChargeExplosionUntil { get; set; } public static bool CrossStitchRequested { get; set; } public static double CrossStitchUntil { get; set; } public static bool CrossStitchRequireRealParry { get; set; } public static bool RuneRageRequested { get; set; } public static double RuneRageUntil { get; set; } public static bool IsImcEquipped() { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null && instance.playerData != null) { return VergilCrestRules.IsImcCrest(instance.playerData.CurrentCrestID); } return false; } } public sealed class VergilCrestFeature : IDisposable { private sealed class CrossStitchTriggerMarker : MonoBehaviour { } private sealed class CrossStitchTriggerAction : FsmStateAction { private static bool _warnedNoParryWait; public override void OnEnter() { try { if (!VergilChargeVariantState.CrossStitchRequested) { return; } if (Time.timeAsDouble > VergilChargeVariantState.CrossStitchUntil) { VergilChargeVariantState.CrossStitchRequested = false; VergilChargeVariantState.CrossStitchRequireRealParry = false; return; } HeroController instance = HeroController.instance; if (DanteCrossStitchRuneRules.ShouldRequestRuneRage(_options?.DanteCrossStitchRuneEnabled ?? false, instance?.playerData?.CurrentCrestID, VergilChargeVariantState.Current)) { VergilChargeVariantState.RuneRageRequested = true; VergilChargeVariantState.RuneRageUntil = Time.timeAsDouble + (_options?.DanteCrossStitchRuneRequestTimeoutSeconds ?? 2.0); } if (VergilChargeVariantState.CrossStitchRequireRealParry) { Wait val = null; Fsm fsm = ((FsmStateAction)this).Fsm; FsmState val2 = ((fsm != null) ? fsm.GetState("Parry Stance") : null); if (((val2 != null) ? val2.Actions : null) != null) { FsmStateAction[] actions = val2.Actions; foreach (FsmStateAction obj in actions) { Wait val3 = (Wait)(object)((obj is Wait) ? obj : null); if (val3 != null) { val = val3; break; } } } if (val != null) { val.time = FsmFloat.op_Implicit(_options?.DanteChargeCrossStitchParryWindowSeconds ?? 0.25f); } else if (!_warnedNoParryWait) { _warnedNoParryWait = true; VergilCrestFeature? instance2 = _instance; if (instance2 != null) { instance2._log.LogWarning((object)"[InsectMayCry][Vergil] Parry Stance Wait not found; parry window override skipped."); } } ((FsmStateAction)this).Finish(); return; } VergilChargeVariantState.CrossStitchRequested = false; VergilChargeVariantState.CrossStitchRequireRealParry = false; Fsm fsm2 = ((FsmStateAction)this).Fsm; if (fsm2 != null) { fsm2.Event("PARRIED"); } } catch (Exception) { } ((FsmStateAction)this).Finish(); } } private sealed class CrossStitchEffectRedirectAction : FsmStateAction { private const string RuneRageTargetState = "Initial Silk Cost"; private const string SharpdartTargetState = "Silk Charge Begin"; private static bool _warned; public override void OnEnter() { try { Fsm fsm = ((FsmStateAction)this).Fsm; AttackModuleOptions options = _options; if (fsm == null || options == null) { ((FsmStateAction)this).Finish(); return; } HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null || instance.playerData == null) { ((FsmStateAction)this).Finish(); return; } string currentCrestID = instance.playerData.CurrentCrestID; if (DanteCrossStitchRuneRules.ShouldRedirect(options.DanteCrossStitchRuneEnabled, currentCrestID, VergilChargeVariantState.RuneRageRequested, Time.timeAsDouble, VergilChargeVariantState.RuneRageUntil)) { VergilChargeVariantState.RuneRageRequested = false; RedirectToRuneRage(fsm, instance, options); ((FsmStateAction)this).Finish(); return; } VergilChargeVariantState.RuneRageRequested = false; if (VergilCrestRules.IsDanteCrest(currentCrestID)) { RedirectToSharpdart(fsm, instance); ((FsmStateAction)this).Finish(); return; } } catch (Exception ex) { WarnOnce(ex.GetType().Name + ": " + ex.Message); } ((FsmStateAction)this).Finish(); } private static void RedirectToRuneRage(Fsm fsm, HeroController hero, AttackModuleOptions options) { if (fsm.GetState("Initial Silk Cost") == null) { WarnOnce("Silk Specials state 'Initial Silk Cost' not found; rune-rage redirect disabled."); return; } PlayMakerFSM val = ResolvePlayMakerFsm(fsm); if ((Object)(object)val == (Object)null) { WarnOnce("PlayMakerFSM component not found; rune-rage redirect disabled."); return; } if (hero.cState != null) { hero.cState.parrying = false; hero.cState.parryAttack = false; } SetFsmInt(fsm, "Casts Left", DanteCrossStitchRuneRules.ClampCasts(options.DanteCrossStitchRuneCasts)); SetFsmFloat(fsm, "Min Spawn Radius", options.DanteCrossStitchRuneMinSpawnRadius); SetFsmFloat(fsm, "Max Spawn Radius", options.DanteCrossStitchRuneMaxSpawnRadius); SetFsmFloat(fsm, "Silk Bomb Cooldown", options.DanteCrossStitchRuneSilkBombCooldownSeconds); SetBindCanBreak(fsm.GameObject, value: true); double num = DanteCrossStitchRuneRules.ClampInvulnerabilitySeconds(options.DanteCrossStitchRuneInvulnerabilitySeconds); if (num > 0.0) { FoundationServices current = FoundationServices.Current; if (current != null) { IGameApi game = current.Game; if (game != null) { game.AcquireInvulnerability("dante.cross-stitch-rune", num); } } } val.SetState("Initial Silk Cost"); } private static void RedirectToSharpdart(Fsm fsm, HeroController hero) { if (fsm.GetState("Silk Charge Begin") == null) { WarnOnce("Silk Specials state 'Silk Charge Begin' not found; sharpdart redirect disabled."); return; } PlayMakerFSM val = ResolvePlayMakerFsm(fsm); if ((Object)(object)val == (Object)null) { WarnOnce("PlayMakerFSM component not found; sharpdart redirect disabled."); return; } if (hero.cState != null) { hero.cState.parrying = false; hero.cState.parryAttack = false; } VergilChargeVariantState.FreeChargeToolName = "Parry"; VergilChargeVariantState.FreeChargeToolUntil = Time.timeAsDouble + (_options?.DanteCrossStitchSharpdartFreeWindowSeconds ?? 0.5); val.SetState("Silk Charge Begin"); } private static PlayMakerFSM? ResolvePlayMakerFsm(Fsm fsm) { if ((Object)(object)((fsm != null) ? fsm.GameObject : null) == (Object)null) { return null; } PlayMakerFSM[] components = fsm.GameObject.GetComponents<PlayMakerFSM>(); foreach (PlayMakerFSM val in components) { if ((Object)(object)val != (Object)null && val.Fsm == fsm) { return val; } } return null; } private static void SetFsmInt(Fsm fsm, string variableName, int value) { object obj; if (fsm == null) { obj = null; } else { FsmVariables variables = fsm.Variables; obj = ((variables != null) ? variables.GetFsmInt(variableName) : null); } FsmInt val = (FsmInt)obj; if (val != null) { val.Value = value; } } private static void SetFsmFloat(Fsm fsm, string variableName, float value) { object obj; if (fsm == null) { obj = null; } else { FsmVariables variables = fsm.Variables; obj = ((variables != null) ? variables.GetFsmFloat(variableName) : null); } FsmFloat val = (FsmFloat)obj; if (val != null) { val.Value = value; } } private static void SetBindCanBreak(GameObject owner, bool value) { if ((Object)(object)owner == (Object)null) { return; } PlayMakerFSM[] components = owner.GetComponents<PlayMakerFSM>(); foreach (PlayMakerFSM val in components) { if (!((Object)(object)val == (Object)null) && !(val.FsmName != "Bind")) { FsmVariables fsmVariables = val.FsmVariables; FsmBool val2 = ((fsmVariables != null) ? fsmVariables.GetFsmBool("Can Bind Break") : null); if (val2 != null) { val2.Value = value; } break; } } } private static void WarnOnce(string message) { if (!_warned) { _warned = true; VergilCrestFeature? instance = _instance; if (instance != null) { instance._log.LogWarning((object)("[InsectMayCry][Vergil] rune-rage redirect: " + message)); } } } } private sealed class CrestSpec { public string Id = ""; public CrestData? Crest; public Action? MovesetHandler; public GameObject? NormalSlash; public GameObject? AltSlash; public GameObject? DownSlash; public GameObject? AltDownSlash; public GameObject? DashStab; public GameObject? ChargeDash; public GameObject? ChargeWitch; public GameObject? ChargeHunter; public GameObject? ChargeShaman; public GameObject? ChargeWanderer; public GameObject? ChargeBeast; public GameObject? ChargeReaper; public GameObject? ChargeSpinSlash; public GameObject? ChargeNone; public GameObject? GetChargeInstance(ChargeVariant variant) { switch (Id) { case "Dante": switch (variant) { case ChargeVariant.Default: return ChargeNone; case ChargeVariant.Up: return ChargeHunter; case ChargeVariant.Down: return ChargeBeast; case ChargeVariant.Dash: return ChargeDash ?? ChargeWanderer; } break; case "Vergil": switch (variant) { case ChargeVariant.Default: return ChargeNone; case ChargeVariant.Up: return ChargeShaman; case ChargeVariant.Down: return ChargeWitch; case ChargeVariant.Dash: return ChargeDash ?? ChargeWanderer; } break; case "Nero": switch (variant) { case ChargeVariant.Default: return ChargeNone; case ChargeVariant.Up: return ChargeReaper; case ChargeVariant.Down: return ChargeNone; case ChargeVariant.Dash: return ChargeDash ?? ChargeWanderer; } break; } return null; } } private sealed class VergilNailArtsMarker : MonoBehaviour { private FsmTransition? _anticFinishTransition; private FsmState? _anticOriginalTarget; public void CaptureAnticFinish(FsmState? antic) { if (antic == null || antic.Transitions == null) { return; } FsmTransition[] transitions = antic.Transitions; foreach (FsmTransition val in transitions) { if (val != null && string.Equals(val.EventName, "FINISHED", StringComparison.Ordinal)) { _anticFinishTransition = val; _anticOriginalTarget = val.ToFsmState; break; } } } public void RouteAnticFinish(FsmState? target) { if (_anticFinishTransition != null && target != null) { _anticFinishTransition.ToFsmState = target; } } private void OnDestroy() { if (_anticFinishTransition != null && _anticOriginalTarget != null) { _anticFinishTransition.ToFsmState = _anticOriginalTarget; } } } private sealed class SprintAirDashMarker : MonoBehaviour { private readonly List<(FsmTransition Transition, FsmState OriginalTarget)> _attackTransitions = new List<(FsmTransition, FsmState)>(); public bool CaptureAttackTarget(FsmState state) { if (((state != null) ? state.Transitions : null) == null) { return false; } FsmTransition[] transitions = state.Transitions; foreach (FsmTransition val in transitions) { if (val != null && string.Equals(val.EventName, "ATTACK", StringComparison.Ordinal)) { _attackTransitions.Add((val, val.ToFsmState)); return true; } } return false; } public void ApplyFamilyTarget(FsmState? dashStabDir) { if (dashStabDir == null) { return; } foreach (var attackTransition in _attackTransitions) { attackTransition.Transition.ToFsmState = dashStabDir; } } public void Restore() { foreach (var (val, toFsmState) in _attackTransitions) { val.ToFsmState = toFsmState; } } private void OnDestroy() { Restore(); } } private sealed class AirDashGateAction : FsmStateAction { public override void OnEnter() { try { Fsm fsm = ((FsmStateAction)this).Fsm; SprintAirDashMarker sprintAirDashMarker = ((fsm != null && (Object)(object)fsm.GameObject != (Object)null) ? fsm.GameObject.GetComponent<SprintAirDashMarker>() : null); if ((Object)(object)sprintAirDashMarker == (Object)null || fsm == null) { ((FsmStateAction)this).Finish(); return; } string text = VergilCrestRules.ResolveAirDashTarget(HeroController.instance?.playerData?.CurrentCrestID, _options?.ShamanDashSlashEnabled ?? false, fsm.GetState("Shaman Antic") != null, fsm.GetState("Nero Start") != null); if (text.Length == 0) { sprintAirDashMarker.Restore(); } else { sprintAirDashMarker.ApplyFamilyTarget(fsm.GetState(text)); } } catch (Exception) { } ((FsmStateAction)this).Finish(); } } private sealed class VergilReleaseDashCaptureAction : FsmStateAction { private static bool _warned; public override void OnEnter() { try { if (_instance == null) { ((FsmStateAction)this).Finish(); return; } HeroController instance = HeroController.instance; VergilChargeVariantState.ReleaseWasDashing = (Object)(object)instance != (Object)null && instance.cState != null && instance.cState.dashing; } catch (Exception ex) { if (!_warned) { _warned = true; VergilCrestFeature? instance2 = _instance; if (instance2 != null) { instance2._log.LogWarning((object)("[InsectMayCry][Vergil] release-dash capture failed: " + ex.GetType().Name + ": " + ex.Message)); } } } ((FsmStateAction)this).Finish(); } } private sealed class VergilChargeVariantPickAction : FsmStateAction { public override void OnEnter() { try { VergilCrestFeature? instance = _instance; Fsm fsm = ((FsmStateAction)this).Fsm; VergilNailArtsMarker vergilNailArtsMarker = ((fsm != null && (Object)(object)fsm.GameObject != (Object)null) ? fsm.GameObject.GetComponent<VergilNailArtsMarker>() : null); if (instance == null || fsm == null) { ((FsmStateAction)this).Finish(); return; } if (!VergilChargeVariantState.IsImcEquipped()) { VergilChargeVariantState.Current = ChargeVariant.None; vergilNailArtsMarker?.RouteAnticFinish(fsm.GetState("Lunge?")); ((FsmStateAction)this).Finish(); return; } string text = HeroController.instance?.playerData?.CurrentCrestID; HeroActions val = ManagerSingleton<InputHandler>.Instance?.inputActions; ChargeVariant chargeVariant = (VergilChargeVariantState.Current = ((val == null) ? ChargeVariant.Default : VergilCrestRules.ResolveChargeVariant(VergilChargeVariantState.ReleaseWasDashing, ((OneAxisInputControl)val.Up).IsPressed, ((OneAxisInputControl)val.Down).IsPressed))); FsmVariables variables = fsm.Variables; FsmString val2 = ((variables != null) ? variables.GetFsmString("Recoil Method") : null); if (val2 != null) { val2.Value = ""; } GameObject val3 = FindChargeInstance(text, chargeVariant); HeroController instance2 = HeroController.instance; bool flag = chargeVariant == ChargeVariant.Down && VergilCrestRules.IsVergilCrest(text) && instance2?.cState != null && !instance2.cState.onGround; if (flag) { val3 = ((text != null && _specById.TryGetValue(text, out CrestSpec value)) ? value.ChargeNone : null); } if ((Object)(object)val3 != (Object)null && fsm.Variables != null) { FsmGameObject fsmGameObject = fsm.Variables.GetFsmGameObject("Current Charge Slash"); if (fsmGameObject != null) { fsmGameObject.Value = val3; } } string text2 = (((chargeVariant == ChargeVariant.Down && VergilCrestRules.IsVergilCrest(text) && !flag) || (chargeVariant == ChargeVariant.Dash && VergilCrestRules.IsNeroCrest(text))) ? "Lunge?" : "Slash Recoil?"); vergilNailArtsMarker?.RouteAnticFinish(fsm.GetState(text2)); } catch (Exception) { } ((FsmStateAction)this).Finish(); } } private sealed class ImcBeastChargeAction : FsmStateAction { public override void OnEnter() { try { HeroController instance = HeroController.instance; if (instance?.playerData == null || VergilChargeVariantState.Current != ChargeVariant.Down || !VergilCrestRules.IsDanteCrest(instance.playerData.CurrentCrestID)) { return; } ApplyBeastChargeVelocity(instance); } catch (Exception) { } ((FsmStateAction)this).Finish(); } } private sealed class VergilDefaultChargeAction : FsmStateAction { private static bool _warned; public override void OnEnter() { try { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null || instance.playerData == null || !VergilChargeVariantState.IsImcEquipped()) { ((FsmStateAction)this).Finish(); return; } string currentCrestID = instance.playerData.CurrentCrestID; switch
CrestsEvolve.LegendsNeverDie/CrestsEvolve.LegendsNeverDie.dll
Decompiled 2 weeks 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.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CrestsEvolve.Core.Combat; using CrestsEvolve.Core.Events; using CrestsEvolve.Core.Input; using CrestsEvolve.Core.State; using CrestsEvolve.Core.Time; using CrestsEvolve.Core.UI; using CrestsEvolve.Features; using CrestsEvolve.Features.Plasma; using CrestsEvolve.Features.Runa; using CrestsEvolve.Features.StatusIcons; using CrestsEvolve.GameInterop; using CrestsEvolve.GameInterop.Patches; using CrestsEvolve.LegendsNeverDie.Bootstrap; using GlobalSettings; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using Microsoft.CodeAnalysis; using Silksong.FsmUtil; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("CrestsEvolve.Core.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CrestsEvolve.LegendsNeverDie")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+757e75a2d4534028308600b7117b47c56eb48037")] [assembly: AssemblyProduct("CrestsEvolve.LegendsNeverDie")] [assembly: AssemblyTitle("CrestsEvolve.LegendsNeverDie")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } internal static class IsExternalInit { } } namespace CrestsEvolve.GameInterop { public static class BlueHealthTintBridge { public const string PoisonVariableName = "Is Poison"; private static readonly Color RedColor = new Color(0.78f, 0.16f, 0.16f, 1f); private static readonly Color GreenColor = new Color(0.28f, 0.78f, 0.36f, 1f); public static Func<bool>? IsBeastTintActive; public static Func<bool>? IsWitchTintActive; public static Func<bool>? IsCursedTintActive; private static ManualLogSource? _log; private static bool _warned; public static void Configure(ManualLogSource log) { _log = log; _warned = false; } public static void MarkActivePostfix(BlueHealth __instance) { ApplyTint(__instance); } public static void CheckPoisonPostfix(BlueHealth __instance) { ApplyTint(__instance); } public static void SetPoisonTintOverridePostfix(SetPoisonTintOverride __instance) { try { object obj; if (__instance == null) { obj = null; } else { Fsm fsm = ((FsmStateAction)__instance).Fsm; obj = ((fsm != null) ? fsm.GameObject : null); } GameObject val = (GameObject)obj; if (!((Object)(object)val == (Object)null)) { BlueHealth component = val.GetComponent<BlueHealth>(); if (!((Object)(object)component == (Object)null)) { ApplyTint(component); } } } catch (Exception ex) { WarnOnce(ex); } } public static void RefreshAll() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) try { BlueHealth[] array = Resources.FindObjectsOfTypeAll<BlueHealth>(); foreach (BlueHealth val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { ApplyTint(val); } } } } catch (Exception ex) { WarnOnce(ex); } } private static void ApplyTint(BlueHealth mask) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) try { BlueHealthTintKind blueHealthTintKind = BlueHealthTintResolver.Resolve(IsBeastTintActive?.Invoke() ?? false, IsWitchTintActive?.Invoke() ?? false, IsCursedTintActive?.Invoke() ?? false, IsPoison(mask)); bool flag = ApplyToSprite(mask, blueHealthTintKind switch { BlueHealthTintKind.Red => RedColor, BlueHealthTintKind.Green => GreenColor, _ => null, }); if (flag && blueHealthTintKind != BlueHealthTintKind.None) { ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)$"[BlueHealthTint][probe] tint applied: kind={blueHealthTintKind} go={((Object)mask).name}"); } } else if (!flag && blueHealthTintKind != BlueHealthTintKind.None) { ManualLogSource? log2 = _log; if (log2 != null) { log2.LogDebug((object)$"[BlueHealthTint][probe] tint skipped (sprite not found): kind={blueHealthTintKind} go={((Object)mask).name}"); } } } catch (Exception ex) { WarnOnce(ex); } } private static bool IsPoison(BlueHealth mask) { PlayMakerFSM[] components = ((Component)mask).GetComponents<PlayMakerFSM>(); foreach (PlayMakerFSM val in components) { if (!((Object)(object)val == (Object)null) && val.FsmVariables != null) { FsmBool val2 = val.FsmVariables.FindFsmBool("Is Poison"); if (val2 != null && val2.Value) { return true; } } } return false; } private static bool ApplyToSprite(BlueHealth mask, Color? tint) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) object obj = FindTk2dSprite(mask); if (obj != null) { return SetTk2dColor(obj, tint); } SpriteRenderer componentInChildren = ((Component)mask).GetComponentInChildren<SpriteRenderer>(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.color = (Color)(((??)tint) ?? Color.white); return true; } return false; } private static object? FindTk2dSprite(BlueHealth mask) { Component[] componentsInChildren = ((Component)mask).GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { string name = ((object)val).GetType().Name; if (name == "tk2dSpriteAnimator") { PropertyInfo property = ((object)val).GetType().GetProperty("Sprite"); if (property != null) { object value = property.GetValue(val); if (value != null) { return value; } } } else if (name == "tk2dSprite") { return val; } } return null; } private static bool SetTk2dColor(object sprite, Color? tint) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) Type type = sprite.GetType(); PropertyInfo property = type.GetProperty("color", BindingFlags.Instance | BindingFlags.Public); if (property == null) { return false; } property.SetValue(sprite, (object)(Color)(((??)tint) ?? Color.white)); (tint.HasValue ? type.GetMethod("EnableKeyword", BindingFlags.Instance | BindingFlags.Public) : type.GetMethod("DisableKeyword", BindingFlags.Instance | BindingFlags.Public))?.Invoke(sprite, new object[1] { "RECOLOUR" }); return true; } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[BlueHealthTint] " + ex.GetType().Name + ": " + ex.Message)); } } } } public enum HealSourceKind { Unknown, General, Bind, ChargedAttack } public sealed class HealClassificationBridge { private readonly ManualLogSource _log; private double _bindWindowEnd; private double _chargedWindowEnd; public HealClassificationBridge(ManualLogSource log) { _log = log ?? throw new ArgumentNullException("log"); } public HealSourceKind Classify(string sourceTag) { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null && instance.cState != null && (instance.cState.focusing || instance.cState.isBinding)) { return HealSourceKind.Bind; } double unscaledTimeAsDouble = Time.unscaledTimeAsDouble; if (_bindWindowEnd > unscaledTimeAsDouble) { return HealSourceKind.Bind; } if (_chargedWindowEnd > unscaledTimeAsDouble) { return HealSourceKind.ChargedAttack; } if (Contains(sourceTag, "bind")) { return HealSourceKind.Bind; } if (Contains(sourceTag, "charge")) { return HealSourceKind.ChargedAttack; } return HealSourceKind.Unknown; } public void OpenBindHealWindow(double seconds) { if (!(seconds <= 0.0)) { _bindWindowEnd = Math.Max(_bindWindowEnd, Time.unscaledTimeAsDouble + seconds); _log.LogDebug((object)$"[HealClassification] bind heal classification window opened ({seconds}s)."); } } public void OpenChargedHealWindow(double seconds) { if (!(seconds <= 0.0)) { _chargedWindowEnd = Math.Max(_chargedWindowEnd, Time.unscaledTimeAsDouble + seconds); _log.LogDebug((object)$"[HealClassification] charged heal classification window opened ({seconds}s)."); } } public void NotifyBindCompleted() { _log.LogInfo((object)"[HealClassification][probe] BindCompleted fired; opening bind heal window."); OpenBindHealWindow(1.0); } private static bool Contains(string? value, string keyword) { if (value != null) { return value.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } } public static class PlasmaBindGatePatch { public static Func<bool>? IsGreenPlasmaActive; private static ManualLogSource? _log; private static bool _warned; public static void Configure(ManualLogSource? log) { _log = log; _warned = false; } public static void Prefix(BoolTest __instance) { if (IsGreenPlasmaActive == null || !IsGreenPlasmaActive()) { return; } try { if (((__instance != null) ? ((FsmStateAction)__instance).Fsm : null) != null && string.Equals(((FsmStateAction)__instance).Fsm.Name, "Spell Control", StringComparison.Ordinal) && ((FsmStateAction)__instance).State != null && string.Equals(((FsmStateAction)__instance).State.Name, "Lifeblood State?", StringComparison.Ordinal) && __instance.boolVariable != null && string.Equals(((NamedVariable)__instance.boolVariable).Name, "Chooser", StringComparison.Ordinal)) { __instance.boolVariable.Value = false; ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)"[Plasma] green plasma bind gate unblocked."); } } } catch (Exception ex) { WarnOnce(ex); } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[PlasmaBind] unblock disabled after error: " + ex.GetType().Name + ": " + ex.Message)); } } } } public static class PlasmaRegenSuppressor { public static Func<bool>? IsSuppressing; private static ManualLogSource? _log; private static bool _warned; public static void Configure(ManualLogSource? log) { _log = log; _warned = false; } public static bool Prefix(CallMethodProper __instance) { if (IsSuppressing == null || !IsSuppressing()) { return true; } try { if (((__instance != null) ? ((FsmStateAction)__instance).Fsm : null) == null) { return true; } if (!string.Equals(((FsmStateAction)__instance).Fsm.Name, "Hive Health Regen", StringComparison.Ordinal)) { return true; } if ((Object)(object)((FsmStateAction)__instance).Fsm.GameObject == (Object)null || !string.Equals(((Object)((FsmStateAction)__instance).Fsm.GameObject).name, "Health", StringComparison.Ordinal)) { return true; } if (__instance.methodName == null || !string.Equals(__instance.methodName.Value, "AddHealth", StringComparison.Ordinal)) { return true; } ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)"[Plasma] suppressed vanilla Hive Health Regen AddHealth."); } return false; } catch (Exception ex) { WarnOnce(ex); return true; } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[PlasmaRegen] suppression disabled after error: " + ex.GetType().Name + ": " + ex.Message)); } } } } public static class PlasmaStateBridge { public static Action? OnPlasmaEntered; public static Action? OnPlasmaExited; private static ManualLogSource? _log; private static bool _knownPlasma; public static void Configure(ManualLogSource? log) { _log = log; } public static void HitMaxBlueHealthPostfix(HeroController __instance) { if (!((Object)(object)__instance == (Object)null)) { SetPlasma(__instance.IsInLifebloodState); } } public static void ResetLifebloodStatePostfix(HeroController __instance) { if (!((Object)(object)__instance == (Object)null)) { SetPlasma(__instance.IsInLifebloodState); } } public static void SyncFromHero() { HeroController instance = HeroController.instance; SetPlasma((Object)(object)instance != (Object)null && instance.IsInLifebloodState); } public static void ResetTracking() { _knownPlasma = false; } private static void SetPlasma(bool active) { if (active != _knownPlasma) { _knownPlasma = active; ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)("[PlasmaState] " + (active ? "entered" : "exited"))); } if (active) { OnPlasmaEntered?.Invoke(); } else { OnPlasmaExited?.Invoke(); } } } } public static class PlasmaTintBridge { private static readonly Color RedColor = new Color(0.78f, 0.16f, 0.16f, 1f); private static readonly Color GreenColor = new Color(0.28f, 0.78f, 0.36f, 1f); public static Func<bool>? IsRedPlasmaActive; public static Func<bool>? IsGreenPlasmaActive; private static ManualLogSource? _log; private static bool _warned; private static bool _tinted; public static void Configure(ManualLogSource? log) { _log = log; _warned = false; _tinted = false; } public static void RefreshAll() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) try { Color? val = ResolveColor(); if (val.HasValue) { ApplyTint(val.Value); _tinted = true; } else if (_tinted) { RestoreTint(); _tinted = false; } } catch (Exception ex) { WarnOnce(ex); } } public static void RestoreAll() { try { if (_tinted) { RestoreTint(); _tinted = false; } } catch (Exception ex) { WarnOnce(ex); } } public static void HitMaxBlueHealthBurstPostfix(HeroController __instance) { RefreshAll(); } public static void SpawnObjectFromGlobalPoolPostfix(SpawnObjectFromGlobalPool __instance) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) Color? val = ResolveColor(); if (!val.HasValue) { return; } try { if (((__instance != null) ? ((FsmStateAction)__instance).Fsm : null) == null || !string.Equals(((FsmStateAction)__instance).Fsm.Name, "health_display", StringComparison.Ordinal)) { return; } FsmGameObject gameObject = __instance.gameObject; GameObject val2 = ((gameObject != null) ? gameObject.Value : null); if ((Object)(object)val2 == (Object)null || !string.Equals(((Object)val2).name, "HP Up Particles HiveB", StringComparison.Ordinal)) { return; } FsmGameObject storeObject = __instance.storeObject; GameObject val3 = ((storeObject != null) ? storeObject.Value : null); if (!((Object)(object)val3 == (Object)null)) { ApplyTintToHierarchy(val3, val.Value); ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)"[PlasmaTint][probe] heal particles tinted."); } } } catch (Exception ex) { WarnOnce(ex); } } private static Color? ResolveColor() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) bool num = IsRedPlasmaActive?.Invoke() ?? false; bool flag = IsGreenPlasmaActive?.Invoke() ?? false; if (num) { return RedColor; } if (flag) { return GreenColor; } return null; } private static void ApplyTint(Color color) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) foreach (GameObject item in FindRegularMasks()) { ApplyTintToHierarchy(item, color); } ApplyTintToHierarchy(FindRecoveryBlob(), color); } private static void RestoreTint() { foreach (GameObject item in FindRegularMasks()) { ApplyTintToHierarchy(item, null); } ApplyTintToHierarchy(FindRecoveryBlob(), null); } private static IEnumerable<GameObject> FindRegularMasks() { PlayMakerFSM[] array = Resources.FindObjectsOfTypeAll<PlayMakerFSM>(); foreach (PlayMakerFSM val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && string.Equals(val.FsmName, "health_display", StringComparison.Ordinal) && !((Object)(object)((Component)val).gameObject.GetComponent<BlueHealth>() != (Object)null)) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { yield return ((Component)val).gameObject; } } } } private static GameObject? FindRecoveryBlob() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) PlayMakerFSM[] array = Resources.FindObjectsOfTypeAll<PlayMakerFSM>(); foreach (PlayMakerFSM val in array) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null || !string.Equals(val.FsmName, "Hive Health Regen", StringComparison.Ordinal)) { continue; } Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { FsmVariables fsmVariables = val.FsmVariables; FsmGameObject val2 = ((fsmVariables != null) ? fsmVariables.FindFsmGameObject("Recovery Blob") : null); if ((Object)(object)((val2 != null) ? val2.Value : null) != (Object)null) { return val2.Value; } } } return null; } private static void ApplyTintToHierarchy(GameObject? root, Color? color) { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null) { return; } Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } try { string name = ((object)val).GetType().Name; if (name == "tk2dSpriteAnimator") { PropertyInfo property = ((object)val).GetType().GetProperty("Sprite"); if (property != null) { object value = property.GetValue(val); if (value != null) { SetTk2dColor(value, color); } } } else if (name == "tk2dSprite") { SetTk2dColor(val, color); } else { SpriteRenderer val2 = (SpriteRenderer)(object)((val is SpriteRenderer) ? val : null); if (val2 != null) { val2.color = (Color)(((??)color) ?? Color.white); } } } catch (Exception ex) { WarnOnce(ex); } } } private static void SetTk2dColor(object sprite, Color? color) { //IL_0035: 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) Type type = sprite.GetType(); PropertyInfo property = type.GetProperty("color", BindingFlags.Instance | BindingFlags.Public); if (!(property == null)) { property.SetValue(sprite, (object)(Color)(((??)color) ?? Color.white)); (color.HasValue ? type.GetMethod("EnableKeyword", BindingFlags.Instance | BindingFlags.Public) : type.GetMethod("DisableKeyword", BindingFlags.Instance | BindingFlags.Public))?.Invoke(sprite, new object[1] { "RECOLOUR" }); } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[PlasmaTint] tint disabled after error: " + ex.GetType().Name + ": " + ex.Message)); } } } } public static class ReaperFireBladeBridge { public static Func<int> Layers { get; set; } = () => 0; public static Func<double> DamageMultiplier { get; set; } = () => 1.0; public static bool IsActive() { return Layers() > 0; } } public sealed class ShardGeoBridge { private readonly ManualLogSource _log; private bool _warned; public ShardGeoBridge(ManualLogSource log) { _log = log ?? throw new ArgumentNullException("log"); } public int AddShardsReturningAdded(int amount) { if (amount <= 0) { return 0; } try { HeroController instance = HeroController.instance; if (instance?.playerData == null) { return 0; } int shellShards = instance.playerData.ShellShards; instance.playerData.AddShards(amount); return Math.Max(0, instance.playerData.ShellShards - shellShards); } catch (Exception ex) { WarnOnce(ex); return 0; } } public void AddGeo(int amount) { if (amount <= 0) { return; } try { HeroController instance = HeroController.instance; if (instance != null) { instance.AddGeo(amount); } } catch (Exception ex) { WarnOnce(ex); } } private void WarnOnce(Exception ex) { if (!_warned) { _warned = true; _log.LogWarning((object)("[ShardGeoBridge] " + ex.GetType().Name + ": " + ex.Message)); } } } public sealed class SpiritFlameSpawner { private readonly ManualLogSource _log; private readonly FieldInfo? _wispPrefabField; private bool _warned; public SpiritFlameSpawner(ManualLogSource log) { _log = log; _wispPrefabField = AccessTools.Field(typeof(HeroWispLantern), "wispPrefab"); } public void Spawn() { //IL_0033: 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) try { HeroController instance = HeroController.instance; GameObject val = ResolveWispPrefab(); if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null) { WarnOnce("hero or spirit flame prefab is missing"); return; } Object.Destroy((Object)(object)Object.Instantiate<GameObject>(val, ((Component)instance).transform.position, ((Component)instance).transform.rotation), 1f); _log.LogDebug((object)"[SpiritFlame] blue wisp flame spawned at player."); } catch (Exception ex) { WarnOnce(ex.Message); } } private GameObject? ResolveWispPrefab() { try { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null && _wispPrefabField != null) { HeroWispLantern componentInChildren = ((Component)instance).GetComponentInChildren<HeroWispLantern>(); if ((Object)(object)componentInChildren != (Object)null) { object? value = _wispPrefabField.GetValue(componentInChildren); GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null) { _log.LogInfo((object)"[SpiritFlame][probe] using HeroWispLantern.wispPrefab (E1)."); return val; } } } } catch (Exception ex) { WarnOnce(ex.Message); } GameObject lifebloodHealEffect = Effects.LifebloodHealEffect; if ((Object)(object)lifebloodHealEffect != (Object)null) { _log.LogInfo((object)"[SpiritFlame][probe] fallback Effects.LifebloodHealEffect."); } return lifebloodHealEffect; } private void WarnOnce(string message) { if (!_warned) { _warned = true; _log.LogWarning((object)("[SpiritFlame] " + message)); } } } } namespace CrestsEvolve.GameInterop.Patches { public static class ArchitectToolRepairPatch { private static ManualLogSource? _log; private static bool _warned; public static void Configure(ManualLogSource log) { _log = log; _warned = false; } public static void Postfix() { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) if (!ArchitectRuneBridge.IsToolRepairActive()) { return; } try { PlayerData instance = PlayerData.instance; if (instance == null || string.IsNullOrEmpty(instance.CurrentCrestID)) { return; } List<ToolCrest> allCrests = ToolItemManager.GetAllCrests(); if (allCrests == null) { return; } foreach (ToolCrest item in allCrests) { if ((Object)(object)item == (Object)null) { continue; } string name = item.name; if (string.IsNullOrEmpty(name) || string.Equals(name, instance.CurrentCrestID, StringComparison.OrdinalIgnoreCase)) { continue; } List<ToolItem> equippedToolsForCrest = ToolItemManager.GetEquippedToolsForCrest(name); if (equippedToolsForCrest == null) { continue; } foreach (ToolItem item2 in equippedToolsForCrest) { if ((Object)(object)item2 == (Object)null) { continue; } Data toolData = instance.GetToolData(item2.name); int toolStorageAmount = ToolItemManager.GetToolStorageAmount(item2); ToolItemStatesLiquid val = (ToolItemStatesLiquid)(object)((item2 is ToolItemStatesLiquid) ? item2 : null); if (val != null) { val.RefillRefills(false); ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)("[ArchitectToolRepair] liquid refills reset for " + item2.name + " (" + name + ").")); } } if (toolData.AmountLeft < toolStorageAmount) { toolData.AmountLeft = toolStorageAmount; instance.SetToolData(item2.name, toolData); } ManualLogSource? log2 = _log; if (log2 != null) { log2.LogDebug((object)$"[ArchitectToolRepair] repaired {item2.name} ({name}) to {toolStorageAmount}."); } } } } catch (Exception ex) { WarnOnce(ex); } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[ArchitectToolRepair] " + ex.GetType().Name + ": " + ex.Message)); } } } } public static class ArchitectToolTransformPatch { public static void Prefix(ToolItemType promptToolType, CanChangeEquipsTypes changeType, ref bool __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)changeType == 2 && ArchitectRuneBridge.IsToolTransformActive()) { __result = true; } } } public static class AttackStartedSignalPatch { public static Action? AttackStarted; public static void Postfix() { AttackStarted?.Invoke(); } } public static class BeastDownspikeInvulnPatch { public const int InvulnerabilitySteps = 15; public static void Postfix(Downspike __instance) { if (BeastRuneBridge.IsEnhancementActive()) { HeroController instance = HeroController.instance; if (!((Object)(object)instance == (Object)null) && instance.cState != null) { instance.cState.downspikeInvulnerabilitySteps = Math.Max(instance.cState.downspikeInvulnerabilitySteps, 15); } } } } public static class BeastPredatorChargeSlashInvulnPatch { public static void Postfix(NailAttackBase __instance) { if (BeastRuneBridge.IsPredatorCharged()) { Transform transform = ((Component)__instance).transform; if (!((Object)(object)transform == (Object)null) && ((Object)transform).name.IndexOf("Charge Slash", StringComparison.OrdinalIgnoreCase) >= 0) { BeastRuneBridge.AcquireChargeSlashInvulnerability?.Invoke(); } } } } public static class BeastPredatorDamagePatch { public static double DamageMultiplier { get; set; } = 1.2; public static void Prefix(ref HitInstance hitInstance) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 if (BeastRuneBridge.IsPredatorCharged() && hitInstance.IsHeroDamage && (int)hitInstance.AttackType == 16) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * DamageMultiplier); } } } public static class BeastRageBindFsmPatch { private sealed class BindFsmMarker : MonoBehaviour { } private sealed class SkipWarriorKickupAction : FsmStateAction { public override void OnEnter() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) try { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null && IsActive() && instance.WarriorState.IsInRageMode) { ((FsmStateAction)this).Fsm.Event("FINISHED"); } } catch { } ((FsmStateAction)this).Finish(); } } private sealed class SuppressDazzleAction : FsmStateAction { public override void OnEnter() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) try { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null && IsActive() && instance.WarriorState.IsInRageMode) { FsmBool val = ((FsmStateAction)this).Fsm.Variables.FindFsmBool("Is Dazzle Equipped"); if (val != null) { val.Value = false; } } } catch { } ((FsmStateAction)this).Finish(); } } private static ManualLogSource? _log; private static bool _warned; public static bool Enabled { get; set; } public static Func<bool> IsActive { get; set; } = () => true; public static void Configure(ManualLogSource log) { _log = log; _warned = false; } public static void Postfix(PlayMakerFSM __instance) { if (!Enabled || (Object)(object)__instance == (Object)null || __instance.Fsm == null || __instance.FsmName != "Bind" || (Object)(object)((Component)__instance).gameObject == (Object)null || !((Object)((Component)__instance).gameObject).name.StartsWith("Hero_Hornet", StringComparison.Ordinal) || (Object)(object)((Component)__instance).GetComponent<BindFsmMarker>() != (Object)null) { return; } try { FsmState state = FsmUtil.GetState(__instance, "Warrior Kickup"); FsmState state2 = FsmUtil.GetState(__instance, "Dazzle?"); if (state == null || state2 == null) { WarnOnce(new InvalidOperationException($"Bind FSM states missing: Warrior Kickup={state != null}, Dazzle?={state2 != null}")); return; } FsmUtil.InsertAction(state, 0, (FsmStateAction)(object)new SkipWarriorKickupAction()); FsmUtil.InsertAction(state2, 0, (FsmStateAction)(object)new SuppressDazzleAction()); ((Component)__instance).gameObject.AddComponent<BindFsmMarker>(); ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)"[BeastRageBind] BEA-005 hooks installed: skip Warrior Kickup + suppress Dazzle? while in Rage mode."); } } catch (Exception ex) { WarnOnce(ex); } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[BeastRageBind] BEA-005 FSM hook disabled: " + ex.GetType().Name + ": " + ex.Message)); } } } } public static class BeastRageDamageProtectPatch { private static readonly FieldInfo _warriorStateField = AccessTools.Field(typeof(HeroController), "warriorState"); private static ManualLogSource? _log; private static bool _warned; private static float _savedEffect; private static float _savedHeal; private static bool _saved; public static Func<bool> IsActive { get; set; } = () => false; public static void Configure(ManualLogSource log) { _log = log; } public static void Prefix(HeroController __instance) { //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_0032: 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_0047: Unknown result type (might be due to invalid IL or missing references) _saved = false; if (!IsActive() || _warriorStateField == null) { return; } try { WarriorCrestStateInfo val = (WarriorCrestStateInfo)_warriorStateField.GetValue(__instance); if (val.IsInRageMode) { _savedEffect = val.RageEffectTimeLeft; _savedHeal = val.RageHealTimeLeft; _saved = true; } } catch (Exception ex) { WarnOnce(ex); } } public static void Postfix(HeroController __instance) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (!_saved || _warriorStateField == null) { return; } try { WarriorCrestStateInfo val = (WarriorCrestStateInfo)_warriorStateField.GetValue(__instance); val.RageEffectTimeLeft = _savedEffect; val.RageHealTimeLeft = _savedHeal; _warriorStateField.SetValue(__instance, val); } catch (Exception ex) { WarnOnce(ex); } finally { _saved = false; } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[BeastRageProtect] " + ex.GetType().Name + ": " + ex.Message)); } } } } public static class BindCostPatch { public static bool Prefix(ref float __result) { if (CloaklessRuneBridge.IsFreeBindCharged()) { __result = 0f; return false; } if (CursedRuneBridge.IsBindEnabled()) { __result = 9f; return false; } return true; } } public static class CursedBindInvulnerabilityPatch { private sealed class CursedBindFsmMarker : MonoBehaviour { } private sealed class AcquireBindInvulnerabilityAction : FsmStateAction { public override void OnEnter() { try { if (!CursedRuneBridge.IsBindInvulnerabilityActive()) { ((FsmStateAction)this).Finish(); return; } if (CursedRuneBridge.BindInvulnerabilitySeconds() <= 0.0) { ((FsmStateAction)this).Finish(); return; } _lease?.Dispose(); _lease = CursedRuneBridge.AcquireBindInvulnerability(); } catch (Exception ex) { WarnOnce(ex); } ((FsmStateAction)this).Finish(); } } private static readonly string[] BindStates = new string[4] { "Binding", "Cursed Bind Start", "Cursed Bind Mid", "Cursed Bind Wait" }; private static ManualLogSource? _log; private static bool _warned; private static IDisposable? _lease; public static void Configure(ManualLogSource log) { _log = log; _warned = false; } public static void OnFsmStart(PlayMakerFSM __instance) { if ((Object)(object)__instance == (Object)null || __instance.Fsm == null || __instance.FsmName != "Spell Control" || (Object)(object)((Component)__instance).gameObject == (Object)null || !((Object)((Component)__instance).gameObject).name.StartsWith("Hero_Hornet", StringComparison.Ordinal) || (Object)(object)((Component)__instance).GetComponent<CursedBindFsmMarker>() != (Object)null) { return; } try { int num = 0; string[] bindStates = BindStates; foreach (string text in bindStates) { FsmState state = FsmUtil.GetState(__instance, text); if (state != null) { FsmUtil.InsertAction(state, 0, (FsmStateAction)(object)new AcquireBindInvulnerabilityAction()); num++; } } if (num == 0) { WarnOnce(new InvalidOperationException("Spell Control bind states not found; bind invulnerability disabled.")); return; } ((Component)__instance).gameObject.AddComponent<CursedBindFsmMarker>(); ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)$"[CursedBind] bind-process invulnerability hooks installed ({num} states)."); } } catch (Exception ex) { WarnOnce(ex); } } public static void Reset() { try { _lease?.Dispose(); } catch (Exception ex) { WarnOnce(ex); } _lease = null; } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[CursedBind] disabled: " + ex.GetType().Name + ": " + ex.Message)); } } } } public static class DashSignalPatch { public static Action? DashStarted { get; set; } public static void Postfix() { DashStarted?.Invoke(); } } public static class GlacialSlowPatch { private sealed class EnemySlowDriver : MonoBehaviour { private sealed record WalkerEntry(Walker Walker, float Left, float Right); private sealed record WalkerV2Entry(WalkerV2 Walker, float Walk, float Run); private readonly List<WalkerEntry> _walkers = new List<WalkerEntry>(); private readonly List<WalkerV2Entry> _walkersV2 = new List<WalkerV2Entry>(); private Rigidbody2D[] _bodies = Array.Empty<Rigidbody2D>(); private float _factor = 0.5f; private float _remaining = 10f; public void Initialize(float factor, float duration) { _factor = Mathf.Clamp(factor, 0.05f, 1f); _remaining = Mathf.Max(0.1f, duration); Capture(); } public void Refresh(float factor, float duration) { _factor = Mathf.Clamp(factor, 0.05f, 1f); _remaining = Mathf.Max(0.1f, duration); } private void Capture() { Walker[] componentsInChildren = ((Component)this).GetComponentsInChildren<Walker>(true); foreach (Walker val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { _walkers.Add(new WalkerEntry(val, val.walkSpeedL, val.walkSpeedR)); val.walkSpeedL *= _factor; val.walkSpeedR *= _factor; } } WalkerV2[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<WalkerV2>(true); foreach (WalkerV2 val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null)) { _walkersV2.Add(new WalkerV2Entry(val2, val2.WalkSpeed, val2.RunSpeed)); val2.WalkSpeed *= _factor; val2.RunSpeed *= _factor; } } _bodies = ((Component)this).GetComponentsInChildren<Rigidbody2D>(true); } private void Update() { _remaining -= Time.deltaTime; if (_remaining <= 0f) { Object.Destroy((Object)(object)this); } } private void FixedUpdate() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (_factor >= 1f) { return; } Rigidbody2D[] bodies = _bodies; foreach (Rigidbody2D val in bodies) { if (!((Object)(object)val == (Object)null)) { val.linearVelocity *= _factor; val.angularVelocity *= _factor; } } } private void OnDestroy() { foreach (WalkerEntry walker in _walkers) { if ((Object)(object)walker.Walker != (Object)null) { walker.Walker.walkSpeedL = walker.Left; walker.Walker.walkSpeedR = walker.Right; } } foreach (WalkerV2Entry item in _walkersV2) { if ((Object)(object)item.Walker != (Object)null) { item.Walker.WalkSpeed = item.Walk; item.Walker.RunSpeed = item.Run; } } } } public static double SlowDurationSeconds { get; set; } = 10.0; public static double SlowFactor { get; set; } = 0.5; public static void Prefix(HealthManager __instance, HitInstance hitInstance) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null) && !((Object)(object)((Component)__instance).gameObject == (Object)null) && hitInstance.IsHeroDamage && CloaklessRuneBridge.TryConsumeGlacialAugment()) { ApplySlow(((Component)__instance).gameObject); } } private static void ApplySlow(GameObject enemy) { if (!((Object)(object)enemy == (Object)null)) { float factor = (float)SlowFactor; float duration = (float)SlowDurationSeconds; EnemySlowDriver component = enemy.GetComponent<EnemySlowDriver>(); if ((Object)(object)component != (Object)null) { component.Refresh(factor, duration); } else { enemy.AddComponent<EnemySlowDriver>().Initialize(factor, duration); } } } } public static class HailOfBladesCritPatch { public static void Prefix(ref HitInstance hitInstance) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (ReaperRuneBridge.IsHailOfBladesFullLayers() && hitInstance.IsHeroDamage && !hitInstance.CriticalHit && (int)hitInstance.AttackType == 0) { hitInstance.CriticalHit = true; } } } public static class HunterConquerorDamagePatch { public static Func<double>? Multiplier; public static Func<bool>? IsEnhanced; public static void Prefix(ref HitInstance hitInstance) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) double multiplier = Multiplier?.Invoke() ?? 1.0; bool enhanced = IsEnhanced?.Invoke() ?? false; hitInstance.DamageDealt = ApplyMultiplier(hitInstance.DamageDealt, multiplier, enhanced, hitInstance.IsHeroDamage, hitInstance.AttackType); } public static int ApplyMultiplier(int damage, double multiplier, bool enhanced, bool isHeroDamage, AttackTypes attackType) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 if (multiplier <= 1.0) { return damage; } if (!enhanced || !isHeroDamage) { return damage; } if ((int)attackType == 2) { return damage; } return (int)Math.Round((double)damage * multiplier); } } public static class HunterVanillaComboPreserver { public static Func<int>? LayerCount { get; set; } public static bool Prefix() { return (LayerCount?.Invoke() ?? 0) <= 0; } } public static class NailAttackRangePatch { public static bool Enabled { get; set; } = true; public static float RangeMultiplier { get; set; } = 2f; public static void Postfix(NailAttackBase __instance) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (Enabled && !(RangeMultiplier <= 0f) && WandererRuneBridge.IsActive()) { WandererEnhancementState current = WandererEnhancementState.Current; if (current != null && current.IsEnhanced) { Vector3 localScale = ((Component)__instance).transform.localScale; ((Component)__instance).transform.localScale = new Vector3(localScale.x * RangeMultiplier, localScale.y, localScale.z); } } } } public static class ParrySignalPatch { public static Action? Parried; public static void Postfix() { Parried?.Invoke(); } } public static class ReaperChargeSlashDamagePatch { public static double DamageMultiplier { get; set; } = 1.5; public static void Prefix(ref HitInstance hitInstance) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 if (!(DamageMultiplier <= 1.0) && ReaperRuneBridge.IsChargeSlashEnhanced() && hitInstance.IsHeroDamage && (int)hitInstance.AttackType == 16) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * DamageMultiplier); } } } public static class ReaperChargeSlashScalePatch { private sealed class ChargeSlashScaleMarker : MonoBehaviour { public Vector3 Original; } public static Func<bool> IsChargeSlashEnhanced { get; set; } = () => false; public static float ScaleMultiplier { get; set; } = 1.5f; public static void Postfix(NailAttackBase __instance) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)__instance).transform; if (!((Object)(object)transform == (Object)null) && ((Object)transform).name.IndexOf("Charge Slash", StringComparison.OrdinalIgnoreCase) >= 0) { ChargeSlashScaleMarker chargeSlashScaleMarker = ((Component)transform).GetComponent<ChargeSlashScaleMarker>(); if ((Object)(object)chargeSlashScaleMarker == (Object)null) { chargeSlashScaleMarker = ((Component)transform).gameObject.AddComponent<ChargeSlashScaleMarker>(); chargeSlashScaleMarker.Original = transform.localScale; } transform.localScale = (Vector3)(IsChargeSlashEnhanced() ? new Vector3(chargeSlashScaleMarker.Original.x * ScaleMultiplier, chargeSlashScaleMarker.Original.y * ScaleMultiplier, chargeSlashScaleMarker.Original.z) : chargeSlashScaleMarker.Original); } } } public static class ReaperFireBladeDamagePatch { public static void Prefix(ref HitInstance hitInstance) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if (!ReaperFireBladeBridge.IsActive()) { return; } double num = ReaperFireBladeBridge.DamageMultiplier(); if (!(num <= 1.0) && hitInstance.IsHeroDamage) { AttackTypes attackType = hitInstance.AttackType; if ((int)attackType <= 1 || (int)attackType == 6 || (int)attackType == 16) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * num); } } } } public static class ReaperSilkBundleAutoCollect { private sealed class SilkOrbMarker : MonoBehaviour { public PlayMakerFSM? Fsm; private void OnDestroy() { lock (_markers) { _markers.Remove(this); } } } private const string FsmName = "Control"; private const string GetSilkState = "Get Silk"; private const string CollectableState = "Collectable"; private const string CollectState = "Collect"; private const string DisappearEvent = "DISAPPEAR"; private static readonly HashSet<SilkOrbMarker> _markers = new HashSet<SilkOrbMarker>(); private static bool _failed; private static bool _warned; public static bool Enabled { get; set; } public static void Postfix(GameObject obj, GameObject prefab) { if (!Enabled || _failed || (Object)(object)obj == (Object)null || (Object)(object)prefab == (Object)null || ((Object)prefab).name != "Reaper Silk Bundle" || (Object)(object)obj.GetComponent<SilkOrbMarker>() != (Object)null) { return; } PlayMakerFSM val = ((IEnumerable<PlayMakerFSM>)obj.GetComponents<PlayMakerFSM>()).FirstOrDefault((Func<PlayMakerFSM, bool>)((PlayMakerFSM f) => (Object)(object)f != (Object)null && f.Fsm != null && f.Fsm.Name == "Control")); if ((Object)(object)val == (Object)null) { Fail("Control FSM not found on Reaper Silk Bundle"); return; } try { Fsm fsm = val.Fsm; FsmState state = fsm.GetState("Get Silk"); FsmState state2 = fsm.GetState("Collectable"); FsmState state3 = fsm.GetState("Collect"); if (state == null || state2 == null || state3 == null) { Fail("one of states Get Silk/Collectable/Collect missing"); return; } DisableCameraShake(state); RetargetDisappear(state2, state3); SilkOrbMarker silkOrbMarker = obj.AddComponent<SilkOrbMarker>(); silkOrbMarker.Fsm = val; lock (_markers) { _markers.Add(silkOrbMarker); } } catch (Exception ex) { Fail(ex.Message); } } public static void NotifyBindCompleted() { if (!Enabled || _failed) { return; } SilkOrbMarker[] array; lock (_markers) { array = _markers.ToArray(); } SilkOrbMarker[] array2 = array; foreach (SilkOrbMarker silkOrbMarker in array2) { if (!((Object)(object)silkOrbMarker == (Object)null) && !((Object)(object)silkOrbMarker.Fsm == (Object)null) && !((Object)(object)((Component)silkOrbMarker).gameObject == (Object)null)) { try { silkOrbMarker.Fsm.SetState("Collect"); } catch (Exception ex) { WarnOnce(ex); } } } } private static void DisableCameraShake(FsmState getSilk) { FsmStateAction val = ((IEnumerable<FsmStateAction>)getSilk.Actions).FirstOrDefault((Func<FsmStateAction, bool>)((FsmStateAction a) => a != null && ((object)a).GetType().Name.IndexOf("Shake", StringComparison.OrdinalIgnoreCase) >= 0)); if (val != null) { val.Enabled = false; } else if (getSilk.Actions.Length > 2) { getSilk.Actions[2].Enabled = false; } } private static void RetargetDisappear(FsmState collectable, FsmState collect) { FsmTransition val = ((IEnumerable<FsmTransition>)collectable.Transitions).FirstOrDefault((Func<FsmTransition, bool>)((FsmTransition t) => t != null && t.EventName == "DISAPPEAR")); if (val != null) { val.ToFsmState = collect; } } private static void Fail(string reason) { _failed = true; Enabled = false; WarnOnce(new InvalidOperationException(reason)); } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; Debug.LogWarning((object)("[ReaperSilkBundle] " + ex.GetType().Name + ": " + ex.Message)); } } } public static class ReaperSilkDropProbabilityPatch { private static FieldInfo? _dropsField; private static FieldInfo? _probabilityField; private static bool _checked; private static bool _available; private static bool _warned; private static float[]? _saved; public static bool Enabled { get; set; } public static void Prefix(HeroController __instance) { if (!Enabled || !ReaperRuneBridge.IsDropRateWindowActive() || !EnsureAvailable()) { return; } try { if (!(_dropsField.GetValue(__instance) is Array { Length: >=3 } array)) { return; } _saved = new float[3]; for (int i = 0; i < 3; i++) { object value = array.GetValue(i); if (value == null) { _saved = null; break; } _saved[i] = Convert.ToSingle(_probabilityField.GetValue(value)); _probabilityField.SetValue(value, i switch { 1 => 0.5f, 0 => 0.15f, _ => 0.35f, }); } } catch (Exception ex) { _saved = null; WarnOnce(ex); } } public static void Restore() { if (_saved == null) { return; } try { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null || !(_dropsField.GetValue(instance) is Array { Length: >=3 } array)) { return; } for (int i = 0; i < 3; i++) { object value = array.GetValue(i); if (value != null) { _probabilityField.SetValue(value, _saved[i]); } } } catch (Exception ex) { WarnOnce(ex); } finally { _saved = null; } } private static bool EnsureAvailable() { if (_checked) { return _available; } _checked = true; try { _dropsField = AccessTools.Field(typeof(HeroController), "reaperBundleDrops"); if (_dropsField == null || _dropsField.FieldType == null || !_dropsField.FieldType.IsArray) { WarnOnce(new InvalidOperationException("reaperBundleDrops field not found or not an array")); return false; } _probabilityField = AccessTools.Field(_dropsField.FieldType.GetElementType(), "Probability"); if (_probabilityField == null) { WarnOnce(new InvalidOperationException("Probability field not found on bundle drop element")); return false; } _available = true; } catch (Exception ex) { WarnOnce(ex); } return _available; } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; Debug.LogWarning((object)("[ReaperSilkDrop] " + ex.GetType().Name + ": " + ex.Message)); } } } public static class ReaperSubBladeAttackSpeedPatch { private static bool _probeLogged; public static double Bonus { get; set; } = 0.3; public static void Postfix(ref float __result) { if (ReaperRuneBridge.IsSubBladeCharged()) { float num = __result; __result = (float)((double)__result * AttackCycleSpeed.TimeFactor(1, Bonus)); if (!_probeLogged) { _probeLogged = true; Debug.LogWarning((object)($"[HailOfBlades][probe] attack-speed getter scaled {num} -> {__result} " + $"(bonus={Bonus}, charged={ReaperRuneBridge.SubBladeChargedAttacksRemaining()}).")); } } } } public static class UnsealedSpellbookFreeBindPatch { private static bool _probeLogged; public static void Prefix(HeroController __instance, ref int amount, SilkTakeSource source) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (amount < 9 || (int)source != 0) { return; } HeroController val = (HeroController)(((Object)(object)__instance != (Object)null) ? ((object)__instance) : ((object)HeroController.instance)); if (!((Object)(object)val == (Object)null) && val.cState != null && (val.cState.isBinding || val.cState.focusing) && CloaklessRuneBridge.TryConsumeFreeBind()) { amount = 0; if (!_probeLogged) { _probeLogged = true; Debug.LogWarning((object)"[UnsealedSpellbook][probe] free bind consumed; silk cost set to 0."); } } } } public static class WandererAttackSpeedPatch { public static double PerLayer { get; set; } = 0.06; public static void Postfix(ref float __result) { if (WandererRuneBridge.IsActive()) { int num = WandererRuneBridge.LayerCount(); if (num > 0) { __result = (float)((double)__result * AttackCycleSpeed.TimeFactor(num, PerLayer)); } } } } public static class WandererCritChancePatch { public static double CritBonus { get; set; } = 0.02; public static void Postfix(ref float __result) { if (!(CritBonus <= 0.0) && WandererRuneBridge.IsActive() && WandererRuneBridge.IsFullLayers()) { __result += (float)CritBonus; } } } public static class WandererLuckyPatch { public static void Postfix(HeroController __instance, ref bool __result) { if (!__result && WandererRuneBridge.IsActive() && WandererRuneBridge.LayerCount() > 0 && !__instance.cState.isMaggoted && ((ToolBase)Gameplay.WandererCrest).IsEquipped) { __result = true; } } } public static class WandererParryCritPatch { public static void Postfix(HeroController __instance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (WandererRuneBridge.IsActive()) { WandererCrestStateInfo wandererState = __instance.WandererState; if (!wandererState.QueuedNextHitCritical) { wandererState.QueuedNextHitCritical = true; __instance.WandererState = wandererState; } } } } public static class WitchAftershockDamagePatch { public const double DamageMultiplier = 1.5; public static void Prefix(ref HitInstance hitInstance) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 if (WitchRuneBridge.AftershockLayers() <= 0 || !hitInstance.IsHeroDamage) { return; } if ((int)hitInstance.AttackType == 6) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * 1.5); } else if ((int)hitInstance.AttackType == 0 || (int)hitInstance.AttackType == 1) { string text = (((Object)(object)hitInstance.Source != (Object)null) ? ((Object)hitInstance.Source).name : string.Empty); if (text.IndexOf("Down", StringComparison.OrdinalIgnoreCase) >= 0 && (text.IndexOf("Slash", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Spike", StringComparison.OrdinalIgnoreCase) >= 0)) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * 1.5); } } } } public static class WitchDashInvulnPatch { public static void Postfix() { if (WitchRuneBridge.AftershockLayers() > 0) { WitchRuneBridge.AcquireDashInvulnerability?.Invoke(); } } } public static class WitchGraspAttackSpeedPatch { public static void Postfix(ref float __result) { if (WitchRuneBridge.IsGraspCharged()) { __result = (float)((double)__result * AttackCycleSpeed.TimeFactor(1, 0.3)); } } } public static class WitchGraspDamagePatch { public const double DamageMultiplier = 1.3; public static void Prefix(ref HitInstance hitInstance) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 if (WitchRuneBridge.IsGraspCharged() && hitInstance.IsHeroDamage && ((int)hitInstance.AttackType == 0 || (int)hitInstance.AttackType == 1)) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * 1.3); } } } public static class WitchGraspDownslashDamagePatch { public const double DamageMultiplier = 1.5; public static void Prefix(ref HitInstance hitInstance) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 if (WitchRuneBridge.GraspLayers() > 0 && hitInstance.IsHeroDamage && ((int)hitInstance.AttackType == 0 || (int)hitInstance.AttackType == 1)) { string text = (((Object)(object)hitInstance.Source != (Object)null) ? ((Object)hitInstance.Source).name : string.Empty); if (text.IndexOf("Down", StringComparison.OrdinalIgnoreCase) >= 0 && (text.IndexOf("Slash", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Spike", StringComparison.OrdinalIgnoreCase) >= 0)) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * 1.5); } } } } public static class WitchGraspRangePatch { public const float RangeMultiplier = 1.3f; public static void Postfix(NailAttackBase __instance) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (WitchRuneBridge.IsGraspCharged()) { Vector3 localScale = ((Component)__instance).transform.localScale; ((Component)__instance).transform.localScale = new Vector3(localScale.x * 1.3f, localScale.y, localScale.z); } } } public static class WitchTakeSilkCostPatch { public const int ReducedCost = 5; private static bool _probeLogged; public static void Prefix(ref int amount, SilkTakeSource source) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (WitchRuneBridge.IsBindChainActive() && WitchRuneBridge.IsGraspActive() && amount >= 9) { if (!_probeLogged) { _probeLogged = true; Debug.LogWarning((object)$"[WitchBind][probe] TakeSilk reduced 9 -> {5} while bind chain active (source={source})."); } amount = 5; } } } } namespace CrestsEvolve.Features { public sealed class ArchitectCrest : ICrestRuneSet, IDisposable { private readonly bool _enabled; public string Key => "architect"; public CrestGate Gate { get; } public IReadOnlyList<IRuneEffect> Runes { get; } public IReadOnlyList<string> RuneKeys { get; } public FirstStrikeRune FirstStrike { get; } public ElectrocuteRune Electrocute { get; } public bool IsToolRepairActive => Gate.IsActive; public bool IsToolTransformActive => Gate.IsActive; public ArchitectCrest(bool enabled, FirstStrikeRune firstStrike, ElectrocuteRune electrocute) { _enabled = enabled; Gate = new CrestGate("architect", RuneCrestIds.Architect, enabled); FirstStrike = firstStrike ?? throw new ArgumentNullException("firstStrike"); Electrocute = electrocute ?? throw new ArgumentNullException("electrocute"); Runes = new IRuneEffect[2] { FirstStrike, Electrocute }; RuneKeys = new string[2] { FirstStrike.Key, Electrocute.Key }; } public void Start(string currentCrestId) { if (_enabled) { FirstStrike.Start(currentCrestId); Electrocute.Start(currentCrestId); } } public void Tick(float deltaSeconds) { if (_enabled) { FirstStrike.Tick(deltaSeconds); Electrocute.Tick(deltaSeconds); } } public void Dispose() { FirstStrike.Dispose(); Electrocute.Dispose(); } } public sealed class BeastCrest : ICrestRuneSet, IDisposable { private readonly bool _enabled; public string Key => "beast"; public CrestGate Gate { get; } public IReadOnlyList<IRuneEffect> Runes { get; } public IReadOnlyList<string> RuneKeys { get; } public DarkHarvestRune DarkHarvest { get; } public PredatorRune Predator { get; } public bool IsRedBloodTintActive => Gate.IsActive; public bool IsRageProtectionActive => Gate.IsActive; public bool IsBindNoPrepActive => Gate.IsActive; public BeastCrest(bool enabled, DarkHarvestRune darkHarvest, PredatorRune predator) { _enabled = enabled; Gate = new CrestGate("beast", RuneCrestIds.Beast, enabled); DarkHarvest = darkHarvest ?? throw new ArgumentNullException("darkHarvest"); Predator = predator ?? throw new ArgumentNullException("predator"); Runes = new IRuneEffect[2] { DarkHarvest, Predator }; RuneKeys = new string[2] { DarkHarvest.Key, Predator.Key }; } public void Start(string currentCrestId) { if (_enabled) { DarkHarvest.Start(currentCrestId); Predator.Start(currentCrestId); } } public void Tick(float deltaSeconds) { if (_enabled) { DarkHarvest.Tick(deltaSeconds); Predator.Tick(deltaSeconds); } } public void Dispose() { DarkHarvest.Dispose(); Predator.Dispose(); } } public enum BlueHealthTintKind { None, Red, Green } public static class BlueHealthTintResolver { public static BlueHealthTintKind Resolve(bool beastTintActive, bool witchTintActive, bool cursedTintActive, bool isPoison) { if (isPoison) { return BlueHealthTintKind.None; } if (beastTintActive) { return BlueHealthTintKind.Red; } if (witchTintActive || cursedTintActive) { return BlueHealthTintKind.Green; } return BlueHealthTintKind.None; } } public sealed class CloaklessCrest : ICrestRuneSet, IDisposable { private readonly bool _enabled; private readonly IRuneEffect? _extraRune; public string Key => "cloakless"; public CrestGate Gate { get; } public IReadOnlyList<IRuneEffect> Runes { get; } public IReadOnlyList<string> RuneKeys { get; } public GlacialAugmentRune GlacialAugment { get; } public UnsealedSpellbookRune UnsealedSpellbook { get; } public IRuneEffect? RandomPassive => _extraRune; public string? RandomPassiveKey => _extraRune?.Key; public CloaklessCrest(bool enabled, GlacialAugmentRune glacialAugment, UnsealedSpellbookRune unsealedSpellbook, bool randomPassiveEnabled, IReadOnlyList<IRuneEffect>? randomPool, Random? random = null, string? pinnedPassiveKey = null) { _enabled = enabled; Gate = new CrestGate("cloakless", RuneCrestIds.Cloakless, enabled); GlacialAugment = glacialAugment ?? throw new ArgumentNullException("glacialAugment"); UnsealedSpellbook = unsealedSpellbook ?? throw new ArgumentNullException("unsealedSpellbook"); List<IRuneEffect> list = new List<IRuneEffect> { GlacialAugment, UnsealedSpellbook }; List<string> list2 = new List<string> { GlacialAugment.Key, UnsealedSpellbook.Key }; if (randomPassiveEnabled && randomPool != null && randomPool.Count > 0) { List<IRuneEffect> list3 = new List<IRuneEffect>(); foreach (IRuneEffect item in randomPool) { if (item != null && !string.Equals(item.Key, GlacialAugment.Key, StringComparison.Ordinal) && !string.Equals(item.Key, UnsealedSpellbook.Key, StringComparison.Ordinal)) { list3.Add(item); } } if (list3.Count > 0) { _extraRune = (string.IsNullOrWhiteSpace(pinnedPassiveKey) ? null : list3.FirstOrDefault((IRuneEffect r) => string.Equals(r.Key, pinnedPassiveKey, StringComparison.OrdinalIgnoreCase))) ?? list3[(random ?? new Random()).Next(list3.Count)]; list.Add(_extraRune); list2.Add(_extraRune.Key); } } Runes = list.ToArray(); RuneKeys = list2.ToArray(); } public void Start(string currentCrestId) { if (_enabled) { GlacialAugment.Start(currentCrestId); UnsealedSpellbook.Start(currentCrestId); } } public void Tick(float deltaSeconds) { if (_enabled) { GlacialAugment.Tick(deltaSeconds); UnsealedSpellbook.Tick(deltaSeconds); } } public void Dispose() { GlacialAugment.Dispose(); UnsealedSpellbook.Dispose(); } } public sealed class CursedCrest : ICrestRuneSet, IDisposable { private readonly bool _enabled; public string Key => "cursed"; public CrestGate Gate { get; } public IReadOnlyList<IRuneEffect> Runes { get; } public IReadOnlyList<string> RuneKeys { get; } public AftershockRune Aftershock { get; } public GuardianRune Guardian { get; } public bool IsBindInvulnerabilityActive => Gate.IsActive; public bool IsBindEnabled => Gate.IsActive; public bool IsGreenBloodTintActive => Gate.IsActive; public CursedCrest(bool enabled, AftershockRune aftershock, GuardianRune guardian) { _enabled = enabled; Gate = new CrestGate("cursed", RuneCrestIds.Cursed, enabled); Aftershock = aftershock ?? throw new ArgumentNullException("aftershock"); Guardian = guardian ?? throw new ArgumentNullException("guardian"); Runes = new IRuneEffect[2] { Aftershock, Guardian }; RuneKeys = new string[2] { Aftershock.Key, Guardian.Key }; } public void Start(string currentCrestId) { if (_enabled) { Aftershock.Start(currentCrestId); Guardian.Start(currentCrestId); } } public void Tick(float deltaSeconds) { if (_enabled) { Aftershock.Tick(deltaSeconds); Guardian.Tick(deltaSeconds); } } public void Dispose() { Aftershock.Dispose(); Guardian.Dispose(); } } public sealed class HunterCrest : ICrestRuneSet, IDisposable { private readonly bool _enabled; public string Key => "hunter"; public CrestGate Gate { get; } public IReadOnlyList<IRuneEffect> Runes { get; } public IReadOnlyList<string> RuneKeys { get; } public ConquerorRune Conqueror { get; } public int VanillaFocusProtectionLayers => Conqueror.LayerCount; public HunterCrest(bool enabled, ConquerorRune conqueror) { _enabled = enabled; Gate = new CrestGate("hunter", RuneCrestIds.Hunter, enabled); Conqueror = conqueror ?? throw new ArgumentNullException("conqueror"); Runes = new IRuneEffect[1] { Conqueror }; RuneKeys = new string[1] { Conqueror.Key }; } public void Start(string currentCrestId) { if (_enabled) { Conqueror.Start(currentCrestId); } } public void Tick(float deltaSeconds) { if (_enabled) { Conqueror.Tick(deltaSeconds); } } public void Dispose() { Conqueror.Dispose(); } } public sealed class ReaperCrest : ICrestRuneSet, IDisposable { private readonly bool _enabled; public string Key => "reaper"; public CrestGate Gate { get; } public IReadOnlyList<IRuneEffect> Runes { get; } public IReadOnlyList<string> RuneKeys { get; } public PressTheAttackRune PressTheAttack { get; } public HailOfBladesRune HailOfBlades { get; } public bool IsDropRateOverrideActive => PressTheAttack.IsDropRateOverrideActive; public bool IsAutoCollectEnabled => PressTheAttack.IsAutoCollectEnabled; public ReaperCrest(bool enabled, PressTheAttackRune pressTheAttack, HailOfBladesRune hailOfBlades) { _enabled = enabled; Gate = new CrestGate("reaper", RuneCrestIds.Reaper, enabled); PressTheAttack = pressTheAttack ?? throw new ArgumentNullException("pressTheAttack"); HailOfBlades = hailOfBlades ?? throw new ArgumentNullException("hailOfBlades"); Runes = new IRuneEffect[2] { PressTheAttack, HailOfBlades }; RuneKeys = new string[2] { PressTheAttack.Key, HailOfBlades.Key }; } public void Start(string currentCrestId) { if (_enabled) { PressTheAttack.Start(currentCrestId); HailOfBlades.Start(currentCrestId); } } public void Tick(float deltaSeconds) { if (_enabled) { PressTheAttack.Tick(deltaSeconds); HailOfBlades.Tick(deltaSeconds); } } public void Dispose() { PressTheAttack.Dispose(); HailOfBlades.Dispose(); } } public sealed class ShamanCrest : ICrestRuneSet, IDisposable { private readonly bool _enabled; public string Key => "shaman"; public CrestGate Gate { get; } public IReadOnlyList<IRuneEffect> Runes { get; } public IReadOnlyList<string> RuneKeys { get; } public ArcaneCometRune ArcaneComet { get; } public PhaseRushRune PhaseRush { get; } public ShamanCrest(bool enabled, ArcaneCometRune arcaneComet, PhaseRushRune phaseRush) { _enabled = enabled; Gate = new CrestGate("shaman", RuneCrestIds.Shaman, enabled); ArcaneComet = arcaneComet ?? throw new ArgumentNullException("arcaneComet"); PhaseRush = phaseRush ?? throw new ArgumentNullException("phaseRush"); Runes = new IRuneEffect[2] { ArcaneComet, PhaseRush }; RuneKeys = new string[2] { ArcaneComet.Key, PhaseRush.Key }; } public void Start(string currentCrestId) { if (_enabled) { ArcaneComet.Start(currentCrestId); PhaseRush.Start(currentCrestId); } } public void Tick(float deltaSeconds) { if (_enabled) { ArcaneComet.Tick(deltaSeconds); PhaseRush.Tick(deltaSeconds); } } public void Dispose() { ArcaneComet.Dispose(); PhaseRush.Dispose(); } } public sealed class WandererCrest : ICrestRuneSet, IDisposable { private readonly bool _enabled; public string Key => "wanderer"; public CrestGate Gate { get; } public IReadOnlyList<IRuneEffect> Runes { get; } public IReadOnlyList<string> RuneKeys { get; } public LethalTempoRune LethalTempo { get; } public double FullLayerCritBonus { get; } public bool IsFullLayers => LethalTempo.IsFullLayers; public WandererCrest(bool enabled, LethalTempoRune lethalTempo, double fullLayerCritBonus) { _enabled = enabled; Gate = new CrestGate("wanderer", RuneCrestIds.Wanderer, enabled); LethalTempo = lethalTempo ?? throw new ArgumentNullException("lethalTempo"); FullLayerCritBonus = Math.Max(0.0, fullLayerCritBonus); Runes = new IRuneEffect[1] { LethalTempo }; RuneKeys = new string[1] { LethalTempo.Key }; } public void Start(string currentCrestId) { if (_enabled) { LethalTempo.Start(currentCrestId); } } public void Tick(float deltaSeconds) { if (_enabled) { LethalTempo.Tick(deltaSeconds); } } public void Dispose() { LethalTempo.Dispose(); } } public sealed class WitchCrest : ICrestRuneSet, IDisposable { public sealed class WitchBindChain : IDisposable { public const double ChainDurationSeconds = 3.0; public const double RefundDelaySeconds = 0.8; public const int RefundSilk = 5; public const int ChainCostSilk = 5; private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly IClock _clock; private readonly Func<string, HealSourceKind> _classify; private readonly Action<int> _addSilk; private bool _started; private bool _active; private bool _chainActive; private double _chainExpiresAt; private double? _refundAt; private bool _bindHealSinceBind; public CrestGate Gate { get; } public bool IsChainActive { get { if (_started && _active) { return _chainActive; } return false; } } public WitchBindChain(ManualLogSource log, GameEventHub events, IClock clock, Func<string, HealSourceKind> classify, Action<int> addSilk, CrestGate gate) { _log = log; _events = events; _clock = clock; _classify = classify; _addSilk = addSilk; Gate = gate ?? throw new ArgumentNullException("gate"); Gate.ActiveChanged += OnGateChanged; } public void Start(string currentCrestId) { if (!_started) { _started = true; _events.HealResolved += OnHealResolved; _events.BindCompleted += OnBindCompleted; _events.BenchRested += OnLifecycleReset; _events.PlayerDied += OnLifecycleReset; _events.SceneChanged += OnSceneChanged; OnGateChanged(Gate.IsActive); } } public void Tick() { if (!_started) { return; } if (_chainActive && _clock.Now >= _chainExpiresAt) { _chainActive = false; _log.LogDebug((object)"[WitchBind] bind chain expired."); } if (_refundAt.HasValue && _clock.Now >= _refundAt.Value && !_bindHealSinceBind) { _refundAt = null; if (_active) { _addSilk(5); _log.LogDebug((object)$"[WitchBind] bind missed; refunded {5} silk."); } } } public void Dispose() { _started = false; _events.HealResolved -= OnHealResolved; _events.BindCompleted -= OnBindCompleted; _events.BenchRested -= OnLifecycleReset; _events.PlayerDied -= OnLifecycleReset; _events.SceneChanged -= OnSceneChanged; Gate.ActiveChanged -= OnGateChanged; Reset(); } private void OnHealResolved(HealEvent e) { if (_active && _classify(e.SourceTag) == HealSourceKind.Bind) { _bindHealSinceBind = true; _refundAt = null; _chainActive = true; _chainExpiresAt = _clock.Now + 3.0; _log.LogDebug((object)"[WitchBind] bind healed; chain active, next cost reduced to 5."); } } private void OnBindCompleted() { if (_active) { _bindHealSinceBind = false; _refundAt = _clock.Now + 0.8; } } private void OnGateChanged(bool active) { _active = active; if (!active) { Reset(); } } private void OnSceneChanged(string scene) { OnLifecycleReset(); } private void OnLifecycleReset() { Reset(); } private void Reset() { _chainActive = false; _refundAt = null; _bindHealSinceBind = false; } } private readonly bool _enabled; public string Key => "witch"; public CrestGate Gate { get; } public IReadOnlyList<IRuneEffect> Runes { get; } public IReadOnlyList<string> RuneKeys { get; } public GraspOfTheUndyingRune Grasp { get; } public FleetFootworkRune Swift { get; } public WitchBindChain BindChain { get; } public bool IsGreenBloodTintActive => Gate.IsActive; public bool IsGraspActive => Grasp.Layers > 0; public WitchCrest(bool enabled, ManualLogSource log, GameEventHub events, IClock clock, Func<string, HealSourceKind> classifyHeal, Action<int> addSilk, GraspOfTheUndyingRune grasp, FleetFootworkRune fleetFootwork) { _enabled = enabled; Gate = new CrestGate("witch", RuneCrestIds.Witch, enabled); Grasp = grasp ?? throw new ArgumentNullException("grasp"); Swift = fleetFootwork ?? throw new ArgumentNullException("fleetFootwork"); Runes = new IRuneEffect[2] { Grasp, Swift }; RuneKeys = new string[2] { Grasp.Key, Swift.Key }; BindChain = new WitchBindChain(log, events, clock, classifyHeal, addSilk, Gate); } public void Start(string currentCrestId) { if (_enabled) { Grasp.Start(currentCrestId); Swift.Start(currentCrestId); BindChain.Start(currentCrestId); } } public void Tick(float deltaSeconds) { if (_enabled) { Grasp.Tick(deltaSeconds); Swift.Tick(deltaSeconds); BindChain.Tick(); } } public void Dispose() { Grasp.Dispose(); Swift.Dispose(); BindChain.Dispose(); } } } namespace CrestsEvolve.Features.StatusIcons { public enum CycleClearOn { None, OnAttack, OnSkillHit, OnAnyDamage } public sealed class CrestCycleDriver : IDisposable { private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly StatusIconTimer _timer; private readonly string _crestId; private readonly CycleClearOn _clearOn; public CrestCycleDriver(ManualLogSource log, GameEventHub events, StatusIconTimer timer, string crestId, CycleClearOn clearOn) { _log = log; _events = events; _crestId = crestId; _clearOn = clearOn; _timer = timer; } public void Start() { _events.AttackResolved += OnAttack; } public void Dispose() { _events.AttackResolved -= OnAttack; } public void Tick(double deltaSeconds) { if (RuneEffects.RunesActive) { _timer.Tick(deltaSeconds); } } private void OnAttack(AttackEvent e) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 if (RuneEffects.RunesActive && !e.IsAdditionalDamage && _clearOn switch { CycleClearOn.OnAttack => true, CycleClearOn.OnSkillHit => (int)e.Kind == 4, CycleClearOn.OnAnyDamage => true, _ => false, }) { _timer.Consume(); _log.LogDebug((object)("[StatusIcons] " + _crestId + " cycle cleared.")); } } } public sealed class CrestRuneIconDriver : IDisposable { private readonly GameEventHub _events; private readonly StatusIconLedger _ledger; private readonly IReadOnlyDictionary<string, IReadOnlyList<(string Key, string Owner)>> _targets; private readonly Func<string> _currentCrestId; private readonly Func<bool> _isRuneToolEquipped; public CrestRuneIconDriver(GameEventHub events, StatusIconLedger ledger, IReadOnlyDictionary<string, IReadOnlyList<(string Key, string Owner)>> targets, Func<string> currentCrestId, Func<bool> isRuneToolEquipped) { _events = events; _ledger = ledger; _targets = targets; _currentCrestId = currentCrestId; _isRuneToolEquipped = isRuneToolEquipped ?? throw new ArgumentNullException("isRuneToolEquipped"); } public void Start(string currentCrestId) { _events.CrestChanged += OnCrestChanged; _events.SceneChanged += OnSceneChanged; _events.ToolEquipsChanged += OnToolEquipsChanged; Apply(currentCrestId); } public void Dispose() { _events.CrestChanged -= OnCrestChanged; _events.SceneChanged -= OnSceneChanged; _events.ToolEquipsChanged -= OnToolEquipsChanged; } private void OnCrestChanged(CrestChangedEvent e) { Apply(e.Current); } private void OnSceneChanged(string scene) { Apply(_currentCrestId()); } private void OnToolEquipsChanged() { Apply(_currentCrestId()); } private void Apply(string crestId) { if (!_isRuneToolEquipped()) { foreach (KeyValuePair<string, IReadOnlyList<(string, string)>> target in _targets) { foreach (var item in target.Value) { if (_ledger.IsRegistered(item.Item1)) { _ledger.SetVisible(item.Item1, item.Item2, false); } } } return; } foreach (KeyValuePair<string, IReadOnlyList<(string, string)>> target2 in _targets) { foreach (var item2 in target2.Value) { if (_ledger.IsRegistered(item2.Item1)) { bool flag = string.Equals(target2.Key, crestId, StringComparison.OrdinalIgnoreCase); _ledger.SetVisible(item2.Item1, item2.Item2, flag); } } } } } public sealed class FireBladeDriver : IDisposable { public const string IconKey = "reaper.fireblade"; public const string IconOwner = "reaper-fireblade"; private const int MaxLayers = 3; private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly StatusIconLedger _ledger; private readonly RuneGate _gate; private readonly ReaperFireBladeOptions _options; private readonly Func<double, bool> _spawnExplosion; private readonly Action? _applyFireVisual; private readonly AttackSkillWindow _window; private int _layers; private bool _started; private bool _active; public int Layers => _layers; public RuneGate Gate => _gate; public double CurrentAttackMultiplier { get; private set; } = 1.0; public FireBladeDriver(ManualLogSource log, GameEventHub events, StatusIconLedger ledger, IClock clock, ReaperFireBladeOptions options, RuneGate gate, Func<double, bool> spawnExplosion, Action? applyFireVisual = null) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown _log = log; _events = events; _ledger = ledger; _gate = gate ?? throw new ArgumentNullException("gate"); _options = options; _spawnExplosion = spawnExplosion; _applyFireVisual = applyFireVisual; _window = new AttackSkillWindow(clock, options.WindowEnd); _gate.ActiveChanged += OnGateChanged; } public void Start(string currentCrestId) { if (!_started) { _started = true; _events.AttackStarted += OnAttackStarted; _events.SkillUsed += OnSkillUsed; _events.AttackResolved += OnAttack; _events.Parried += OnParried; _events.BenchRested += OnLifecycleReset; _events.PlayerDied += OnLifecycleReset; _events.SceneChanged += OnSceneChanged; Reset("start"); OnGateChanged(_gate.IsActive); } } public void Tick(float deltaSeconds) { if (_started && !Active()) { Reset("inactive"); } } public void Dispose() { _started = false; _events.AttackStarted -= OnAttackStarted; _events.SkillUsed -= OnSkillUsed; _events.AttackResolved -= OnAttack; _events.Parried -= OnParried; _events.BenchRested -= OnLifecycleReset; _events.PlayerDied -= OnLifecycleReset; _events.SceneChanged -= OnSceneChanged; _gate.ActiveChanged -= OnGateChanged; Reset("driver disposed"); } private void OnAttackStarted() { if (!Active()) { return; } _window.OnAttack(); CurrentAttackMultiplier = ((_layers > 0) ? (1.0 + 2.0 * _options.FireBuffBonus) : 1.0); if (_layers <= 0) { return; } try { _applyFireVisual?.Invoke(); } catch (Exception ex) { _log.LogWarning((object)("[FireBlade] fire visual failed: " + ex.Message)); } } private void OnSkillUsed(SkillUseEvent e) { if (Active()) { if (_window.TryConsumeIn(_options.PreciseStart, _options.PreciseEnd)) { AddLayers(3); } else if (_window.TryConsumeIn(_options.WindowStart, _options.WindowEnd)) { AddLayers(1); } else { _window.Clear(); } } } private void OnAttack(AttackEvent e) { //IL_0012: 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) //IL_005f: Invalid comparison between Unknown and I4 //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Invalid comparison between Unknown and I4 if (!Active() || e.IsAdditionalDamage) { return; } if ((int)e.Kind == 0) { if (_layers > 0) { _layers--; SetIcon(); _log.LogDebug((object)$"[FireBlade] normal attack consumed 1 layer ({_layers} left), no explosion."); } } else if (((int)e.Kind == 1 || (int)e.Kind == 3) && _layers >= 3) { SpawnExplosionForAttack(e.BaseDamage); } } private void OnParried() { if (Active() && _layers >= 3) { SpawnExplosion(_options.ParryExplosionDamage); } } private void SpawnExplosionForAttack(double baseDamage) { SpawnExplosion(baseDamage * _options.ExplosionDamageMultiplier); } private void SpawnExplosion(double damage) { bool flag = false; try { flag = _spawnExplosion(damage); } catch (Exception ex) { _log.LogWarning((object)("[FireBlade] explosion spawn threw: " + ex.Message)); } _log.LogDebug((object)$"[FireBlade] explosion consumed 3 layers: damage={damage}, spawned={flag}."); _layers = 0; SetIcon(); } private void AddLayers(int amount) { _layers = Math.Min(3, _layers + amount); SetIcon(); _log.LogDebug((object)$"[FireBlade] fire-blade window hit: +{amount} layer(s) ({_layers}/{3})."); } private void OnGateChanged(bool active) { _active = active; if (!active) { Reset("gate closed"); } } private void OnSceneChanged(string scene) { OnLifecycleReset(); } private void OnLifecycleReset() { Reset("lifecycle reset"); } private void Reset(string reason) { bool num = _layers != 0 || CurrentAttackMultiplier != 1.0; _layers = 0; CurrentAttackMultiplier = 1.0; _window.Clear(); SetIcon(); if (num) { _log.LogDebug((object)("[FireBlade] reset (" + reason + ").")); } } private void SetIcon() { _ledger.Set("reaper.fireblade", "reaper-fireblade", _layers); } private bool Active() { if (_started && _active) { return _options.Enabled; } return false; } } public sealed record ReaperFireBladeOptions(bool Enabled, double WindowStart, double WindowEnd, double PreciseStart, double PreciseEnd, double FireBuffBonus, double ExplosionRadius, double ExplosionDamageMultiplier, double ParryExplosionDamage, bool FireVisualEnabled); public static class FireBladeBinder { public static ReaperFireBladeOptions Bind(ConfigFile config, ManualLogSource log) { bool value = config.Bind<bool>("Reaper.FireBlade", "Enabled", true, "Enable the Fire Blade passive (Flintslate second form; window stacking, damage, explosions).").Value; double value2 = config.Bind<double>("Reaper.FireBlade", "WindowStart", 0.2, "Fire-blade press window start after a nail swing.").Value; double value3 = config.Bind<double>("Reaper.FireBlade", "WindowEnd", 0.3, "Fire-blade press window end after a nail swing.").Value; double value4 = config.Bind<double>("Reaper.FireBlade", "PreciseStart", 0.27, "Precise fire-blade window start; grants +3 layers.").Value; double value5 = config.Bind<double>("Reaper.FireBlade", "PreciseEnd", 0.3, "Precise fire-blade window end; grants +3 layers.").Value; double value6 = config.Bind<double>("Reaper.FireBlade", "FireBuffBonus", 0.25, "Vanilla fire-buff damage bonus assumed by the passive (damage x (1 + 2 x bonus), [待实测]).").Value; double value7 = config.Bind<double>("Reaper.FireBlade", "ExplosionRadius", 3.0, "Fire-blade explosion radius in meters (REA-006/007, [待实测]).").Value; double value8 = config.Bind<double>("Reaper.FireBlade", "ExplosionDamageMultiplier", 3.0, "Fire-blade explosion damage over the triggering attack's base damage (REA-006/007, [待实测]).").Value; double value9 = config.Bind<double>("Reaper.FireBlade", "ParryExplosionDamage", 10.0, "Fire-blade explosion damage when triggered by parry (no base attack damage, [待实测]).").Value; ReaperFireBladeOptions reaperFireBladeOptions = new ReaperFireBladeOptions(FireVisualEnabled: config.Bind<bool>("Reaper.FireBlade", "FireVisualEnabled", true, "Apply vanilla fire-nail visual (HeroNailImbuement.SetElement) while layers > 0.").Value, Enabled: value, WindowStart: Math.Max(0.0, value2), WindowEnd: Math.Max(value2, value3), PreciseStart: Math.Max(value2, value4), PreciseEnd: Math.Max(value4, value5), FireBuffBonus: Math.Max(0.0, value6), ExplosionRadius: Math.Max(0.1, value7), ExplosionDamageMultiplier: Math.Max(0.0, value8), ParryExplosionDamage: Math.Max(0.0, value9)); log.LogDebug((object)$"[FireBlade] config bound: {reaperFireBladeOptions}"); return reaperFireBladeOptions; } } public sealed class WitchAftershockDriver : IDisposable { public const string IconKey = "witch.aftershock"; public const string IconOwner = "witch-aftershock"; private const int MaxLayers = 10; private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly StatusIconLedger _ledger; private readonly RuneGate _gate; private readonly Func<string, HealSourceKind> _classify; private int _layers; private bool _started; private bool _active; public int Layers => _layers; public RuneGate Gate => _gate; public WitchAftershockDriver(ManualLogSource log, GameEventHub events, StatusIconLedger ledger, Func<string, HealSourceKind> classify, RuneGate gate) { _log = log; _events = events; _ledger = ledger; _classify = classify; _gate = gate ?? throw new ArgumentNullException("gate"); _gate.ActiveChanged += OnGateChanged; } public void Start(string currentCrestId) { if (!_started) { _started = true; _events.HealResolved += OnHealResolved; _events.PlayerDamageAfter += OnPlayerDamage; _events.BenchRested += OnLifecycleReset; _events.PlayerDied += OnLifecycleReset; _events.SceneChanged += OnSceneChanged; Apply(); OnGateChanged(_gate.IsActive); } } public void Dispose() { _started = false; _events.HealResolved -= OnHealResolved; _events.PlayerDamageAfter -= OnPlayerDamage; _events.BenchRested -= OnLifecycleReset; _events.PlayerDied -= OnLifecycleReset; _events.SceneChanged -= OnSceneChanged; _gate.ActiveChanged -= OnGateChanged; _layers = 0; Apply(); } private void OnHealResolved(HealEvent e) { if (_started && _active && _classify(e.SourceTag) == HealSourceKind.Bind) { int num = ((e.Applied > 0) ? e.Applied : e.Requested); if (num > 0) { AddLayers(num); } } } private void OnPlayerDamage(PlayerDamageEvent e) { if (_started && _active && !e.Cancelled && _layers > 0) { ConsumeLayers(2); } } private void OnGateChanged(bool active) { _active = active; if (!active) { Reset("gate closed"); } } private void OnSceneChanged(string scene) { OnLifecycleReset(); } private void OnLifecycleReset() { Reset("lifecycle reset"); } private void AddLayers(int amount) { _layers = Math.Min(10, _layers + amount); Apply(); _log.LogDebug((object)$"[WitchAftershock] +{amount} layer(s) ({_layers}/{10})."); } private void ConsumeLayers(int amount) { _layers = Math.Max(0, _layers - amount); Apply(); _log.LogDebug((object)$"[WitchAftershock] damaged: -{amount} layer(s) ({_layers} left)."); } private void Reset(string reason) { if (_layers != 0) { _layers = 0; Apply(); _log.LogDebug((object)("[WitchAftershock] reset (" + reason + ").")); } } private void Apply() { _ledger.Set("witch.aftershock", "witch-aftershock", _layers); _ledger.SetVisible("witch.aftershock", "witch-aftershock", _layers > 0); } } } namespace CrestsEvolve.Features.Runa { public sealed class AftershockRune : IRuneEffect, IDisposable { public const string IconKey = "cursed.aftershock"; public const string IconOwner = "cursed-aftershock"; private readonly bool _enabled; private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly StatusIconLedger _ledger; private readonly AftershockOptions _options; private readonly Action<int> _addBlueMasks; private bool _started; private bool _active; private bool _charged; private double _elapsed; public string Key => "aftershock"; public RuneGate Gate { get; } public IReadOnlyList<(string Key, string Owner)> Icons => new(string, string)[1] { ("cursed.aftershock", "cursed-aftershock") }; public bool IsCharged => _charged; public AftershockRune(bool enabled, ManualLogSource log, GameEventHub events, StatusIconLedger ledger, IClock clock, RuneGate gate, AftershockOptions options, Action<int> addBlueMasks) { _enabled = enabled; _log = log; _events = events; _ledger = ledger; Gate = gate ?? throw new ArgumentNullException("gate"); _options = options ?? throw new ArgumentNullException("options"); _addBlueMasks = addBlueMasks ?? throw new ArgumentNullException("addBlueMasks"); Gate.ActiveChanged += OnGateChanged; } public void Start(string currentCrestId) { if (!_started) { _started = true; _events.BindCompleted += OnBindCompleted; _events.PlayerDied += OnLifecycleReset; SetIcon(); OnGateChanged(Gate.IsActive); } } public void Tick(float deltaSeconds) { if (!_started) { return; } if (!_active) { Reset("gate closed"); } else if (!_charged) { _elapsed += deltaSeconds; if (_elapsed >= _options.CycleSeconds) { _charged = true; _log.LogDebug((object)"[Aftershock] charged; next bind restores masks."); } SetIcon(); } } public void Dispose() { _started = false; _events.BindCompleted -= OnBindCompleted; _events.PlayerDied -= OnLifecycleReset; Gate.ActiveChanged -= OnGateChanged; Reset("driver disposed"); } private void OnBindCompleted() { if (_started && _active && _charged) { try { _addBlueMasks(_options.BlueMasksOnBind); } catch (Exception ex) { _log.LogWarning((object)("[Aftershock] blue-mask grant failed: " + ex.Message)); } Reset("bind consumed"); _log.LogDebug((object)$"[Aftershock] bind consumed charge; +{_options.BlueMasksOnBind} masks."); } } private void OnGateChanged(bool active) { _active = active; if (!active) { Reset("gate closed"); } } private void OnLifecycleReset() { Reset("lifecycle reset"); } private void Reset(string reason) { bool num = _charged || _elapsed != 0.0; _charged = false; _elapsed = 0.0; SetIcon(); if (num) { _log.LogDebug((object)("[Aftershock] reset (" + reason + ").")); } } private void SetIcon() { int num = (int)Math.Round(_options.CycleSeconds); _ledger.SetMax("cursed.aftershock", "cursed-aftershock", num); _ledger.SetVisible("cursed.aftershock", "cursed-aftershock", _active); _ledger.Set("cursed.aftershock", "cursed-aftershock", _charged ? num : Math.Min(num, (int)Math.Floor(_elapsed))); } } public sealed record AftershockOptions(bool Enabled, double CycleSeconds, int BlueMasksOnBind); public static class AftershockBinder { public static AftershockOptions Bind(ConfigFile config, ManualLogSource log) { AftershockOptions aftershockOptions = new AftershockOptions(config.Bind<bool>("Cursed.Aftershock", "Enabled", true, "Enable the Cursed Aftershock rune (10s charge; bind restores 3 blue masks).").Value, Math.Max(0.1, config.Bind<double>("Cursed.Aftershock", "CycleSeconds", 10.0, "Seconds until the rune is fully charged.").Value), Math.Max(1, config.Bind<int>("Cursed.Aftershock", "BlueMasksOnBind", 3, "Blue masks restored when binding at full charge.").Value)); log.LogDebug((object)$"[Aftershock] config bound: {aftershockOptions}"); return aftershockOptions; } } public sealed class ArcaneCometRune : IRuneEffect, IDisposable { public const string IconKey = "shaman.cycle"; public const string IconOwner = "shaman"; private readonly bool _enabled; private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly StatusIconLedger _ledger; private readonly ArcaneCometOptions _options; private readonly Action _spawnSpiritFlame; private bool _started; private bool _active; private bool _charged; private double _elapsed; public string Key => "arcane-comet"; public RuneGate Gate { get; } public IReadOnlyList<(string Key, string Owner)> Icons => new(string, string)[1] { ("shaman.cycle", "shaman") }; public bool IsCharged => _charged; public ArcaneCometRune(bool enabled, ManualLogSource log, GameEventHub events, StatusIconLedger ledger, IClock clock, RuneGate gate, ArcaneCometOptions options, Action spawnSpiritFlame) { _enabled = enabled; _log = log; _events = events; _ledger = ledger; Gate = gate ?? throw new ArgumentNullException("gate"); _options = options ?? throw new ArgumentNullException("options"); _spawnSpiritFlame = spawnSpiritFlame ?? throw new ArgumentNullException("spawnSpiritFlame"); Gate.ActiveChanged += OnGateChanged; } public void Start(string currentCrestId) { if (!_started) { _started = true; _events.EnemyDamageResolved += OnEnemyDamage; _events.PlayerDied += OnLifecycleReset; SetIcon(); OnGateChanged(Gate.IsActive); } } public void Tick(float deltaSeconds) { if (!_started) { return; } if (!_active) { Reset("gate closed"); } else if (!_charged) { _elapsed += deltaSeconds; if (_elapsed >= _options.CycleSeconds) { _charged = true; _log.LogDebug((object)"[ArcaneComet] charged; next skill hit summons a spirit flame."); } SetIcon(); } } public void Dispose() { _started = false; _events.EnemyDamageResolved -= OnEnemyDamage; _events.PlayerDied -= OnLifecycleReset; Gate.ActiveChanged -= OnGateChanged; Reset("driver disposed"); } private void OnEnemyDamage(AttackEvent e) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 if (_started && _active && _charged && !e.IsAdditionalDamage && e.IsHeroDamage && (int)e.Kind == 4) {
CrestsEvolve.RageOfTheGoner/CrestsEvolve.RageOfTheGoner.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CrestsEvolve.Core.Balance; using CrestsEvolve.Core.Combat; using CrestsEvolve.Core.Events; using CrestsEvolve.Core.Rage; using CrestsEvolve.Core.State; using CrestsEvolve.Core.UI; using CrestsEvolve.Features; using CrestsEvolve.Features.Rage; using CrestsEvolve.Features.StatusIcons; using CrestsEvolve.GameInterop; using CrestsEvolve.Rage1; using CrestsEvolve.Rage2; using CrestsEvolve.RageOfTheGoner.Bootstrap; using GlobalEnums; using GlobalSettings; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("CrestsEvolve.Core.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CrestsEvolve.RageOfTheGoner")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+757e75a2d4534028308600b7117b47c56eb48037")] [assembly: AssemblyProduct("CrestsEvolve.RageOfTheGoner")] [assembly: AssemblyTitle("CrestsEvolve.RageOfTheGoner")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } internal static class IsExternalInit { } } namespace CrestsEvolve.Rage2 { public static class ArchitectRage2 { public const string CrestId = "toolmaster"; public static bool IsToolHit(HitInstance hit) { //IL_0000: 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) if (hit.IsHeroDamage) { return hit.RepresentingTool != null; } return false; } public static void SilkPostfix(HitInstance hitInstance) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (!ArchitectRageBridge.IsSuperRageActive() || !IsToolHit(hitInstance)) { return; } int num = ArchitectRageBridge.ToolSilkPerHit(); if (num > 0) { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null) { instance.AddSilk(num, false, (SilkAddSource)0); } } } } public static class BeastRage2 { public const string CrestId = "warrior"; public static void DamagePrefix(ref HitInstance hitInstance) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 if (!BeastRageBridge.IsSuperRageActive() || !hitInstance.IsHeroDamage) { return; } AttackTypes attackType = hitInstance.AttackType; if ((int)attackType <= 1 || (int)attackType == 6 || (int)attackType == 16) { double num = BeastRageBridge.AttackDamageMultiplier(); if (!(num <= 1.0)) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * num); } } } public static void AttackSpeedPostfix(ref float __result) { if (BeastRageBridge.IsSuperRageActive()) { double num = BeastRageBridge.AttackSpeedBonus(); if (!(num <= 0.0)) { __result = (float)((double)__result / (1.0 + num)); } } } } public static class CloaklessRage2 { public const string CrestId = "cloakless"; public static Func<float> NextFloat { get; set; } = () => Random.Range(0f, 1f); public static bool ShouldCrit(double chance, float roll) { if (chance > 0.0) { return roll <= (float)chance; } return false; } public static void CritPrefix(ref HitInstance hitInstance) { if (CloaklessRageBridge.IsSuperRageActive() && hitInstance.IsHeroDamage && !hitInstance.CriticalHit && ShouldCrit(CloaklessRageBridge.CritChance(), NextFloat())) { hitInstance.CriticalHit = true; } } } public static class CursedRage2 { public const string CrestId = "cursed"; public static void NoCostPostfix(PlayerData __instance, ref int __result) { if (CursedRageBridge.IsSuperRageActive()) { __result = 0; } } } public static class HunterRage2 { public const string CrestId = "hunter"; public static int ExtraSilk(int layers, int threshold, int silkPerHit) { if (layers <= threshold) { return 0; } return Math.Max(0, silkPerHit); } public static void SilkPostfix(HitInstance hitInstance) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!HunterRageBridge.IsSuperRageActive() || !hitInstance.IsHeroDamage) { return; } int num = ExtraSilk(HunterRageBridge.ComboLayerCount(), HunterRageBridge.FocusThresholdSuperRage(), HunterRageBridge.SilkPerHit()); if (num > 0) { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null) { instance.AddSilk(num, false, (SilkAddSource)0); } } } } public static class ReaperRage2 { public const string CrestId = "reaper"; public static int SilkPartsAmount(int amount, int multiplier) { return Math.Max(0, amount * multiplier); } public static void SilkPartsPrefix(HeroController __instance, ref int amount) { if (ReaperRageBridge.IsSuperRageActive()) { amount = SilkPartsAmount(amount, ReaperRageBridge.SilkOrbPartsMultiplier()); } } } public static class ShamanRage2 { public const string CrestId = "spell"; public static int ApplyCostReduction(int cost, int reduction) { return Math.Max(0, cost - reduction); } public static void SilkCostPostfix(PlayerData __instance, ref int __result) { if (ShamanRageBridge.IsSuperRageActive()) { __result = ApplyCostReduction(__result, ShamanRageBridge.SkillCostReduction()); } } } public static class WandererRage2 { public const string CrestId = "wanderer"; public static void CritMultiplierPostfix(ref float __result) { if (WandererRageBridge.IsSuperRageActive()) { __result *= (float)WandererRageBridge.CritDamageMultiplier(); } } public static void KnockbackMultiplierPostfix(ref float __result) { if (WandererRageBridge.IsSuperRageActive()) { __result *= (float)WandererRageBridge.KnockbackMultiplier(); } } } public static class WitchRage2 { public const string CrestId = "witch"; public static void DamagePrefix(ref HitInstance hitInstance) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (WitchRageBridge.IsSuperRageActive() && hitInstance.IsHeroDamage) { double num = 1.0; if (((hitInstance.Source != null) ? ((Object)hitInstance.Source).name : string.Empty).IndexOf("Bind", StringComparison.OrdinalIgnoreCase) >= 0) { num = WitchRageBridge.BindDamageMultiplier(); } else if (WitchRage1.IsMultiHitAttack(hitInstance)) { num = WitchRageBridge.SuperRageMultiHitDamageMultiplier(); } if (!(num <= 1.0)) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * num); } } } } public sealed class WitchRage2BindRangeScaler : MonoBehaviour { private readonly Dictionary<Transform, Vector3> _originals = new Dictionary<Transform, Vector3>(); private Transform? _witchBind; private bool _captured; private float _appliedMultiplier; private void LateUpdate() { if (!WitchRageBridge.IsSuperRageActive() || !WitchRageBridge.ScaleBindRange()) { if (_captured) { RestoreAll(); } return; } HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return; } if ((Object)(object)_witchBind == (Object)null) { _captured = false; _originals.Clear(); _witchBind = FindWitchBind(instance); if ((Object)(object)_witchBind == (Object)null) { return; } } float num = (float)WitchRageBridge.BindRangeMultiplier(); if (num <= 0f) { return; } if (!_captured) { CaptureAndApply(num); _captured = true; _appliedMultiplier = num; return; } CaptureAndApply(num); if (!Mathf.Approximately(_appliedMultiplier, num)) { ApplyMultiplier(num); _appliedMultiplier = num; } } private void CaptureAndApply(float multiplier) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_witchBind == (Object)null) { return; } foreach (Transform item in _witchBind) { Transform val = item; if (!((Object)(object)val == (Object)null)) { string name = ((Object)val).name; if ((name.IndexOf("Damager", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Tendril", StringComparison.OrdinalIgnoreCase) >= 0) && !_originals.ContainsKey(val)) { Vector3 localScale = val.localScale; _originals.Add(val, localScale); val.localScale = new Vector3(localScale.x * multiplier, localScale.y * multiplier, localScale.z * multiplier); } } } } private void ApplyMultiplier(float multiplier) { //IL_002e: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<Transform, Vector3> original in _originals) { if (!((Object)(object)original.Key == (Object)null)) { original.Key.localScale = new Vector3(original.Value.x * multiplier, original.Value.y * multiplier, original.Value.z * multiplier); } } } private static Transform? FindWitchBind(HeroController hero) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)hero != (Object)null) { foreach (Transform item in ((Component)hero).transform) { Transform val = item; if (string.Equals(((Object)val).name, "Witch Bind", StringComparison.OrdinalIgnoreCase)) { return val; } } } GameObject val2 = GameObject.Find("Witch Bind"); if (!((Object)(object)val2 != (Object)null)) { return null; } return val2.transform; } private void RestoreAll() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<Transform, Vector3> original in _originals) { if (!((Object)(object)original.Key == (Object)null)) { original.Key.localScale = original.Value; } } _originals.Clear(); _captured = false; _appliedMultiplier = 0f; } private void OnDestroy() { if (_captured) { RestoreAll(); } } } } namespace CrestsEvolve.Rage1 { public static class ArchitectRage1 { public const string CrestId = "toolmaster"; public static bool IsToolHit(HitInstance hit) { //IL_0000: 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) if (hit.IsHeroDamage) { return hit.RepresentingTool != null; } return false; } public static int ScaleDamage(int damage, double multiplier) { return (int)Math.Round((double)damage * multiplier); } public static void DamagePrefix(ref HitInstance hitInstance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (ArchitectRageBridge.IsActive() && IsToolHit(hitInstance)) { double num = ArchitectRageBridge.ToolDamageMultiplier(); if (!(num <= 1.0)) { hitInstance.DamageDealt = ScaleDamage(hitInstance.DamageDealt, num); } } } } public static class BeastRage1 { public const string CrestId = "warrior"; public static void RangePostfix(NailAttackBase __instance) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) if (BeastRageBridge.IsActive()) { float num = (float)BeastRageBridge.AttackRangeMultiplier(); if (!(num <= 0f)) { Vector3 localScale = ((Component)__instance).transform.localScale; ((Component)__instance).transform.localScale = new Vector3(localScale.x * num, localScale.y, localScale.z); } } } } public static class CloaklessRage1 { public const string CrestId = "cloakless"; private static ManualLogSource? _log; private static FieldInfo? _luckyDiceShieldedHits; private static bool _warned; public static void Configure(ManualLogSource log) { _log = log; _luckyDiceShieldedHits = AccessTools.Field(typeof(HeroController), "luckyDiceShieldedHits"); if (_luckyDiceShieldedHits == null) { log.LogWarning((object)"[CloaklessRage] luckyDiceShieldedHits field not found; block disabled."); } } public static int BlockThreshold(int current, bool active, int blockChancePercent) { if (!active) { return current; } return blockChancePercent; } public static void LuckyDiceThresholdPostfix(int hits, ref int __result) { if (CloaklessRageBridge.IsActive()) { __result = BlockThreshold(__result, active: true, CloaklessRageBridge.BlockChancePercent()); } } public static void TakeDamagePrefix(HeroController __instance, HazardType hazardType) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 if (!CloaklessRageBridge.IsActive() || (int)hazardType != 1 || (Object)(object)__instance == (Object)null || _luckyDiceShieldedHits == null) { return; } try { if ((int)(_luckyDiceShieldedHits.GetValue(__instance) ?? ((object)0)) < 1) { _luckyDiceShieldedHits.SetValue(__instance, 1); } } catch (Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[CloaklessRage] luckyDiceShieldedHits write failed: " + ex.Message)); } } } } } public static class CursedRage1 { public const string CrestId = "cursed"; private static readonly string[] SkillSourceKeywords = new string[4] { "Wave", "Rune", "Charge Slash", "Bind" }; public static bool IsSkillSourceName(string sourceName) { if (string.IsNullOrEmpty(sourceName)) { return false; } string[] skillSourceKeywords = SkillSourceKeywords; foreach (string value in skillSourceKeywords) { if (sourceName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } public static bool IsSkillHit(HitInstance hit) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0011: Invalid comparison between Unknown and I4 //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!hit.IsHeroDamage) { return false; } if ((int)hit.AttackType == 2 || (int)hit.AttackType == 16) { return true; } return IsSkillSourceName((hit.Source != null) ? ((Object)hit.Source).name : string.Empty); } public static int ScaleDamage(int damage, double multiplier) { return (int)Math.Round((double)damage * multiplier); } public static void SkillDamagePrefix(ref HitInstance hitInstance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (CursedRageBridge.IsActive() && IsSkillHit(hitInstance)) { double num = CursedRageBridge.SkillDamageMultiplier(); if (!(num <= 1.0)) { hitInstance.DamageDealt = ScaleDamage(hitInstance.DamageDealt, num); } } } } public static class HunterRage1 { public const string CrestId = "hunter"; public static int ExtraSilk(int layers, int threshold, int silkPerHit) { if (layers <= threshold) { return 0; } return Math.Max(0, silkPerHit); } public static void SilkPostfix(HitInstance hitInstance) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!HunterRageBridge.IsActive() || !hitInstance.IsHeroDamage) { return; } int num = ExtraSilk(HunterRageBridge.ComboLayerCount(), HunterRageBridge.FocusThresholdRage(), HunterRageBridge.SilkPerHit()); if (num > 0) { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null) { instance.AddSilk(num, false, (SilkAddSource)0); } } } } public static class ReaperRage1 { public const string CrestId = "reaper"; public static void CritExplosionPostfix(HealthManager __instance, HitInstance hitInstance) { //IL_000d: 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) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (ReaperRageBridge.IsActive() && hitInstance.IsHeroDamage && hitInstance.CriticalHit && (int)hitInstance.AttackType != 11 && !((Object)(object)__instance == (Object)null) && !((Object)(object)((Component)__instance).transform == (Object)null)) { ReaperRageBridge.SpawnExplosionAt(((Component)__instance).transform.position); } } public static void BindCritPostfix(HeroController __instance) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (ReaperRageBridge.IsActive() && !((Object)(object)__instance == (Object)null)) { WandererCrestStateInfo wandererState = __instance.WandererState; if (!wandererState.QueuedNextHitCritical) { wandererState.QueuedNextHitCritical = true; __instance.WandererState = wandererState; } } } } public static class ShamanRage1 { public const string CrestId = "spell"; public static bool IsWaveHit(string? sourceName) { if (string.IsNullOrEmpty(sourceName)) { return false; } if (sourceName.IndexOf("Wave", StringComparison.OrdinalIgnoreCase) < 0) { return sourceName.IndexOf("Rune", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } public static void WaveSilkPrefix(ref HitInstance hitInstance) { if (!ShamanRageBridge.IsActive() || !hitInstance.IsHeroDamage || !IsWaveHit((hitInstance.Source != null) ? ((Object)hitInstance.Source).name : string.Empty)) { return; } int num = ShamanRageBridge.WaveSilkPerHit(); if (num > 0) { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null) { instance.AddSilk(num, false, (SilkAddSource)0); } } } } public static class WandererRage1 { public const string CrestId = "wanderer"; public static void DamagePrefix(ref HitInstance hitInstance) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 if (!WandererRageBridge.IsActive() || !hitInstance.IsHeroDamage) { return; } AttackTypes attackType = hitInstance.AttackType; if ((int)attackType <= 1 || (int)attackType == 6 || (int)attackType == 16) { double num = WandererRageBridge.DamageMultiplier(); if (!(num <= 1.0)) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * num); } } } } public static class WitchRage1 { public const string CrestId = "witch"; private static bool _dashInProgress; public static bool IsMultiHitSourceName(string? sourceName) { if (string.IsNullOrEmpty(sourceName)) { return false; } if (sourceName.IndexOf("Charge Slash", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (sourceName.IndexOf("Down", StringComparison.OrdinalIgnoreCase) >= 0 && (sourceName.IndexOf("Slash", StringComparison.OrdinalIgnoreCase) >= 0 || sourceName.IndexOf("Spike", StringComparison.OrdinalIgnoreCase) >= 0)) { return true; } return false; } public static bool IsMultiHitAttack(HitInstance hit) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!hit.IsHeroDamage) { return false; } if (_dashInProgress) { return true; } if ((int)hit.AttackType == 16) { return true; } return IsMultiHitSourceName((hit.Source != null) ? ((Object)hit.Source).name : string.Empty); } public static void DashPostfix(HeroController __instance) { _dashInProgress = (Object)(object)__instance != (Object)null && __instance.cState != null && __instance.cState.dashing; } public static void SilkPrefix(ref HitInstance hitInstance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!WitchRageBridge.IsActive() || !IsMultiHitAttack(hitInstance)) { return; } int num = WitchRageBridge.MultiHitSilkPerHit(); if (num > 0) { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null) { instance.AddSilk(num, false, (SilkAddSource)0); } } } } } namespace CrestsEvolve.GameInterop { public static class ArchitectRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<double> ToolDamageMultiplier { get; set; } = () => 1.75; public static Func<bool> IsSuperRageActive { get; set; } = () => false; public static Func<int> ToolSilkPerHit { get; set; } = () => 0; } public static class BeastRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<bool> IsSuperRageActive { get; set; } = () => false; public static Func<double> AttackRangeMultiplier { get; set; } = () => 1.25; public static Func<double> AttackDamageMultiplier { get; set; } = () => 1.25; public static Func<double> AttackSpeedBonus { get; set; } = () => 0.18; public static Func<bool> IsHealBlocked { get; set; } = () => false; public static Action<bool> SetHealBlocked { get; set; } = delegate { }; public static Action SetRageActive { get; set; } = delegate { }; } public static class CloaklessRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<int> BlockChancePercent { get; set; } = () => 80; public static Func<bool> IsSuperRageActive { get; set; } = () => false; public static Func<double> CritChance { get; set; } = () => 0.8; } public static class CursedRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<double> SkillDamageMultiplier { get; set; } = () => 1.75; public static Func<bool> IsSuperRageActive { get; set; } = () => false; } public static class HunterRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<bool> IsSuperRageActive { get; set; } = () => false; public static Func<int> ComboLayerCount { get; set; } = () => 0; public static Func<int> SilkPerHit { get; set; } = () => 1; public static Func<int> FocusThresholdRage { get; set; } = () => 6; public static Func<int> FocusThresholdSuperRage { get; set; } = () => 12; } public static class RageToolBridge { public static Func<bool> IsActive { get; set; } = () => false; } public static class ReaperRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<bool> IsSuperRageActive { get; set; } = () => false; public static Func<int> SilkOrbPartsMultiplier { get; set; } = () => 3; public static Func<Vector3, bool> SpawnExplosionAt { get; set; } = (Vector3 _) => false; } public static class ReaperSuperRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<double> ExplosionDamageMultiplier { get; set; } = () => 1.0; public static Func<float> ExplosionRadius { get; set; } = () => 3f; public static Func<double, float, Vector3, bool> SpawnExplosionAt { get; set; } = (double _, float _, Vector3 _) => false; } public static class ShamanRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<double> CritChance { get; set; } = () => 0.1; public static Func<int> WaveSilkPerHit { get; set; } = () => 1; public static Func<bool> IsSuperRageActive { get; set; } = () => false; public static Func<int> SkillCostReduction { get; set; } = () => 1; } public static class WandererRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<double> DamageMultiplier { get; set; } = () => 1.0; public static Func<bool> IsSuperRageActive { get; set; } = () => false; public static Func<double> CritDamageMultiplier { get; set; } = () => 2.0; public static Func<double> KnockbackMultiplier { get; set; } = () => 2.0; } public static class WandererSuperRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<int> BonusCount { get; set; } = () => 0; public static Func<double> CritPerBonus { get; set; } = () => 0.02; } public static class WitchRageBridge { public static Func<bool> IsActive { get; set; } = () => false; public static Func<double> DashDamageMultiplier { get; set; } = () => 1.0; public static Func<int> DashSilkPerHit { get; set; } = () => 0; public static Func<double> SawbladeCritChance { get; set; } = () => 0.0; public static Func<string> SawbladeToolId { get; set; } = () => "Brolly Spike"; public static Func<double> BindDamageMultiplier { get; set; } = () => 1.5; public static Func<double> BindRangeMultiplier { get; set; } = () => 1.5; public static Func<bool> ScaleBindRange { get; set; } = () => true; public static Func<bool> IsSuperRageActive { get; set; } = () => false; public static Func<int> MultiHitSilkPerHit { get; set; } = () => 1; public static Func<double> SuperRageMultiHitDamageMultiplier { get; set; } = () => 2.0; } } namespace CrestsEvolve.Features.StatusIcons { public sealed class RageStatusDriver : IDisposable { public sealed record RageIconState(bool Visible, int Value, string? ColorHex); public const string IconKey = "rage.fury"; public const string IconOwner = "rage"; private readonly RageFeature _rage; private readonly StatusIconLedger _ledger; private readonly GameEventHub _events; private readonly Func<bool> _isRageToolEquipped; private bool _started; public RageStatusDriver(RageFeature rage, StatusIconLedger ledger, GameEventHub events, Func<bool>? isRageToolEquipped = null) { _rage = rage ?? throw new ArgumentNullException("rage"); _ledger = ledger ?? throw new ArgumentNullException("ledger"); _events = events ?? throw new ArgumentNullException("events"); _isRageToolEquipped = isRageToolEquipped ?? ((Func<bool>)(() => true)); } public static RageIconState Resolve(bool toolEquipped, RageMode mode) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 if (!toolEquipped) { return new RageIconState(Visible: false, 0, null); } if ((int)mode == 0) { return new RageIconState(Visible: true, 0, "#FFFFFF"); } if ((int)mode == 2) { return new RageIconState(Visible: true, 1, null); } return new RageIconState(Visible: true, 0, null); } public void Start() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (!_started) { _started = true; _rage.Changed += OnChanged; _events.ToolEquipsChanged += OnToolEquipsChanged; Refresh(_rage.Snapshot.Mode, _isRageToolEquipped()); } } public void RefreshEquipped() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (_started) { Refresh(_rage.Snapshot.Mode, _isRageToolEquipped()); } } public void Dispose() { if (_started) { _started = false; _rage.Changed -= OnChanged; _events.ToolEquipsChanged -= OnToolEquipsChanged; _ledger.Set("rage.fury", "rage", 0); _ledger.SetVisible("rage.fury", "rage", false); _ledger.SetColorOverride("rage.fury", "rage", (string)null); } } private void OnChanged(RageSnapshot snapshot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) Refresh(snapshot.Mode, _isRageToolEquipped()); } private void OnToolEquipsChanged() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (_started) { Refresh(_rage.Snapshot.Mode, _isRageToolEquipped()); } } private void Refresh(RageMode mode, bool toolEquipped) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) RageIconState rageIconState = Resolve(toolEquipped, mode); _ledger.SetVisible("rage.fury", "rage", rageIconState.Visible); _ledger.Set("rage.fury", "rage", rageIconState.Value); _ledger.SetColorOverride("rage.fury", "rage", rageIconState.ColorHex); } } } namespace CrestsEvolve.Features.Rage { public sealed class ArchitectRageBehavior : IRageBehavior { private readonly Func<bool> _gateActive; public string CrestId => "toolmaster"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public ArchitectRageBehavior(Func<bool>? gateActive = null) { _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive()) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.Allow; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { return RageDamageResponse.Keep; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } public sealed class BeastRageBehavior : IRageBehavior { private readonly Func<bool> _gateActive; public string CrestId => "warrior"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public BeastRageBehavior(Func<bool>? gateActive = null) { _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive()) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.Allow; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { return RageDamageResponse.Keep; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } public sealed class CloaklessRageBehavior : IRageBehavior { private readonly Func<bool> _gateActive; public string CrestId => "cloakless"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public CloaklessRageBehavior(Func<bool>? gateActive = null) { _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive()) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.Allow; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { return RageDamageResponse.Keep; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } public sealed class CursedRageBehavior : IRageBehavior { private readonly Func<bool> _gateActive; public string CrestId => "cursed"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public CursedRageBehavior(Func<bool>? gateActive = null) { _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive()) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.Allow; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { return RageDamageResponse.Keep; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } public static class FuryWhiteSlotPolicy { public static bool ShouldProjectMaxHealth(bool rageEnabled, bool isFuryInWhiteSlot) { return rageEnabled && isFuryInWhiteSlot; } } public sealed class FuryWhiteSlotDriver : IDisposable { private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly IGameApi _game; private readonly Func<bool> _isFuryInWhiteSlot; private readonly Func<bool> _isRageEnabled; private bool _projectedByWhiteSlot; private bool _started; public FuryWhiteSlotDriver(ManualLogSource log, GameEventHub events, IGameApi game, Func<bool> isFuryInWhiteSlot, Func<bool> isRageEnabled) { _log = log ?? throw new ArgumentNullException("log"); _events = events ?? throw new ArgumentNullException("events"); _game = game ?? throw new ArgumentNullException("game"); _isFuryInWhiteSlot = isFuryInWhiteSlot ?? throw new ArgumentNullException("isFuryInWhiteSlot"); _isRageEnabled = isRageEnabled ?? throw new ArgumentNullException("isRageEnabled"); } public void Start() { if (!_started) { _started = true; _events.ToolEquipsChanged += OnChanged; _events.SceneChanged += OnSceneChanged; _events.CrestChanged += OnCrestChanged; Refresh(); } } public void Dispose() { if (_started) { _started = false; _events.ToolEquipsChanged -= OnChanged; _events.SceneChanged -= OnSceneChanged; _events.CrestChanged -= OnCrestChanged; if (_projectedByWhiteSlot) { _game.SetTemporaryMaxHealth((int?)null); _projectedByWhiteSlot = false; } } } private void OnCrestChanged(CrestChangedEvent e) { Refresh(); } private void OnSceneChanged(string scene) { Refresh(); } private void OnChanged() { Refresh(); } private void Refresh() { bool flag; try { flag = FuryWhiteSlotPolicy.ShouldProjectMaxHealth(_isRageEnabled(), _isFuryInWhiteSlot()); } catch (Exception ex) { _log.LogWarning((object)("[FuryWhiteSlot] gate query failed: " + ex.Message)); return; } if (flag) { _game.SetTemporaryMaxHealth((int?)1); _projectedByWhiteSlot = true; } else if (_projectedByWhiteSlot) { _game.SetTemporaryMaxHealth((int?)null); _projectedByWhiteSlot = false; } } } public sealed class HunterRageBehavior : IRageBehavior { private readonly HunterBalanceOptions _balance; private readonly Func<bool> _gateActive; public string CrestId => "hunter"; public double SuperRageDurationSeconds => _balance.SuperRageDurationSeconds; public int AttacksPerBonus => _balance.SuperRageHitsPerBonus; public HunterRageBehavior(HunterBalanceOptions balance, Func<bool>? gateActive = null) { _balance = balance ?? throw new ArgumentNullException("balance"); _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive()) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.RefreshSuperRage; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { return RageDamageResponse.Keep; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } public sealed record RageToolOptions(string ToolName); public static class RageBinder { public static RageToolOptions BindTool(ConfigFile config) { return new RageToolOptions(config.Bind<string>("Rage", "ToolName", "Fury of the Fallen", "White tool that gates all rage effects (normal rage + super rage / rage plus).").Value); } } public sealed class RageFeature : IDisposable { private readonly ManualLogSource _log; private readonly GameEventHub _events; private readonly IGameApi _game; private readonly RageBehaviorRegistry _behaviors; private readonly RageBalanceOptions _options; private readonly Func<bool> _isRageToolEquipped; private readonly Func<HealEvent, bool> _healInterceptor; private RageStateMachine _machine; private RageSnapshot _lastSnapshot; private bool _started; private readonly bool _enabled; private int _lastMaxHealth = -1; private RageMode _lastMode; private bool _rageToolGateActive = true; public RageMode CurrentMode => _machine.Current; public RageSnapshot Snapshot => _machine.Snapshot; public event Action<RageSnapshot>? Changed; public RageFeature(ManualLogSource log, GameEventHub events, IGameApi game, RageBehaviorRegistry behaviors, RageBalanceOptions options, Func<bool>? isRageToolEquipped = null) { _log = log ?? throw new ArgumentNullException("log"); _events = events ?? throw new ArgumentNullException("events"); _game = game ?? throw new ArgumentNullException("game"); _behaviors = behaviors ?? throw new ArgumentNullException("behaviors"); _options = options ?? throw new ArgumentNullException("options"); _enabled = options.Enabled; _isRageToolEquipped = isRageToolEquipped ?? ((Func<bool>)(() => true)); _healInterceptor = OnHealIntercept; IRageBehavior behavior = behaviors.Get(game.CurrentCrestId) ?? new DefaultRageBehavior(options); _machine = new RageStateMachine(behavior, OnModeChanged); _lastSnapshot = _machine.Snapshot; } public void Start() { if (!_started) { if (!_enabled) { _log.LogInfo((object)"[Rage] disabled by configuration (Rage.Enabled=false)."); return; } _started = true; _events.CrestChanged += OnCrestChanged; _events.PlayerDamageAfter += OnPlayerDamaged; _events.HealthChanged += OnHealthChanged; _events.EnemyDamageResolved += OnEnemyDamageResolved; _events.BenchRested += OnBenchRested; _events.PlayerDied += OnPlayerDied; _events.SceneChanged += OnSceneChanged; _events.ToolEquipsChanged += OnToolEquipsChanged; _events.HealInterceptor = _healInterceptor; _rageToolGateActive = SafeRageToolEquipped(); Refresh(); } } public void Tick(double deltaSeconds) { if (_enabled) { Refresh(deltaSeconds); } } public void Dispose() { if (_started) { _started = false; _events.CrestChanged -= OnCrestChanged; _events.PlayerDamageAfter -= OnPlayerDamaged; _events.HealthChanged -= OnHealthChanged; _events.EnemyDamageResolved -= OnEnemyDamageResolved; _events.BenchRested -= OnBenchRested; _events.PlayerDied -= OnPlayerDied; _events.SceneChanged -= OnSceneChanged; _events.ToolEquipsChanged -= OnToolEquipsChanged; if ((object)_events.HealInterceptor == _healInterceptor) { _events.HealInterceptor = null; } _machine.Reset((ResetReason)3); PublishIfChanged(); } } private bool OnHealIntercept(HealEvent heal) { RageContext context = BuildContext(); RageHealDecision rageHealDecision = _machine.HandleHealRequest(context, new RageHealRequest(heal.Requested, heal.SourceTag)); switch (rageHealDecision) { case RageHealDecision.RefreshSuperRage: _log.LogDebug((object)$"[Rage] heal consumed to refresh super rage duration (amount={heal.Requested})."); break; case RageHealDecision.ExitSuperRage: _log.LogDebug((object)$"[Rage] heal consumed to exit super rage (amount={heal.Requested})."); break; } Refresh(); return rageHealDecision != RageHealDecision.Allow; } private void OnCrestChanged(CrestChangedEvent e) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 if ((int)_machine.Current == 2) { Refresh(); return; } IRageBehavior rageBehavior = _behaviors.Get(e.Current) ?? new DefaultRageBehavior(_options); if (!string.Equals(rageBehavior.CrestId, _machine.BehaviorCrestId, StringComparison.OrdinalIgnoreCase)) { _machine.Reset((ResetReason)0); _machine = new RageStateMachine(rageBehavior, OnModeChanged); _log.LogInfo((object)("[Rage] behavior switched to '" + rageBehavior.CrestId + "' for crest '" + e.Current + "'.")); } Refresh(); } private void OnPlayerDamaged(PlayerDamageEvent e) { if (!e.Cancelled) { _machine.HandlePlayerDamaged(BuildContext(), new RageDamageEvent(e.MaskDamage, e.SourceTag)); Refresh(); } } private void OnHealthChanged(HealthChangedEvent e) { Refresh(); } private void OnEnemyDamageResolved(AttackEvent e) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 if (e.IsHeroDamage && !e.IsAdditionalDamage && (int)_machine.Current == 2) { _machine.OnAttackResolved(BuildContext()); Refresh(); } } private void OnBenchRested() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 if ((int)_machine.Current != 2) { Reset((ResetReason)1); } } private void OnPlayerDied() { Reset((ResetReason)3); } private void OnSceneChanged(string scene) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 if ((int)_machine.Current != 2) { Reset((ResetReason)5); } } private void OnToolEquipsChanged() { _rageToolGateActive = SafeRageToolEquipped(); if (_rageToolGateActive) { Refresh(); } else { Reset((ResetReason)8); } } private bool SafeRageToolEquipped() { try { return _isRageToolEquipped(); } catch (Exception ex) { _log.LogWarning((object)("[Rage] rage-tool gate query failed: " + ex.Message)); return false; } } private void Reset(ResetReason reason) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) _machine.Reset(reason); Refresh(); } private void Refresh(double deltaSeconds = 0.0) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) RageContext rageContext = BuildContext(); RageMode current = _machine.Current; _machine.Update(rageContext, deltaSeconds); _lastMaxHealth = rageContext.MaxHealth; for (int i = 0; i < 3; i++) { if (_machine.Current == current) { break; } current = _machine.Current; _machine.Update(rageContext, 0.0); } PublishIfChanged(); } private RageContext BuildContext() { int num = Math.Max(1, _game.MaxHealth); bool maxHealthChanged = num != _lastMaxHealth; return new RageContext(_game.CurrentHealth, num, 0, IsEnhancing: false, maxHealthChanged, _rageToolGateActive); } private void OnModeChanged(RageMode mode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_000b: Invalid comparison between Unknown and I4 if ((int)mode == 2 && (int)_lastMode != 2) { _game.ClearBlueHealth("rage.super-rage"); } _lastMode = mode; _log.LogInfo((object)$"[Rage] mode changed: {mode} (crest={_machine.BehaviorCrestId})"); } private void PublishIfChanged() { RageSnapshot snapshot = _machine.Snapshot; if (!(snapshot == _lastSnapshot)) { _lastSnapshot = snapshot; this.Changed?.Invoke(snapshot); } } } public sealed class ReaperRageBehavior : IRageBehavior { private readonly Func<bool> _gateActive; public string CrestId => "reaper"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public ReaperRageBehavior(Func<bool>? gateActive = null) { _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive()) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.Allow; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { return RageDamageResponse.Keep; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } public sealed class ShamanRageBehavior : IRageBehavior { private readonly Func<bool> _gateActive; public string CrestId => "spell"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public ShamanRageBehavior(Func<bool>? gateActive = null) { _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive()) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.Allow; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { return RageDamageResponse.Keep; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } public sealed class WandererRageBehavior : IRageBehavior { private readonly Func<bool> _gateActive; public string CrestId => "wanderer"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public WandererRageBehavior(Func<bool>? gateActive = null) { _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive()) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.RefreshSuperRage; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { return RageDamageResponse.Keep; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } public sealed class WitchRageBehavior : IRageBehavior { private readonly Func<bool> _gateActive; public string CrestId => "witch"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public WitchRageBehavior(Func<bool>? gateActive = null) { _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive()) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.Allow; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { return RageDamageResponse.Keep; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } } namespace CrestsEvolve.Core.Rage { public sealed record RageContext(int Health, int MaxHealth, int CurrentStacks, bool IsEnhancing, bool MaxHealthChanged = false, bool RageToolGateActive = true); public sealed record RageSnapshot(RageMode Mode, string BehaviorCrestId, double RemainingSeconds, int AttackCount, int BonusCount, long Revision) { [CompilerGenerated] public void Deconstruct(out RageMode Mode, out string BehaviorCrestId, out double RemainingSeconds, out int AttackCount, out int BonusCount, out long Revision) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected I4, but got Unknown Mode = (RageMode)(int)this.Mode; BehaviorCrestId = this.BehaviorCrestId; RemainingSeconds = this.RemainingSeconds; AttackCount = this.AttackCount; BonusCount = this.BonusCount; Revision = this.Revision; } } public enum RageHealDecision { Allow, RefreshSuperRage, ExitSuperRage, Consume } public enum RageDamageResponse { Keep, ResetProgress, Exit } public sealed record RageHealRequest(int Amount, string SourceTag); public sealed record RageDamageEvent(int MaskDamage, string SourceTag); public static class RageRules { public static bool IsOneMask(RageContext context) { if (context.Health > 0) { return context.Health <= 1; } return false; } public static bool IsMaxHealthOne(RageContext context) { return context.MaxHealth <= 1; } public static bool CanAutoSuperRage(RageContext context) { if (IsOneMask(context)) { return IsMaxHealthOne(context); } return false; } } public interface IRageBehavior { string CrestId { get; } double SuperRageDurationSeconds { get; } int AttacksPerBonus { get; } bool CanEnterRage(RageContext context); bool CanEnterSuperRage(RageContext context); bool ShouldExit(RageContext context); bool ShouldResetOn(ResetReason reason); RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal); RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage); void OnEnterRage(RageContext context); void OnEnterSuperRage(RageContext context); void OnExit(RageMode previous, RageContext context); void Tick(double deltaSeconds, RageContext context); } public sealed class DefaultRageBehavior : IRageBehavior { private readonly bool _autoSuperRageEnabled; private readonly bool _resetBonusOnDamage; private readonly double _superRageDurationSeconds; private readonly int _attacksPerBonus; private readonly Func<bool> _gateActive; public string CrestId => "default"; public double SuperRageDurationSeconds => _superRageDurationSeconds; public int AttacksPerBonus => _attacksPerBonus; public DefaultRageBehavior(bool autoSuperRageEnabled = true, bool resetBonusOnDamage = false, double superRageDurationSeconds = 10.0, int attacksPerBonus = 6, Func<bool>? gateActive = null) { if (superRageDurationSeconds <= 0.0) { throw new ArgumentOutOfRangeException("superRageDurationSeconds"); } if (attacksPerBonus <= 0) { throw new ArgumentOutOfRangeException("attacksPerBonus"); } _autoSuperRageEnabled = autoSuperRageEnabled; _resetBonusOnDamage = resetBonusOnDamage; _superRageDurationSeconds = superRageDurationSeconds; _attacksPerBonus = attacksPerBonus; _gateActive = gateActive ?? ((Func<bool>)(() => true)); } public DefaultRageBehavior(RageBalanceOptions options, Func<bool>? gateActive = null) : this(options.AutoSuperRageEnabled, options.ResetBonusOnDamage, options.SuperRageDurationSeconds, options.AttacksPerBonus, gateActive) { } public bool CanEnterRage(RageContext context) { if (RageRules.IsOneMask(context)) { return _gateActive(); } return false; } public bool CanEnterSuperRage(RageContext context) { if (_gateActive() && _autoSuperRageEnabled) { return RageRules.CanAutoSuperRage(context); } return false; } public bool ShouldExit(RageContext context) { if (RageRules.IsOneMask(context)) { return !_gateActive(); } return true; } public bool ShouldResetOn(ResetReason reason) { return true; } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { return RageHealDecision.Allow; } public RageDamageResponse OnPlayerDamaged(RageContext context, RageDamageEvent damage) { if (!_resetBonusOnDamage) { return RageDamageResponse.Keep; } return RageDamageResponse.ResetProgress; } public void OnEnterRage(RageContext context) { } public void OnEnterSuperRage(RageContext context) { } public void OnExit(RageMode previous, RageContext context) { } public void Tick(double deltaSeconds, RageContext context) { } } public sealed class RageBehaviorRegistry { private readonly Dictionary<string, IRageBehavior> _behaviors = new Dictionary<string, IRageBehavior>(StringComparer.OrdinalIgnoreCase); public void Register(IRageBehavior behavior) { if (behavior == null) { throw new ArgumentNullException("behavior"); } if (string.IsNullOrWhiteSpace(behavior.CrestId)) { throw new ArgumentException("Rage behavior CrestId must not be empty.", "behavior"); } if (!_behaviors.TryAdd(behavior.CrestId, behavior)) { throw new InvalidOperationException("Rage behavior already registered: " + behavior.CrestId); } } public IRageBehavior? Get(string crestId) { if (string.IsNullOrWhiteSpace(crestId)) { return null; } if (_behaviors.TryGetValue(crestId, out IRageBehavior value)) { return value; } foreach (KeyValuePair<string, IRageBehavior> behavior in _behaviors) { if (crestId.StartsWith(behavior.Key + "_", StringComparison.OrdinalIgnoreCase)) { return behavior.Value; } } return null; } } public sealed class RageStateMachine { private readonly IRageBehavior _behavior; private readonly Action<RageMode>? _onModeChanged; private RageContext _lastContext = new RageContext(0, 1, 0, IsEnhancing: false); private double _superRageRemaining; private int _attackCount; private int _bonusCount; private long _revision; private bool _suppressAutoReentry; public RageMode Current { get; private set; } public bool IsActive => (int)Current > 0; public string BehaviorCrestId => _behavior.CrestId; public double RemainingSeconds { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Current != 2) { return 0.0; } return Math.Max(0.0, _superRageRemaining); } } public int AttackCount => _attackCount; public int BonusCount => _bonusCount; public RageSnapshot Snapshot => new RageSnapshot(Current, BehaviorCrestId, RemainingSeconds, AttackCount, BonusCount, _revision); public event Action<int>? BonusReached; public RageStateMachine(IRageBehavior behavior, Action<RageMode>? onModeChanged = null) { _behavior = behavior ?? throw new ArgumentNullException("behavior"); _onModeChanged = onModeChanged; } public void Update(RageContext context, double deltaSeconds) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected I4, but got Unknown if (deltaSeconds < 0.0) { throw new ArgumentOutOfRangeException("deltaSeconds"); } bool flag = context.MaxHealthChanged || context.MaxHealth != _lastContext.MaxHealth; if (_suppressAutoReentry && (context.Health != _lastContext.Health || flag)) { _suppressAutoReentry = false; } _lastContext = context; RageMode current = Current; switch ((int)current) { case 0: if (context.RageToolGateActive && _behavior.CanEnterRage(context)) { EnterRage(context); } break; case 1: if (!context.RageToolGateActive) { Exit(context); } else if (_behavior.ShouldExit(context)) { Exit(context); } else if (flag && _behavior.CanEnterSuperRage(context) && !_suppressAutoReentry) { EnterSuperRage(context); } else { _behavior.Tick(deltaSeconds, context); } break; case 2: if (!context.RageToolGateActive) { Exit(context); } else if (!RageRules.IsMaxHealthOne(context)) { Exit(context); } else { _behavior.Tick(deltaSeconds, context); } break; } } public void OnAttackResolved(RageContext? context = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Current == 2) { if (context != null) { _lastContext = context; } _attackCount++; if (_behavior.AttacksPerBonus > 0 && _attackCount % _behavior.AttacksPerBonus == 0) { _bonusCount++; _revision++; this.BonusReached?.Invoke(_bonusCount); } } } public RageHealDecision HandleHealRequest(RageContext context, RageHealRequest heal) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 _lastContext = context; if ((int)Current != 2) { return RageHealDecision.Allow; } RageHealDecision rageHealDecision = _behavior.HandleHealRequest(context, heal); switch (rageHealDecision) { case RageHealDecision.RefreshSuperRage: _superRageRemaining = _behavior.SuperRageDurationSeconds; _revision++; break; case RageHealDecision.ExitSuperRage: Exit(context); break; } return rageHealDecision; } public void HandlePlayerDamaged(RageContext context, RageDamageEvent damage) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)Current != 0) { switch (_behavior.OnPlayerDamaged(context, damage)) { case RageDamageResponse.ResetProgress: ResetProgress(); break; case RageDamageResponse.Exit: Exit(context); break; } } } public void Reset(ResetReason reason) { //IL_0001: 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) if ((int)Current != 0 && _behavior.ShouldResetOn(reason)) { _suppressAutoReentry = false; Exit(_lastContext); } } public void ResetProgress() { if (_attackCount != 0 || _bonusCount != 0) { _attackCount = 0; _bonusCount = 0; _revision++; } } private void EnterRage(RageContext context) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) Current = (RageMode)1; _revision++; _behavior.OnEnterRage(context); _onModeChanged?.Invoke(Current); } private void EnterSuperRage(RageContext context) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) Current = (RageMode)2; _superRageRemaining = _behavior.SuperRageDurationSeconds; _attackCount = 0; _bonusCount = 0; _suppressAutoReentry = false; _revision++; _behavior.OnEnterSuperRage(context); _onModeChanged?.Invoke(Current); } private void Exit(RageContext context, bool expired = false) { //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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) RageMode current = Current; Current = (RageMode)0; _superRageRemaining = 0.0; _attackCount = 0; _bonusCount = 0; if (expired) { _suppressAutoReentry = true; } _revision++; _behavior.OnExit(current, context); _onModeChanged?.Invoke(Current); } } } namespace CrestsEvolve.RageOfTheGoner { [BepInPlugin("local.crestsevolve.rageofthegoner", "RAGE OF THE GONER", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "local.crestsevolve.rageofthegoner"; public const string PluginName = "RAGE OF THE GONER"; public const string PluginVersion = "0.1.0"; private Harmony? _harmony; private RageBootstrap? _bootstrap; private void Awake() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _harmony = new Harmony("local.crestsevolve.rageofthegoner"); _bootstrap = new RageBootstrap(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config, _harmony); _bootstrap.Start(); } private void OnDestroy() { _bootstrap?.Dispose(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace CrestsEvolve.RageOfTheGoner.Bootstrap { public sealed class RageBootstrap : IDisposable { private sealed class RageIconDriver : IStatusIconDriver, IDisposable { private readonly RageFeature _rage; private readonly RageStatusDriver _driver; public RageIconDriver(RageFeature rage) { _rage = rage; _driver = new RageStatusDriver(rage, FoundationServices.Current.StatusLedger, FoundationServices.Current.Events, () => RageToolBridge.IsActive()); } public void Start(string currentCrestId) { _driver.Start(); } public void Dispose() { _driver.Dispose(); } public void RefreshEquipped() { _driver.RefreshEquipped(); } } private sealed class RageProjectionAdapter : IRageProjection, IDisposable { private readonly RageFeature _rage; public RageProjectionData? Current { get { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown RageSnapshot snapshot = _rage.Snapshot; return new RageProjectionData((int)snapshot.Mode == 1 || (int)snapshot.Mode == 2, (int)snapshot.Mode == 2, snapshot.RemainingSeconds, snapshot.Revision); } } public event Action? Changed; public RageProjectionAdapter(RageFeature rage) { _rage = rage; _rage.Changed += OnChanged; } private void OnChanged(RageSnapshot snapshot) { this.Changed?.Invoke(); } public void Dispose() { _rage.Changed -= OnChanged; } } private readonly ManualLogSource _log; private readonly ConfigFile _config; private readonly Harmony _harmony; private RageFeature? _rage; private RageProjectionAdapter? _projection; private FuryWhiteSlotDriver? _furyWhiteSlot; private GameObject? _witchRage2Go; private bool _disposed; public RageBootstrap(ManualLogSource log, ConfigFile config, Harmony harmony) { _log = log; _config = config; _harmony = harmony; } public void Start() { //IL_0750: Unknown result type (might be due to invalid IL or missing references) //IL_075a: Expected O, but got Unknown FoundationServices services = FoundationServices.Current; if (services == null) { _log.LogWarning((object)"[RageOfTheGoner] foundation not loaded; rage disabled."); return; } FoundationServices obj = services; BalanceOptions obj2 = services.Balance.<Clone>$(); obj2.set_Hunter(BalanceBinder.BindHunter(_config)); obj2.set_Rage(BalanceBinder.BindRage(_config)); obj.Balance = obj2; RageToolOptions rageToolOptions = RageBinder.BindTool(_config); bool rageToolWarned = false; RageToolBridge.IsActive = delegate { try { return WhiteToolSystem.IsEquippedAnywhere(rageToolOptions.ToolName); } catch (Exception ex) { if (!rageToolWarned) { rageToolWarned = true; _log.LogWarning((object)("[Rage] rage tool gate query failed: " + ex.Message)); } return false; } }; RageBehaviorRegistry rageBehaviorRegistry = new RageBehaviorRegistry(); rageBehaviorRegistry.Register(new DefaultRageBehavior(services.Balance.Rage.AutoSuperRageEnabled, services.Balance.Rage.ResetBonusOnDamage, services.Balance.Rage.SuperRageDurationSeconds, services.Balance.Rage.AttacksPerBonus, () => RageToolBridge.IsActive())); rageBehaviorRegistry.Register(new HunterRageBehavior(services.Balance.Hunter, rageGate(() => true))); rageBehaviorRegistry.Register(new ReaperRageBehavior(rageGate(() => true))); rageBehaviorRegistry.Register(new WandererRageBehavior(rageGate(() => true))); rageBehaviorRegistry.Register(new BeastRageBehavior(rageGate(() => true))); rageBehaviorRegistry.Register(new WitchRageBehavior(rageGate(() => true))); rageBehaviorRegistry.Register(new ArchitectRageBehavior(rageGate(() => true))); rageBehaviorRegistry.Register(new ShamanRageBehavior(rageGate(() => true))); rageBehaviorRegistry.Register(new CursedRageBehavior(rageGate(() => string.Equals(services.Game.CurrentCrestId, "Cursed", StringComparison.OrdinalIgnoreCase)))); rageBehaviorRegistry.Register(new CloaklessRageBehavior(rageGate(() => string.Equals(services.Game.CurrentCrestId, "Cloakless", StringComparison.OrdinalIgnoreCase)))); _rage = new RageFeature(_log, services.Events, services.Game, rageBehaviorRegistry, services.Balance.Rage, () => RageToolBridge.IsActive()); WandererRageBridge.IsActive = () => _rage != null && ((int)_rage.CurrentMode == 1 || (int)_rage.CurrentMode == 2) && string.Equals(_rage.Snapshot.BehaviorCrestId, "wanderer", StringComparison.OrdinalIgnoreCase); WandererRageBridge.DamageMultiplier = () => services.Balance.Rage.WandererDamageMultiplier; BeastRageBridge.SetRageActive = delegate { services.BeastState.SetRageStateActive(true); }; BeastRageBridge.SetHealBlocked = delegate(bool blocked) { services.BeastState.SetRageHealBlocked(blocked); }; WitchRageBridge.IsActive = () => _rage != null && ((int)_rage.CurrentMode == 1 || (int)_rage.CurrentMode == 2) && string.Equals(_rage.Snapshot.BehaviorCrestId, "witch", StringComparison.OrdinalIgnoreCase); WitchRageBridge.DashDamageMultiplier = () => services.Balance.Rage.WitchDashDamageMultiplier; WitchRageBridge.DashSilkPerHit = () => (_rage != null && (int)_rage.CurrentMode == 2) ? services.Balance.Rage.WitchDashSilkPerHit : 0; WitchRageBridge.BindDamageMultiplier = () => (_rage == null || (int)_rage.CurrentMode != 2) ? 1.5 : services.Balance.Rage.WitchSuperRageBindDamageMultiplier; WitchRageBridge.BindRangeMultiplier = () => (_rage == null || (int)_rage.CurrentMode != 2) ? 1.5 : services.Balance.Rage.WitchSuperRageBindRangeMultiplier; WitchRageBridge.SawbladeCritChance = () => services.Balance.Rage.WitchSawbladeCritChance; WitchRageBridge.SawbladeToolId = () => services.Balance.Rage.WitchSawbladeToolId; WitchRageBridge.IsSuperRageActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "witch", StringComparison.OrdinalIgnoreCase); ArchitectRageBridge.IsActive = () => _rage != null && ((int)_rage.CurrentMode == 1 || (int)_rage.CurrentMode == 2) && string.Equals(_rage.Snapshot.BehaviorCrestId, "toolmaster", StringComparison.OrdinalIgnoreCase); ArchitectRageBridge.ToolDamageMultiplier = () => services.Balance.Rage.ArchitectToolDamageMultiplier; ArchitectRageBridge.IsSuperRageActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "toolmaster", StringComparison.OrdinalIgnoreCase); ArchitectRageBridge.ToolSilkPerHit = () => services.Balance.Rage.ArchitectToolSilkPerHit; ShamanRageBridge.IsActive = () => _rage != null && ((int)_rage.CurrentMode == 1 || (int)_rage.CurrentMode == 2) && string.Equals(_rage.Snapshot.BehaviorCrestId, "spell", StringComparison.OrdinalIgnoreCase); ShamanRageBridge.CritChance = () => services.Balance.Rage.ShamanCritChance; ShamanRageBridge.WaveSilkPerHit = () => services.Balance.Rage.ShamanWaveSilkPerHit; ShamanRageBridge.IsSuperRageActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "spell", StringComparison.OrdinalIgnoreCase); ShamanRageBridge.SkillCostReduction = () => services.Balance.Rage.ShamanSkillCostReduction; CursedRageBridge.IsActive = () => _rage != null && ((int)_rage.CurrentMode == 1 || (int)_rage.CurrentMode == 2) && string.Equals(_rage.Snapshot.BehaviorCrestId, "cursed", StringComparison.OrdinalIgnoreCase); CursedRageBridge.IsSuperRageActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "cursed", StringComparison.OrdinalIgnoreCase); CursedRageBridge.SkillDamageMultiplier = () => services.Balance.Rage.CursedSkillDamageMultiplier; CloaklessRageBridge.IsActive = () => _rage != null && ((int)_rage.CurrentMode == 1 || (int)_rage.CurrentMode == 2) && string.Equals(_rage.Snapshot.BehaviorCrestId, "cloakless", StringComparison.OrdinalIgnoreCase); CloaklessRageBridge.IsSuperRageActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "cloakless", StringComparison.OrdinalIgnoreCase); CloaklessRageBridge.BlockChancePercent = () => (int)Math.Round(services.Balance.Rage.CloaklessBlockChance * 100.0); CloaklessRageBridge.CritChance = () => services.Balance.Rage.CloaklessCritChance; HunterRageBridge.IsActive = () => _rage != null && ((int)_rage.CurrentMode == 1 || (int)_rage.CurrentMode == 2) && string.Equals(_rage.Snapshot.BehaviorCrestId, "hunter", StringComparison.OrdinalIgnoreCase); HunterRageBridge.IsSuperRageActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "hunter", StringComparison.OrdinalIgnoreCase); HunterRageBridge.ComboLayerCount = () => services.HunterState.CurrentMeterHits(); HunterRageBridge.SilkPerHit = () => services.Balance.Rage.HunterSilkPerHit; HunterRageBridge.FocusThresholdRage = () => services.Balance.Rage.HunterFocusThresholdRage; HunterRageBridge.FocusThresholdSuperRage = () => services.Balance.Rage.HunterFocusThresholdSuperRage; ReaperRageBridge.IsActive = () => _rage != null && ((int)_rage.CurrentMode == 1 || (int)_rage.CurrentMode == 2) && string.Equals(_rage.Snapshot.BehaviorCrestId, "reaper", StringComparison.OrdinalIgnoreCase); ReaperRageBridge.IsSuperRageActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "reaper", StringComparison.OrdinalIgnoreCase); ReaperRageBridge.SilkOrbPartsMultiplier = () => services.Balance.Rage.ReaperSilkOrbPartsMultiplier; ReaperRageBridge.SpawnExplosionAt = (Vector3 position) => services.Game.SpawnNativeExplosionAt(position); BeastRageBridge.IsActive = () => _rage != null && ((int)_rage.CurrentMode == 1 || (int)_rage.CurrentMode == 2) && string.Equals(_rage.Snapshot.BehaviorCrestId, "warrior", StringComparison.OrdinalIgnoreCase); BeastRageBridge.IsSuperRageActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "warrior", StringComparison.OrdinalIgnoreCase); BeastRageBridge.AttackRangeMultiplier = () => services.Balance.Rage.BeastAttackRangeMultiplier; BeastRageBridge.AttackDamageMultiplier = () => services.Balance.Rage.BeastAttackDamageMultiplier; BeastRageBridge.AttackSpeedBonus = () => services.Balance.Rage.BeastAttackSpeedBonus; WandererRageBridge.IsSuperRageActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "wanderer", StringComparison.OrdinalIgnoreCase); WandererRageBridge.CritDamageMultiplier = () => services.Balance.Rage.WandererCritDamageMultiplier; WandererRageBridge.KnockbackMultiplier = () => services.Balance.Rage.WandererKnockbackMultiplier; WitchRageBridge.MultiHitSilkPerHit = () => services.Balance.Rage.WitchMultiHitSilkPerHit; WitchRageBridge.SuperRageMultiHitDamageMultiplier = () => services.Balance.Rage.WitchSuperRageMultiHitDamageMultiplier; WandererSuperRageBridge.IsActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "wanderer", StringComparison.OrdinalIgnoreCase); WandererSuperRageBridge.BonusCount = () => (_rage != null) ? _rage.Snapshot.BonusCount : 0; WandererSuperRageBridge.CritPerBonus = () => services.Balance.Rage.WandererSuperRageCritPerBonus; ReaperSuperRageBridge.IsActive = () => _rage != null && (int)_rage.CurrentMode == 2 && string.Equals(_rage.Snapshot.BehaviorCrestId, "reaper", StringComparison.OrdinalIgnoreCase); RegisterIcons(services); RageIconDriver rageIconDriver = new RageIconDriver(_rage); services.StatusIcons.RegisterDriver((IStatusIconDriver)(object)rageIconDriver); services.StatusIcons.AddTick((Action<float>)delegate(float delta) { _rage.Tick(delta); }); services.StatusIcons.AddTick((Action<float>)delegate { rageIconDriver.RefreshEquipped(); }); if (StatusIconFeature.ShouldStart(services.StatusOptions)) { _rage.Start(); } else { _log.LogInfo((object)"[Rage] status-icon master switch disabled; rage not started (vanilla behavior)."); } _projection = new RageProjectionAdapter(_rage); services.Runtime.AttachRage((IRageProjection)(object)_projection); RegisterRageProbe(); InstallPatches(services); _witchRage2Go = new GameObject("CrestsEvolveWitchRage2"); Object.DontDestroyOnLoad((Object)(object)_witchRage2Go); _witchRage2Go.AddComponent<WitchRage2BindRangeScaler>(); services.StatusIcons.Start(services.Game.CurrentCrestId); _furyWhiteSlot = new FuryWhiteSlotDriver(_log, services.Events, services.Game, () => WhiteToolSystem.IsEquippedInWhiteSlot(rageToolOptions.ToolName), () => services.Balance.Rage.Enabled && StatusIconFeature.ShouldStart(services.StatusOptions)); _furyWhiteSlot.Start(); _log.LogInfo((object)"[RageOfTheGoner] rage wired."); static Func<bool> rageGate(Func<bool> gate) { return () => gate() && RageToolBridge.IsActive(); } } private void RegisterIcons(FoundationServices services) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown Dictionary<string, StatusIconEntryOptions> dictionary = new Dictionary<string, StatusIconEntryOptions>(StringComparer.Ordinal); string[] array = new string[3] { "rage.fury", "hunter.super-rage", "wanderer.super-rage" }; foreach (string key in array) { IconDefault val = StatusIconDefaults.All.First((IconDefault d) => d.Key == key); StatusIconEntryOptions val2 = StatusIconBinder.BindIcon(_config, "UI.StatusIcons." + key, key, new StatusIconEntryOptions(true, val.Max, val.ColorHex, val.Texture ?? "placeholder", val.IconColorHex)); dictionary[key] = val2; services.StatusIcons.RegisterIcon(new StatusIconDefinition(key, "rage", val2.Max)); } services.StatusOptions.RegisterIconOptions((IEnumerable<KeyValuePair<string, StatusIconEntryOptions>>)dictionary); } private void RegisterRageProbe() { if (_rage != null) { _rage.Changed += delegate(RageSnapshot snapshot) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 _log.LogInfo((object)($"[Rage][probe] snapshot mode={snapshot.Mode} bonus={snapshot.BonusCount} " + $"remaining={snapshot.RemainingSeconds:0.0} (rage.fury current={(((int)snapshot.Mode == 2) ? 1 : 0)}/1)")); }; } } private void InstallPatches(FoundationServices services) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Expected O, but got Unknown //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Expected O, but got Unknown //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Expected O, but got Unknown //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Expected O, but got Unknown //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Expected O, but got Unknown //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Expected O, but got Unknown //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Expected O, but got Unknown //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Expected O, but got Unknown //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Expected O, but got Unknown //IL_043b: Unknown result type (might be due to invalid IL or missing references) //IL_0448: Expected O, but got Unknown //IL_050a: Unknown result type (might be due to invalid IL or missing references) //IL_0517: Expected O, but got Unknown //IL_056c: Unknown result type (might be due to invalid IL or missing references) //IL_0579: Expected O, but got Unknown //IL_05fa: Unknown result type (might be due to invalid IL or missing references) //IL_0608: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(HealthManager), "Hit", new Type[1] { typeof(HitInstance) }, (Type[])null); if (methodInfo != null) { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ArchitectRage1), "DamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ShamanRage1), "WaveSilkPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(CursedRage1), "SkillDamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(CloaklessRage2), "CritPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(WandererRage1), "DamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(BeastRage2), "DamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(WitchRage1), "SilkPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(WitchRage2), "DamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ArchitectRage2), "SilkPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(HunterRage1), "SilkPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(HunterRage2), "SilkPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ReaperRage1), "CritExplosionPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { _log.LogWarning((object)"[Rage] HealthManager.Hit not found; architect/shaman/cursed/cloakless hit effects disabled."); } MethodInfo methodInfo2 = AccessTools.PropertyGetter(typeof(PlayerData), "SilkSkillCost"); if (methodInfo2 != null) { _harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(ShamanRage2), "SilkCostPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(CursedRage2), "NoCostPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { _log.LogWarning((object)"[Rage] PlayerData.SilkSkillCost not found; shaman/cursed cost effects disabled."); } MethodInfo methodInfo3 = AccessTools.Method(typeof(HeroController), "BindCompleted", (Type[])null, (Type[])null); if (methodInfo3 != null) { _harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(ReaperRage1), "BindCritPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo4 = AccessTools.Method(typeof(HeroController), "AddSilkParts", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo4 != null) { _harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(ReaperRage2), "SilkPartsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo5 = AccessTools.Method(typeof(HeroController), "Dash", (Type[])null, (Type[])null); if (methodInfo5 != null) { _harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, new HarmonyMethod(typeof(WitchRage1), "DashPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo6 = AccessTools.PropertyGetter(typeof(Gameplay), "WandererCritMultiplier"); if (methodInfo6 != null) { _harmony.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, new HarmonyMethod(typeof(WandererRage2), "CritMultiplierPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo7 = AccessTools.PropertyGetter(typeof(Gameplay), "WeightedAnkletDmgKnockbackMult"); if (methodInfo7 != null) { _harmony.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, new HarmonyMethod(typeof(WandererRage2), "KnockbackMultiplierPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo8 = AccessTools.Method(typeof(NailAttackBase), "OnSlashStarting", (Type[])null, (Type[])null); if (methodInfo8 != null) { _harmony.Patch((MethodBase)methodInfo8, (HarmonyMethod)null, new HarmonyMethod(typeof(BeastRage1), "RangePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo[] array = new MethodInfo[6] { AccessTools.PropertyGetter(typeof(HeroControllerConfig), "AttackDuration"), AccessTools.PropertyGetter(typeof(HeroControllerConfig), "AttackRecoveryTime"), AccessTools.PropertyGetter(typeof(HeroControllerConfig), "AttackCooldownTime"), AccessTools.PropertyGetter(typeof(HeroControllerConfig), "QuickAttackCooldownTime"), AccessTools.PropertyGetter(typeof(HeroController), "CurrentNailChargeTime"), AccessTools.PropertyGetter(typeof(HeroController), "CurrentNailChargeBeginTime") }; foreach (MethodInfo methodInfo9 in array) { if (!(methodInfo9 == null)) { _harmony.Patch((MethodBase)methodInfo9, (HarmonyMethod)null, new HarmonyMethod(typeof(BeastRage2), "AttackSpeedPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } CloaklessRage1.Configure(_log); MethodInfo methodInfo10 = AccessTools.Method(typeof(HeroController), "GetLuckyDiceShieldThreshold", (Type[])null, (Type[])null); if (methodInfo10 != null) { _harmony.Patch((MethodBase)methodInfo10, (HarmonyMethod)null, new HarmonyMethod(typeof(CloaklessRage1), "LuckyDiceThresholdPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo11 = AccessTools.Method(typeof(HeroController), "TakeDamage", new Type[5] { typeof(GameObject), typeof(CollisionSide), typeof(int), typeof(HazardType), typeof(DamagePropertyFlags) }, (Type[])null); if (methodInfo11 != null) { _harmony.Patch((MethodBase)methodInfo11, new HarmonyMethod(typeof(CloaklessRage1), "TakeDamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { _log.LogWarning((object)"[Rage] HeroController.TakeDamage not found; cloakless block disabled."); } } public void Dispose() { if (!_disposed) { _disposed = true; FoundationServices current = FoundationServices.Current; if (current != null) { current.Runtime.AttachRage((IRageProjection)null); } if ((Object)(object)_witchRage2Go != (Object)null) { Object.Destroy((Object)(object)_witchRage2Go); } _furyWhiteSlot?.Dispose(); _rage?.Dispose(); _projection?.Dispose(); RageToolBridge.IsActive = () => false; _harmony.UnpatchSelf(); } } } }
CrestsEvolve.ToolsAscension/CrestsEvolve.ToolsAscension.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CrestsEvolve.Core.Balance; using CrestsEvolve.Core.Events; using CrestsEvolve.Core.State; using CrestsEvolve.Core.Tools; using CrestsEvolve.Core.UI; using CrestsEvolve.Features; using CrestsEvolve.Features.StatusIcons; using CrestsEvolve.Features.Tools; using CrestsEvolve.Features.Tools.Blue; using CrestsEvolve.Features.Tools.Yellow; using CrestsEvolve.GameInterop; using CrestsEvolve.GameInterop.Patches; using CrestsEvolve.ToolsAscension.Bootstrap; using CrestsEvolve.ToolsAscension.Features.Tools; using GlobalEnums; using GlobalSettings; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using Microsoft.CodeAnalysis; using Silksong.FsmUtil; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("CrestsEvolve.Core.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CrestsEvolve.ToolsAscension")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+757e75a2d4534028308600b7117b47c56eb48037")] [assembly: AssemblyProduct("CrestsEvolve.ToolsAscension")] [assembly: AssemblyTitle("CrestsEvolve.ToolsAscension")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } internal static class IsExternalInit { } } namespace CrestsEvolve.GameInterop { public sealed class CogflyAutoReleaseRunner : MonoBehaviour { private const float Interval = 1f; private float _timer; private void Update() { _timer += Time.deltaTime; if (!(_timer < 1f)) { _timer = 0f; CogflyAutoReleasePatch.Tick(); } } } public sealed class CogflySpawner { public const string PrefabName = "Clockwork Hatchling"; public const string ToolName = "Cogwork Flier"; public const string AltToolName = "Cogfly"; private readonly ManualLogSource _log; private GameObject? _prefab; private AssetBundle? _bundle; private FieldInfo? _toolSourceField; private bool _warnedResolve; private bool _warnedSpawn; private bool _warnedToolSource; private bool _warnedCount; private bool _bundleLoadFailed; private bool _loggedSpawn; public CogflySpawner(ManualLogSource log) { _log = log ?? throw new ArgumentNullException("log"); _toolSourceField = AccessTools.Field(typeof(ClockworkHatchling), "toolSource"); } public void OnSceneChanged() { if ((Object)(object)_bundle == (Object)null) { _prefab = null; } } public void Tick() { //IL_0085: 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_009e: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return; } ToolItem val = ToolItemManager.GetToolByName("Cogwork Flier") ?? ToolItemManager.GetToolByName("Cogfly"); if ((Object)(object)val == (Object)null) { return; } int toolStorageAmount = ToolItemManager.GetToolStorageAmount(val); int num = CountActive(); if (!CogflyAutoReleaseRules.ShouldRelease(num, toolStorageAmount)) { return; } GameObject val2 = ResolvePrefab(val); if ((Object)(object)val2 == (Object)null) { if (!_warnedResolve) { _warnedResolve = true; _log.LogWarning((object)"[CogflySpawner] 'Clockwork Hatchling' prefab not resolved; auto-release disabled."); } return; } try { Vector3 val3 = ((Component)instance).transform.position + new Vector3(1.5f, 1f, 0f); GameObject val4 = ObjectPoolExtensions.Spawn(val2, val3, Quaternion.identity); if (!((Object)(object)val4 == (Object)null)) { val4.SetActive(true); ClockworkHatchling component = val4.GetComponent<ClockworkHatchling>(); if ((Object)(object)component != (Object)null && _toolSourceField != null) { _toolSourceField.SetValue(component, val); } else if (!_warnedToolSource) { _warnedToolSource = true; _log.LogWarning((object)"[CogflySpawner] hatchling prefab missing ClockworkHatchling/toolSource; tool association skipped."); } if (!_loggedSpawn) { _loggedSpawn = true; _log.LogInfo((object)$"[CogflySpawner] spawned '{((Object)val4).name}' (active {num + 1}/{toolStorageAmount})."); } } } catch (Exception ex) { if (!_warnedSpawn) { _warnedSpawn = true; _log.LogWarning((object)("[CogflySpawner] spawn failed: " + ex.Message)); } } } private int CountActive() { try { return Object.FindObjectsByType<ClockworkHatchling>((FindObjectsSortMode)0).Count((ClockworkHatchling h) => (Object)(object)h != (Object)null && (IsCogflyHatchling(h) || _toolSourceField == null)); } catch (Exception ex) { if (!_warnedCount) { _warnedCount = true; _log.LogWarning((object)("[CogflySpawner] hatchling count failed: " + ex.Message)); } return 0; } } private bool IsCogflyHatchling(ClockworkHatchling hatchling) { if (_toolSourceField == null) { return true; } try { object? value = _toolSourceField.GetValue(hatchling); ToolItem val = (ToolItem)((value is ToolItem) ? value : null); return (Object)(object)val != (Object)null && (string.Equals(val.name, "Cogwork Flier", StringComparison.Ordinal) || string.Equals(val.name, "Cogfly", StringComparison.Ordinal)); } catch { return true; } } private GameObject? ResolvePrefab(ToolItem tool) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_prefab != (Object)null) { return _prefab; } _prefab = tool.Usage.ThrowPrefab; if ((Object)(object)_prefab != (Object)null) { return _prefab; } _prefab = LoadViaAddressables(); if ((Object)(object)_prefab == (Object)null) { _prefab = LoadViaLoadedBundles(); } if ((Object)(object)_prefab == (Object)null) { _prefab = LoadViaBundleFile(); } return _prefab; } private GameObject? LoadViaAddressables() { try { Type type = (from a in AppDomain.CurrentDomain.GetAssemblies() select a.GetType("UnityEngine.AddressableAssets.Addressables", throwOnError: false)).FirstOrDefault((Type t) => t != null); if (type == null) { return null; } MethodInfo[] array = (from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public) where m.Name == "LoadAssetAsync" && m.IsGenericMethodDefinition && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(string) select m).Take(2).ToArray(); if (array.Length != 1) { return null; } object obj = array[0].MakeGenericMethod(typeof(GameObject)).Invoke(null, new object[1] { "Clockwork Hatchling" }); if (obj == null) { return null; } object? obj2 = obj.GetType().GetMethod("WaitForCompletion", Type.EmptyTypes)?.Invoke(obj, null); return (GameObject?)((obj2 is GameObject) ? obj2 : null); } catch (Exception ex) { _log.LogWarning((object)("[CogflySpawner] Addressables load failed: " + ex.Message)); return null; } } private GameObject? LoadViaLoadedBundles() { try { foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { if (!((Object)(object)allLoadedAssetBundle == (Object)null)) { GameObject val = allLoadedAssetBundle.LoadAsset<GameObject>("Clockwork Hatchling"); if ((Object)(object)val != (Object)null) { return val; } } } } catch (Exception ex) { _log.LogWarning((object)("[CogflySpawner] loaded-bundle search failed: " + ex.Message)); } return null; } private GameObject? LoadViaBundleFile() { if (_bundleLoadFailed) { return null; } try { string text = Path.Combine(Application.streamingAssetsPath, "aa", "StandaloneWindows64", "herodynamic_assets_all.bundle"); if (!File.Exists(text)) { return null; } if ((Object)(object)_bundle == (Object)null) { _bundle = AssetBundle.LoadFromFile(text); } AssetBundle? bundle = _bundle; return (bundle != null) ? bundle.LoadAsset<GameObject>("Clockwork Hatchling") : null; } catch (Exception ex) { _bundleLoadFailed = true; _log.LogWarning((object)("[CogflySpawner] bundle load failed: " + ex.Message)); return null; } } } public static class ReserveBindBridge { public static Func<bool>? IsEquipped { get; set; } = () => false; public static Func<bool>? TryRestore { get; set; } = () => false; public static Func<bool>? TryRepairTool { get; set; } } public sealed class RevengeCrystalSpawner { public const string PrefabName = "Revenge Crystal Hornet"; public const string PrefabPath = "Assets/Prefabs/Heroes/Tools/Revenge Crystal Hornet.prefab"; public const string SpawnQueueGroupPath = "Assets/Data Assets/Spawn Queue Groups/Revenge Crystal Hornet.asset"; private const int MaxSpawnPerCall = 3; private const int SpawnQueueGroupLimit = 3; private const float SecondsBetweenStatueSpawns = 0.1f; private readonly ManualLogSource _log; private GameObject? _prefab; private AssetBundle? _prefabBundle; private AssetBundle? _spawnQueueGroupBundle; private bool _groupLimitRaised; private bool _disabled; private bool _warnedResolve; private bool _warnedSpawn; private bool _warnedBundleSearch; private bool _prefabBundleLoadFailed; private bool _spawnQueueGroupLoadFailed; public RevengeCrystalSpawner(ManualLogSource log) { _log = log ?? throw new ArgumentNullException("log"); } public void OnSceneChanged() { if ((Object)(object)_prefabBundle == (Object)null && (Object)(object)_spawnQueueGroupBundle == (Object)null) { _prefab = null; } _groupLimitRaised = false; _disabled = false; } public void Spawn(int count) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (count <= 0 || _disabled) { return; } EnsureGroupLimit(); HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { _log.LogDebug((object)"[RevengeCrystalSpawner] spawn skipped: HeroController.instance is null."); return; } GameObject val = ResolvePrefab(); if ((Object)(object)val == (Object)null) { if (!_warnedResolve) { _warnedResolve = true; _log.LogWarning((object)"[RevengeCrystalSpawner] Revenge Crystal Hornet prefab not resolved; Revenge Crystal white/L2-L3 spawn disabled."); } return; } Vector3 position = ((Component)instance).transform.position; int num = Math.Min(count, 3); _log.LogInfo((object)$"[RevengeCrystalSpawner] spawning {num} statue(s) from '{((Object)val).name}'."); for (int i = 0; i < num; i++) { try { GameObject val2 = ObjectPoolExtensions.Spawn(val, position, Quaternion.identity); if ((Object)(object)val2 != (Object)null) { val2.SetActive(true); BindHero(val2, ((Component)instance).gameObject); ResetStatueFsm(val2); } } catch (Exception ex) { if (!_warnedSpawn) { _warnedSpawn = true; _log.LogWarning((object)("[RevengeCrystalSpawner] spawn failed: " + ex.Message)); } } } } public void SpawnStaggered(int count) { if (count <= 0 || _disabled) { return; } EnsureGroupLimit(); HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { _log.LogDebug((object)"[RevengeCrystalSpawner] staggered spawn skipped: HeroController.instance is null."); return; } GameObject val = ResolvePrefab(); if ((Object)(object)val == (Object)null) { if (!_warnedResolve) { _warnedResolve = true; _log.LogWarning((object)"[RevengeCrystalSpawner] Revenge Crystal Hornet prefab not resolved; staggered spawn disabled."); } } else { int num = Math.Min(count, 3); _log.LogInfo((object)$"[RevengeCrystalSpawner] spawning {num} staggered statue(s) from '{((Object)val).name}'."); ((MonoBehaviour)instance).StartCoroutine(SpawnStaggeredRoutine(instance, val, num)); } } private IEnumerator SpawnStaggeredRoutine(HeroController hero, GameObject prefab, int amount) { Vector3 offset = default(Vector3); for (int i = 0; i < amount; i++) { if (i > 0) { yield return (object)new WaitForSeconds(0.1f); } try { GameObject val = ObjectPoolExtensions.Spawn(prefab, ((Component)hero).transform.position, Quaternion.identity); if ((Object)(object)val != (Object)null) { val.SetActive(true); BindHero(val, ((Component)hero).gameObject); ResetStatueFsm(val); ((Vector3)(ref offset))..ctor((float)(i + 1) * 0.6f, (i % 2 == 0) ? 0.5f : (-0.5f), 0f); val.AddComponent<RevengeCrystalStatueOffsetter>().Init(((Component)hero).transform, offset); } } catch (Exception ex) { if (!_warnedSpawn) { _warnedSpawn = true; _log.LogWarning((object)("[RevengeCrystalSpawner] staggered spawn failed: " + ex.Message)); } } } } private GameObject? ResolvePrefab() { if ((Object)(object)_prefab != (Object)null) { return _prefab; } if (_disabled) { return null; } _prefab = LoadViaAddressables(); if ((Object)(object)_prefab == (Object)null) { _prefab = FindUniqueRuntimeObject(); } if ((Object)(object)_prefab == (Object)null) { _prefab = LoadViaAssetBundle(); } if ((Object)(object)_prefab != (Object)null) { _log.LogInfo((object)("[RevengeCrystalSpawner] prefab resolved: " + ((Object)_prefab).name)); } return _prefab; } private GameObject? LoadViaAddressables() { try { string[] array = new string[2] { "Assets/Prefabs/Heroes/Tools/Revenge Crystal Hornet.prefab", "Revenge Crystal Hornet" }; foreach (string key in array) { object? obj = LoadAddressableObject(typeof(GameObject), key); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val != (Object)null && LooksLikeStatuePrefab(val)) { return val; } } return null; } catch (Exception ex) { _log.LogWarning((object)("[RevengeCrystalSpawner] Addressables load failed for 'Revenge Crystal Hornet': " + ex.Message)); return null; } } private object? LoadAddressableObject(Type assetType, string key) { try { Type type = (from a in AppDomain.CurrentDomain.GetAssemblies() select a.GetType("UnityEngine.AddressableAssets.Addressables", throwOnError: false)).FirstOrDefault((Type t) => t != null); if (type == null) { return null; } MethodInfo[] array = (from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public) where m.Name == "LoadAssetAsync" && m.IsGenericMethodDefinition && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(string) select m).Take(2).ToArray(); if (array.Length != 1) { return null; } object obj = array[0].MakeGenericMethod(assetType).Invoke(null, new object[1] { key }); if (obj == null) { return null; } MethodInfo method = obj.GetType().GetMethod("WaitForCompletion", Type.EmptyTypes); if (method == null) { return null; } return method.Invoke(obj, null); } catch (Exception ex) { _log.LogWarning((object)("[RevengeCrystalSpawner] Addressables load failed for '" + key + "': " + ex.Message)); return null; } } private void EnsureGroupLimit() { if (_groupLimitRaised) { return; } try { List<SpawnQueueGroup> list = ResolveSpawnQueueGroups(); if (list.Count == 0) { return; } FieldInfo field = typeof(SpawnQueueGroup).GetField("groupLimit", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) { _log.LogWarning((object)"[RevengeCrystalSpawner] SpawnQueueGroup.groupLimit field not found."); return; } foreach (SpawnQueueGroup item in list) { field.SetValue(item, 3); } _groupLimitRaised = true; _log.LogInfo((object)($"[RevengeCrystalSpawner] SpawnQueueGroup.groupLimit raised to {3} " + $"({list.Count} instance(s)).")); } catch (Exception ex) { _log.LogWarning((object)("[RevengeCrystalSpawner] failed to raise SpawnQueueGroup.groupLimit: " + ex.Message)); } } private List<SpawnQueueGroup> ResolveSpawnQueueGroups() { List<SpawnQueueGroup> list = new List<SpawnQueueGroup>(); try { SpawnQueueGroup[] array = Resources.FindObjectsOfTypeAll<SpawnQueueGroup>(); foreach (SpawnQueueGroup val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name == "Revenge Crystal Hornet") { list.Add(val); } } } catch (Exception ex) { if (!_warnedBundleSearch) { _warnedBundleSearch = true; _log.LogWarning((object)("[RevengeCrystalSpawner] runtime SpawnQueueGroup search failed: " + ex.Message)); } } if (list.Count > 0) { return list; } try { foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { if (!((Object)(object)allLoadedAssetBundle == (Object)null)) { SpawnQueueGroup val2 = allLoadedAssetBundle.LoadAsset<SpawnQueueGroup>("Assets/Data Assets/Spawn Queue Groups/Revenge Crystal Hornet.asset"); if ((Object)(object)val2 == (Object)null) { val2 = allLoadedAssetBundle.LoadAsset<SpawnQueueGroup>("Revenge Crystal Hornet"); } if ((Object)(object)val2 != (Object)null) { list.Add(val2); break; } } } } catch (Exception ex2) { if (!_warnedBundleSearch) { _warnedBundleSearch = true; _log.LogWarning((object)("[RevengeCrystalSpawner] loaded-bundle SpawnQueueGroup search failed: " + ex2.Message)); } } if (list.Count > 0) { return list; } object? obj = LoadAddressableObject(typeof(SpawnQueueGroup), "Revenge Crystal Hornet"); SpawnQueueGroup val3 = (SpawnQueueGroup)((obj is SpawnQueueGroup) ? obj : null); if (val3 != null) { list.Add(val3); return list; } if (_spawnQueueGroupLoadFailed) { return list; } try { string text = Path.Combine(Application.streamingAssetsPath, "aa", "StandaloneWindows64", "dataassets_assets_assets", "dataassets", "spawnqueuegroups.bundle"); if (!File.Exists(text)) { return list; } AssetBundle val4 = _spawnQueueGroupBundle; if ((Object)(object)val4 == (Object)null) { val4 = FindLoadedBundle(Path.GetFileName(text)); if ((Object)(object)val4 == (Object)null) { val4 = AssetBundle.LoadFromFile(text); } _spawnQueueGroupBundle = val4; } if ((Object)(object)val4 == (Object)null) { _spawnQueueGroupLoadFailed = true; return list; } SpawnQueueGroup val5 = val4.LoadAsset<SpawnQueueGroup>("Assets/Data Assets/Spawn Queue Groups/Revenge Crystal Hornet.asset"); if ((Object)(object)val5 != (Object)null) { list.Add(val5); } } catch (Exception ex3) { _spawnQueueGroupLoadFailed = true; _log.LogWarning((object)("[RevengeCrystalSpawner] SpawnQueueGroup bundle load failed: " + ex3.Message)); } return list; } private GameObject? LoadViaAssetBundle() { if (_prefabBundleLoadFailed) { return null; } try { string text = Path.Combine(Application.streamingAssetsPath, "aa", "StandaloneWindows64", "localpoolprefabs_assets_shared.bundle"); if (!File.Exists(text)) { return null; } AssetBundle val = _prefabBundle; if ((Object)(object)val == (Object)null) { val = FindLoadedBundle(Path.GetFileName(text)); if ((Object)(object)val == (Object)null) { val = AssetBundle.LoadFromFile(text); } _prefabBundle = val; } if ((Object)(object)val == (Object)null) { _prefabBundleLoadFailed = true; return null; } GameObject val2 = val.LoadAsset<GameObject>("Assets/Prefabs/Heroes/Tools/Revenge Crystal Hornet.prefab"); return ((Object)(object)val2 != (Object)null && LooksLikeStatuePrefab(val2)) ? val2 : null; } catch (Exception ex) { _prefabBundleLoadFailed = true; _log.LogWarning((object)("[RevengeCrystalSpawner] AssetBundle load failed: " + ex.Message)); return null; } } private AssetBundle? FindLoadedBundle(string fileName) { try { foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { if ((Object)(object)allLoadedAssetBundle != (Object)null && string.Equals(((Object)allLoadedAssetBundle).name, fileName, StringComparison.OrdinalIgnoreCase)) { return allLoadedAssetBundle; } } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); foreach (AssetBundle allLoadedAssetBundle2 in AssetBundle.GetAllLoadedAssetBundles()) { if ((Object)(object)allLoadedAssetBundle2 != (Object)null && ((Object)allLoadedAssetBundle2).name.IndexOf(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase) >= 0) { return allLoadedAssetBundle2; } } } catch (Exception ex) { if (!_warnedBundleSearch) { _warnedBundleSearch = true; _log.LogWarning((object)("[RevengeCrystalSpawner] loaded-bundle search failed: " + ex.Message)); } } return null; } private static bool LooksLikeStatuePrefab(GameObject candidate) { if ((Object)(object)candidate == (Object)null) { return false; } PlayMakerFSM component = candidate.GetComponent<PlayMakerFSM>(); if ((Object)(object)component == (Object)null) { return false; } if (!string.Equals(component.FsmName, "FSM", StringComparison.Ordinal)) { return false; } if (component.FsmVariables.FindFsmGameObject("Form Antic Pt") == null) { return component.FsmVariables.FindFsmGameObject("Enemy Damager") != null; } return true; } private GameObject? FindUniqueRuntimeObject() { try { GameObject[] array = (from g in Resources.FindObjectsOfTypeAll<GameObject>() where (Object)(object)g != (Object)null && ((Object)g).name == "Revenge Crystal Hornet" select g).Take(2).ToArray(); if (array.Length == 1) { return array[0]; } if (array.Length > 1) { _disabled = true; _log.LogWarning((object)"[RevengeCrystalSpawner] multiple 'Revenge Crystal Hornet' objects found; spawn disabled (ambiguous)."); } return null; } catch (Exception ex) { _log.LogWarning((object)("[RevengeCrystalSpawner] runtime object search failed: " + ex.Message)); return null; } } private static void BindHero(GameObject spawned, GameObject hero) { if ((Object)(object)spawned == (Object)null || (Object)(object)hero == (Object)null) { return; } PlayMakerFSM component = spawned.GetComponent<PlayMakerFSM>(); if (!((Object)(object)component == (Object)null)) { FsmGameObject val = component.FsmVariables.FindFsmGameObject("Hero") ?? FsmVariables.GlobalVariables.FindFsmGameObject("Hero"); if (val != null) { val.Value = hero; } } } private static void ResetStatueFsm(GameObject statue) { if ((Object)(object)statue == (Object)null) { return; } PlayMakerFSM component = statue.GetComponent<PlayMakerFSM>(); if ((Object)(object)component == (Object)null) { return; } try { ((object)component).GetType().GetMethod("SetState", new Type[1] { typeof(string) })?.Invoke(component, new object[1] { "Init Wait" }); } catch (Exception ex) { Debug.LogWarning((object)("[RevengeCrystalSpawner] FSM reset failed: " + ex.Message)); } } } public sealed class RevengeCrystalStatueOffsetter : MonoBehaviour { private Transform? _hero; private Vector3 _offset; public void Init(Transform hero, Vector3 offset) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) _hero = hero; _offset = offset; ((MonoBehaviour)this).StartCoroutine(ApplyOffsetAfterAppear()); } private IEnumerator ApplyOffsetAfterAppear() { yield return (object)new WaitForSeconds(1.3f); if ((Object)(object)_hero != (Object)null) { ((Component)this).transform.position = _hero.position + _offset; } } } public sealed class ShellSatchelRepairRunner : MonoBehaviour { private sealed class Entry { public ToolItem? Tool; public float ReadyTime; } private const float RepairDelaySeconds = 2f; private static readonly List<Entry> Pending = new List<Entry>(); private static bool _warned; public static void Schedule(ToolItem tool) { if ((Object)(object)tool == (Object)null) { return; } foreach (Entry item in Pending) { if ((Object)(object)item.Tool == (Object)(object)tool) { item.ReadyTime = Time.time + 2f; return; } } Pending.Add(new Entry { Tool = tool, ReadyTime = Time.time + 2f }); } public static void CancelAll() { Pending.Clear(); } private void Update() { for (int num = Pending.Count - 1; num >= 0; num--) { Entry entry = Pending[num]; if ((Object)(object)entry.Tool == (Object)null) { Pending.RemoveAt(num); } else if (!(Time.time < entry.ReadyTime)) { Pending.RemoveAt(num); TryRepair(entry.Tool); } } } private static void TryRepair(ToolItem tool) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) try { if (ShellSatchelAutoRepairPatch.IsLevelActive(ShellSatchelAutoRepairPatch.GetLevel?.Invoke("Shell Satchel") ?? 0) && (int)tool.Type == 0 && tool.SavedData.AmountLeft <= 0) { tool.CollectFree(ToolItemManager.GetToolStorageAmount(tool)); ToolItemStatesLiquid val = (ToolItemStatesLiquid)(object)((tool is ToolItemStatesLiquid) ? tool : null); if (val != null) { val.RefillRefills(false); } } } catch (Exception ex) { if (!_warned) { _warned = true; Debug.LogWarning((object)("[ShellSatchelRepair] repair failed: " + ex.Message)); } } } } public sealed class ToolEquipRefreshBridge : MonoBehaviour { private GameEventHub? _events; private ToolEquipIconDriver? _driver; public void Initialize(GameEventHub events, ToolEquipIconDriver driver) { _events = events; _driver = driver; events.CrestChanged += OnCrestChanged; events.SceneChanged += OnSceneChanged; } private void OnDestroy() { if (_events != null) { _events.CrestChanged -= OnCrestChanged; _events.SceneChanged -= OnSceneChanged; } } private void OnCrestChanged(CrestChangedEvent e) { ((MonoBehaviour)this).StartCoroutine(RefreshNextFrame()); } private void OnSceneChanged(string scene) { ((MonoBehaviour)this).StartCoroutine(RefreshNextFrame()); } private IEnumerator RefreshNextFrame() { yield return null; _driver?.Refresh(); } } } namespace CrestsEvolve.GameInterop.Patches { public static class BellBindSkillShieldPatch { private sealed class ActionCache { public bool Stored; public bool OriginalEnabled; } private sealed class SkillShieldMarker : MonoBehaviour { } private sealed class BindBellGateMarker : MonoBehaviour { } private sealed class EndBellBindShieldAction : FsmStateAction { public override void OnEnter() { try { EndShield(); } catch (Exception ex) { WarnOnce(ex); } ((FsmStateAction)this).Finish(); } } public static readonly string[] SkillShieldEvents = new string[4] { "NEEDLE THROW", "THREAD SPHERE", "PARRY", "SILK BOMB" }; private static readonly string[] EndStates = new string[4] { "N Throw Recover", "Special End", "Cancel All", "Cancel Frame" }; public static Func<string, bool>? IsEquippedInWhiteSlot; private static ManualLogSource? _log; private static bool _warned; private static FieldInfo? _bellBindFsmField; private static readonly List<PlayMakerFSM> _patchedSpecials = new List<PlayMakerFSM>(); private static readonly List<PlayMakerFSM> _patchedBind = new List<PlayMakerFSM>(); private static readonly ConditionalWeakTable<ActivateGameObject, ActionCache> _bindBellCache = new ConditionalWeakTable<ActivateGameObject, ActionCache>(); public static void Configure(ManualLogSource log) { _log = log; _warned = false; _bellBindFsmField = AccessTools.Field(typeof(HeroController), "bellBindFSM"); _patchedSpecials.Clear(); _patchedBind.Clear(); } public static bool ShouldTriggerSkillShield(string? eventName) { if (eventName != null) { return Array.IndexOf<string>(SkillShieldEvents, eventName) >= 0; } return false; } public static void OnSkillEvent(PlayMakerFSM __instance, string eventName) { try { if (ShouldTriggerSkillShield(eventName) && !((Object)(object)__instance == (Object)null) && !(__instance.FsmName != "Silk Specials")) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Bell Bind")) { ActivateShield(); } } } catch (Exception ex) { WarnOnce(ex); } } public static void OnFsmStart(PlayMakerFSM __instance) { if ((Object)(object)__instance == (Object)null || __instance.Fsm == null || (Object)(object)((Component)__instance).gameObject == (Object)null || !((Object)((Component)__instance).gameObject).name.StartsWith("Hero_Hornet", StringComparison.Ordinal)) { return; } try { if (__instance.FsmName == "Silk Specials") { InstallSkillEndActions(__instance); } else if (__instance.FsmName == "Spell Control") { InstallBindBellGate(__instance); } } catch (Exception ex) { WarnOnce(ex); } } public static void RefreshAll() { for (int num = _patchedBind.Count - 1; num >= 0; num--) { PlayMakerFSM val = _patchedBind[num]; if ((Object)(object)val == (Object)null) { _patchedBind.RemoveAt(num); } else { ApplyBindBellGate(val); } } } public static void RestoreAll() { foreach (PlayMakerFSM item in _patchedBind) { if (!((Object)(object)item == (Object)null)) { ApplyBindBellGate(item, forceInactive: true); } } _patchedBind.Clear(); _patchedSpecials.Clear(); } private static void ActivateShield() { PlayMakerFSM val = ResolveBellBindFsm(); if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { ((Component)val).gameObject.SetActive(true); ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)"[BellBindSkillShield] white-slot skill shield activated."); } } } private static void EndShield() { PlayMakerFSM val = ResolveBellBindFsm(); if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeSelf) { val.SendEvent("DISAPPEAR"); ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)"[BellBindSkillShield] white-slot skill shield ended (DISAPPEAR)."); } } } private static PlayMakerFSM? ResolveBellBindFsm() { if (_bellBindFsmField == null) { return null; } HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return null; } object? value = _bellBindFsmField.GetValue(instance); return (PlayMakerFSM?)((value is PlayMakerFSM) ? value : null); } private static void InstallSkillEndActions(PlayMakerFSM fsm) { if ((Object)(object)((Component)fsm).GetComponent<SkillShieldMarker>() != (Object)null) { return; } int num = 0; string[] endStates = EndStates; foreach (string text in endStates) { FsmState state = FsmUtil.GetState(fsm, text); if (state != null) { FsmUtil.InsertAction(state, state.Actions.Length, (FsmStateAction)(object)new EndBellBindShieldAction()); num++; } } if (num == 0) { WarnOnce(new InvalidOperationException("Silk Specials FSM end states not found (N Throw Recover/Special End/Cancel All/Cancel Frame); skill shield end disabled.")); return; } ((Component)fsm).gameObject.AddComponent<SkillShieldMarker>(); _patchedSpecials.Add(fsm); ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)$"[BellBindSkillShield] Silk Specials FSM hook installed ({num} end state(s))."); } } private static void InstallBindBellGate(PlayMakerFSM fsm) { if ((Object)(object)((Component)fsm).GetComponent<BindBellGateMarker>() != (Object)null) { ApplyBindBellGate(fsm); return; } if (!FindBindBellActivation(fsm)) { WarnOnce(new InvalidOperationException("Spell Control | Bind Bell? ActivateGameObject(Bind Bell, true) not found; bind-release gate disabled.")); return; } ((Component)fsm).gameObject.AddComponent<BindBellGateMarker>(); _patchedBind.Add(fsm); ApplyBindBellGate(fsm); ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)"[BellBindSkillShield] Spell Control | Bind Bell? gate installed (white-slot bind no shield)."); } } private static bool FindBindBellActivation(PlayMakerFSM fsm) { FsmState[] fsmStates = fsm.FsmStates; foreach (FsmState val in fsmStates) { if (val.Name != "Bind Bell?") { continue; } FsmStateAction[] actions = val.Actions; for (int j = 0; j < actions.Length; j++) { if (IsBindBellActivation(actions[j])) { return true; } } } return false; } private static bool IsBindBellActivation(FsmStateAction action) { ActivateGameObject val = (ActivateGameObject)(object)((action is ActivateGameObject) ? action : null); if (val != null && val.activate != null && val.activate.Value && val.gameObject != null && val.gameObject.GameObject != null) { return string.Equals(((NamedVariable)val.gameObject.GameObject).Name, "Bind Bell", StringComparison.Ordinal); } return false; } private static void ApplyBindBellGate(PlayMakerFSM fsm, bool forceInactive = false) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown try { bool flag = !forceInactive && IsEquippedInWhiteSlot != null && IsEquippedInWhiteSlot("Bell Bind"); FsmState[] fsmStates = fsm.FsmStates; foreach (FsmState val in fsmStates) { if (val.Name != "Bind Bell?") { continue; } FsmStateAction[] actions = val.Actions; foreach (FsmStateAction val2 in actions) { if (IsBindBellActivation(val2)) { ActionCache orCreateValue = _bindBellCache.GetOrCreateValue((ActivateGameObject)val2); if (!orCreateValue.Stored) { orCreateValue.Stored = true; orCreateValue.OriginalEnabled = val2.Enabled; } val2.Enabled = !flag && orCreateValue.OriginalEnabled; } } } } catch (Exception ex) { WarnOnce(ex); } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[BellBindSkillShield] disabled: " + ex.GetType().Name + ": " + ex.Message)); } } } } public static class BlueToolEffectPatches { private sealed class WispCooldownCache { public float Min; public float Max; } private sealed class SlashScaleCache { public Vector3 Original; } private const int SilkMaxCapacity = 36; public static Func<string, int>? GetLevel; public static Func<string, bool>? IsEquippedInWhiteSlot; public static Action<int>? SpawnRevengeCrystals; public static Action<int>? AddExtraBlueHealth; private static ManualLogSource? _log; private static bool _warned; private static FieldInfo? _moss1Hits; private static FieldInfo? _moss2Hits; private static FieldInfo? _lavaCooldown; private static FieldInfo? _taggedDamage; private static FieldInfo? _wispCooldownField; private static FieldInfo? _minMaxStartField; private static FieldInfo? _minMaxEndField; private static FieldInfo? _queuedAutoThrowTool; private static FieldInfo? _willThrowTool; private static MethodInfo? _resetMaggotCharm; private static FieldInfo? _heroCState; private static FieldInfo? _whipLashing; private static FieldInfo? _luckyDiceShieldedHits; private static object? _zeroMinMaxFloat; private static double _pendingPoisonMultiplier = 1.0; private static double _pendingSilkMultiplier = 1.0; private static int _quickSlingRemaining; private static int _revengeHealthBefore = -1; private static int _lastLoggedCompassCap = -1; private static ToolItem? _quickSlingTool; private static FieldInfo? _quickeningTimeLeft; private static FieldInfo? _imbuementTimeLeft; private static FieldInfo? _nailAttackBaseScale; private static string? _pendingWhiteSlotTool; private static double _pendingWhiteSlotMultiplier = 1.0; private static readonly ConditionalWeakTable<HeroWispLantern, WispCooldownCache> _wispCooldownCache = new ConditionalWeakTable<HeroWispLantern, WispCooldownCache>(); private static readonly ConditionalWeakTable<NailAttackBase, SlashScaleCache> _slashScales = new ConditionalWeakTable<NailAttackBase, SlashScaleCache>(); public static bool IsDiceCritWindow { get; private set; } public static void Configure(ManualLogSource log) { _log = log; _moss1Hits = AccessTools.Field(typeof(HeroController), "mossCreep1Hits"); _moss2Hits = AccessTools.Field(typeof(HeroController), "mossCreep2Hits"); _lavaCooldown = AccessTools.Field(typeof(HeroController), "lavaBellCooldownTimeLeft"); _taggedDamage = AccessTools.Field(typeof(TagDamageTaker), "taggedDamage"); _wispCooldownField = AccessTools.Field(typeof(HeroWispLantern), "wispSpawnCooldown"); Type type = Type.GetType("TeamCherry.SharedUtils.MinMaxFloat, TeamCherry.SharedUtils"); if (type != null) { _minMaxStartField = AccessTools.Field(type, "Start"); _minMaxEndField = AccessTools.Field(type, "End"); try { _zeroMinMaxFloat = Activator.CreateInstance(type); if (_zeroMinMaxFloat != null && _minMaxStartField != null && _minMaxEndField != null) { _minMaxStartField.SetValue(_zeroMinMaxFloat, 0f); _minMaxEndField.SetValue(_zeroMinMaxFloat, 0f); } } catch (Exception ex) { _log.LogWarning((object)("[BlueToolEffects] MinMaxFloat zero instance creation failed: " + ex.Message)); } } _queuedAutoThrowTool = AccessTools.Field(typeof(HeroController), "queuedAutoThrowTool"); _willThrowTool = AccessTools.Field(typeof(HeroController), "willThrowTool"); _quickeningTimeLeft = AccessTools.Field(typeof(HeroController), "quickeningTimeLeft"); _imbuementTimeLeft = AccessTools.Field(typeof(HeroNailImbuement), "imbuementTimeLeft"); _nailAttackBaseScale = AccessTools.Field(typeof(NailAttackBase), "scale"); _resetMaggotCharm = AccessTools.Method(typeof(HeroController), "ResetMaggotCharm", (Type[])null, (Type[])null); _heroCState = AccessTools.Field(typeof(HeroController), "cState"); _whipLashing = ((_heroCState == null) ? null : AccessTools.Field(_heroCState.FieldType, "whipLashing")); _luckyDiceShieldedHits = AccessTools.Field(typeof(HeroController), "luckyDiceShieldedHits"); ClearQuickSling(); ClearPendingWhiteSlot(); _revengeHealthBefore = -1; } public static double GetDamageMultiplier(string toolName) { return EffectsFor(toolName).DamageMultiplier; } public static bool ShouldApplyEffectDamagePrefix(bool hasRepresentingTool) { return !hasRepresentingTool; } public static bool IsShellSatchelWhiteFixedCostActive() { return ShouldApplyShellSatchelWhiteFixedCost(IsEquippedInWhiteSlot?.Invoke("Shell Satchel") ?? false, EffectsFor("Shell Satchel").ShellSatchelWhiteFixedShardCost); } public static bool IsCompassEquippedAnywhere() { if (!Gameplay.CompassTool.Status.IsEquipped) { return IsEquippedInWhiteSlot?.Invoke("Compass") ?? false; } return true; } public static bool IsCompassAbsoluteStorageActive(ToolItem tool) { if ((Object)(object)tool != (Object)null) { return ShouldApplyCompassAbsoluteStorage(IsCompassEquippedAnywhere(), GetLevel?.Invoke("Compass") ?? 0, tool.name); } return false; } public static bool IsCogworkStorageTool(string toolName) { if (!string.Equals(toolName, "Cogwork Flier", StringComparison.Ordinal)) { return string.Equals(toolName, "Cogfly", StringComparison.Ordinal); } return true; } public static bool ShouldApplyShellSatchelWhiteFixedCost(bool inWhiteSlot, bool effectEnabled) { return inWhiteSlot && effectEnabled; } public static bool ShouldApplyCompassAbsoluteStorage(bool compassEquipped, int level, string toolName) { if (compassEquipped && level >= 2) { return IsCogworkStorageTool(toolName); } return false; } public static bool ShouldForceDiceLucky(bool diceEquipped, int level, bool inCritWindow) { return diceEquipped && level >= 2 && inCritWindow; } public static void LongneedleSlashPrefix(NailAttackBase __instance) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) if (((ToolBase)Gameplay.LongNeedleTool).IsEquipped) { ToolEffectSpec toolEffectSpec = EffectsFor("Longneedle"); if (!(toolEffectSpec.NailLengthMultiplier <= 1.0) || !(toolEffectSpec.NailWidthMultiplier <= 1.0) || !(toolEffectSpec.NailWhiteLengthMultiplier <= 1.0)) { _slashScales.Remove(__instance); Vector3 original = (Vector3)((_nailAttackBaseScale != null) ? ((Vector3)(_nailAttackBaseScale.GetValue(__instance) ?? ((object)Vector3.one))) : ((Component)__instance).transform.localScale); _slashScales.Add(__instance, new SlashScaleCache { Original = original }); } } } public static void LongneedleSlashPostfix(NailAttackBase __instance) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) if (!((ToolBase)Gameplay.LongNeedleTool).IsEquipped) { return; } ToolEffectSpec toolEffectSpec = EffectsFor("Longneedle"); if ((!(toolEffectSpec.NailLengthMultiplier <= 1.0) || !(toolEffectSpec.NailWidthMultiplier <= 1.0) || !(toolEffectSpec.NailWhiteLengthMultiplier <= 1.0)) && _slashScales.TryGetValue(__instance, out SlashScaleCache value)) { _slashScales.Remove(__instance); double num = toolEffectSpec.NailLengthMultiplier; double num2 = toolEffectSpec.NailWidthMultiplier; if (IsWitchCrest()) { num += toolEffectSpec.WitchNailLengthBonus; num2 += toolEffectSpec.WitchNailWidthBonus; } num *= toolEffectSpec.NailWhiteLengthMultiplier; ((Component)__instance).transform.localScale = new Vector3(value.Original.x * (float)num, value.Original.y * (float)num2, value.Original.z); } } public static void WispLanternEquipsChangedPostfix(HeroWispLantern __instance) { ToolEffectSpec toolEffectSpec = EffectsFor("Wisp Lantern"); if (!Gameplay.WispLanternTool.Status.IsEquipped || toolEffectSpec.WispSpawnIntervalSeconds <= 0f) { RestoreWispCooldown(__instance); } else { SetWispCooldown(__instance, toolEffectSpec.WispSpawnIntervalSeconds); } } public static bool TakeSilkWispPrefix(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 == 1) { return !EffectsFor("Wisp Lantern").WispNoSilkCost; } return true; } public static void SilkSkillCostPostfix(PlayerData __instance, ref int __result) { if (Gameplay.FleaCharmTool.IsEquippedHud) { ToolEffectSpec toolEffectSpec = EffectsFor("Flea Charm"); if (toolEffectSpec.SilkSkillCost > 0 && (toolEffectSpec.SilkSkillCostNoHealthGate || __instance.health >= __instance.CurrentMaxHealth)) { __result = toolEffectSpec.SilkSkillCost; } } } public static void NailChargeTimePostfix(HeroController __instance, ref float __result) { ApplyNailChargeFactor(__instance, ref __result, __instance.NAIL_CHARGE_TIME); } public static void NailChargeBeginTimePostfix(HeroController __instance, ref float __result) { ApplyNailChargeFactor(__instance, ref __result, __instance.NAIL_CHARGE_BEGIN_TIME); } public static void SilkGainPrefix(HeroController __instance, HitInstance hitInstance) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 if (ToolItemManager.IsToolEquipped("Pinstress Tool")) { ToolEffectSpec toolEffectSpec = EffectsFor("Pinstress Tool"); if (toolEffectSpec.ChargeSilkMultiplier > 1.0 && (int)hitInstance.AttackType == 16) { _pendingSilkMultiplier = toolEffectSpec.ChargeSilkMultiplier; } } } public static void AddSilkPrefix(ref int amount) { if (!(_pendingSilkMultiplier <= 1.0)) { amount = (int)Math.Round((double)amount * _pendingSilkMultiplier); _pendingSilkMultiplier = 1.0; } } public static void WandererCritMultiplierPostfix(ref float __result) { if (Gameplay.CompassTool.Status.IsEquipped) { ToolEffectSpec toolEffectSpec = EffectsFor("Compass"); if (toolEffectSpec.CritMultiplierBonus > 0.0) { __result += (float)toolEffectSpec.CritMultiplierBonus; } } } public static void ZapDamageMultPostfix(ref float __result) { if (Gameplay.ZapImbuementTool.Status.IsEquipped) { ToolEffectSpec toolEffectSpec = EffectsFor("Zap Imbuement"); double num = toolEffectSpec.SilkSkillDamageBonus; if (toolEffectSpec.SilkSkillDamageWhiteBonus) { num += 0.25; } if (num > 0.0) { __result += (float)num; } } } public static void SilkRegenMaxPostfix(PlayerData __instance, ref int __result) { if (Gameplay.WhiteRingTool.Status.IsEquipped) { ToolEffectSpec toolEffectSpec = EffectsFor("White Ring"); int num = ((toolEffectSpec.SilkRegenCapBonus <= 0) ? 1 : toolEffectSpec.SilkRegenCapBonus); __result += num - 1; } } public static void WhiteRingSpeedMultiplierPostfix(ref float __result) { if (Gameplay.WhiteRingTool.Status.IsEquipped) { ToolEffectSpec toolEffectSpec = EffectsFor("White Ring"); double num = ((toolEffectSpec.SilkRegenSpeedFactor > 0.0) ? toolEffectSpec.SilkRegenSpeedFactor : 1.35); __result *= (float)(1.35 / num); } } public static bool SilkSpoolSetRegenPrefix(SilkSpool __instance, int amount, bool isUpgraded) { if (amount != 0) { return true; } Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot == null || !isEquippedInWhiteSlot("White Ring")) { return true; } return !EffectsFor("White Ring").SilkRegenNoResetOnChange; } public static void SilkMaxPostfix(PlayerData __instance, ref int __result) { if (Gameplay.SpoolExtenderTool.Status.IsEquipped) { ToolEffectSpec toolEffectSpec = EffectsFor("Spool Extender"); int num = ((toolEffectSpec.SilkCapBonus > 0) ? toolEffectSpec.SilkCapBonus : 3); if (toolEffectSpec.SilkCapWhiteDouble) { num *= 2; } __result = Math.Min(__instance.CurrentSilkMaxBasic + num, 36); } } public static void DrawSpoolPrefix(ref int maxOffset) { try { PlayerData instance = PlayerData.instance; if (instance != null) { int num = instance.CurrentSilkMax - instance.CurrentSilkMaxBasic; if (num > 0) { maxOffset += num; } } } catch (Exception ex) { WarnOnce(ex); } } public static void BrollyHitPrefix(ref HitInstance hitInstance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (IsBrollyHit(hitInstance) && Gameplay.BrollySpikeTool.Status.IsEquipped && ShouldApplyEffectDamagePrefix((Object)(object)hitInstance.RepresentingTool != (Object)null)) { ToolEffectSpec toolEffectSpec = EffectsFor("Brolly Spike"); if (!(toolEffectSpec.DamageMultiplier <= 1.0)) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * toolEffectSpec.DamageMultiplier); } } } public static void BrollyHitPostfix(HitInstance hitInstance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!IsBrollyHit(hitInstance)) { return; } Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Brolly Spike") && EffectsFor("Brolly Spike").BrollyWhiteSilk) { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null) { instance.AddSilk(1, false, (SilkAddSource)0); } } } public static void DazzleHitPrefix(ref HitInstance hitInstance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!IsDazzleHit(hitInstance)) { return; } ToolDefinition toolDefinition = FindDazzleDefinition(); if (!(toolDefinition == null) && ShouldApplyEffectDamagePrefix((Object)(object)hitInstance.RepresentingTool != (Object)null)) { ToolEffectSpec toolEffectSpec = EffectsFor(toolDefinition.Name); if (!(toolEffectSpec.DamageMultiplier <= 1.0)) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * toolEffectSpec.DamageMultiplier); } } } public static void DazzleWhiteTakeDamagePostfix(HeroController __instance) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00d3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { return; } ToolDefinition toolDefinition = FindDazzleDefinition(); if (toolDefinition == null) { return; } Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot == null || !isEquippedInWhiteSlot(toolDefinition.Name) || !EffectsFor(toolDefinition.Name).DazzleWhiteOnHit) { return; } try { GameObject val = ReadDazzleFlashPrefab((PlayMakerFSM?)(((Object)(object)__instance.spellControl != (Object)null) ? ((object)__instance.spellControl) : ((object)((IEnumerable<PlayMakerFSM>)((Component)__instance).GetComponents<PlayMakerFSM>()).FirstOrDefault((Func<PlayMakerFSM, bool>)((PlayMakerFSM f) => (Object)(object)f != (Object)null && (f.FsmName == "Bind" || f.FsmName == "Spell Control"))))), toolDefinition.Name == "Dazzle Bind Upgraded"); if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)__instance).transform.position + new Vector3(0f, 0f, -0.21f); ObjectPoolExtensions.Spawn(val, val2, Quaternion.identity); } } catch (Exception ex) { WarnOnce(ex); } } public static void RevengeCrystalTakeDamagePrefix(HeroController __instance) { _revengeHealthBefore = -1; if (!((Object)(object)__instance == (Object)null)) { PlayerData playerData = __instance.playerData; if (playerData != null) { _revengeHealthBefore = playerData.health + playerData.healthBlue; } } } public static void RevengeCrystalTakeDamagePostfix(HeroController __instance) { if ((Object)(object)__instance == (Object)null) { return; } try { PlayerData playerData = __instance.playerData; if (playerData == null) { return; } int revengeHealthBefore = _revengeHealthBefore; _revengeHealthBefore = -1; if (revengeHealthBefore >= 0 && playerData.health + playerData.healthBlue < revengeHealthBefore) { string text = "Revenge Crystal"; ToolEffectSpec toolEffectSpec = EffectsFor(text); bool equipped = ToolItemManager.IsToolEquipped(text); bool inWhiteSlot = IsEquippedInWhiteSlot?.Invoke(text) ?? false; int effectiveLevel = GetLevel?.Invoke(text) ?? 0; int revengeCrystalCount = toolEffectSpec.RevengeCrystalCount; bool num = RevengeCrystalRules.ShouldSpawnExtras(equipped, inWhiteSlot, effectiveLevel, revengeCrystalCount); int num2 = RevengeCrystalRules.ExtraCount(revengeCrystalCount); if (num && num2 > 0) { SpawnRevengeCrystals?.Invoke(num2); } } } catch (Exception ex) { WarnOnce(ex); } } public static void RevengeCrystalSpecialDamagePostfix(HeroController __instance) { if ((Object)(object)__instance == (Object)null) { return; } try { string text = "Revenge Crystal"; ToolEffectSpec toolEffectSpec = EffectsFor(text); bool equipped = ToolItemManager.IsToolEquipped(text); bool inWhiteSlot = IsEquippedInWhiteSlot?.Invoke(text) ?? false; int effectiveLevel = GetLevel?.Invoke(text) ?? 0; int revengeCrystalCount = toolEffectSpec.RevengeCrystalCount; bool num = RevengeCrystalRules.ShouldSpawnExtras(equipped, inWhiteSlot, effectiveLevel, revengeCrystalCount); int num2 = RevengeCrystalRules.ExtraCount(revengeCrystalCount); if (num && num2 > 0) { SpawnRevengeCrystals?.Invoke(num2); } } catch (Exception ex) { WarnOnce(ex); } } public static void ShardsAddPrefix(PlayerData __instance, ref int amount) { if (amount > 0 && ToolItemManager.IsToolEquipped("Bone Necklace")) { ToolEffectSpec toolEffectSpec = EffectsFor("Bone Necklace"); if (!(toolEffectSpec.ShardValueMultiplier <= 1.0)) { amount = (int)Math.Round((double)amount * toolEffectSpec.ShardValueMultiplier); } } } public static void GeoAddPrefix(HeroController __instance, ref int amount) { if (amount > 0 && ToolItemManager.IsToolEquipped("Rosary Magnet")) { ToolEffectSpec toolEffectSpec = EffectsFor("Rosary Magnet"); if (!(toolEffectSpec.GeoValueMultiplier <= 1.0)) { amount = (int)Math.Round((double)amount * toolEffectSpec.GeoValueMultiplier); } } } public static void MaxFallVelocityPostfix(HeroController __instance, ref float __result) { if (Gameplay.WeightedAnkletTool.Status.IsEquipped) { ToolEffectSpec toolEffectSpec = EffectsFor("Weighted Anklet"); if (!(toolEffectSpec.FallSpeedBonusPercent <= 0.0)) { __result = __instance.MAX_FALL_VELOCITY * (float)(1.0 + toolEffectSpec.FallSpeedBonusPercent); } } } public static void WeightedKnockbackMultPostfix(ref float __result) { if (Gameplay.WeightedAnkletTool.Status.IsEquipped && (GetLevel?.Invoke("Weighted Anklet") ?? 0) >= 2) { __result = (float)EffectsFor("Weighted Anklet").KnockbackMultiplier; } } public static void WeightedInvulnMultPostfix(ref float __result) { if (Gameplay.WeightedAnkletTool.Status.IsEquipped && (GetLevel?.Invoke("Weighted Anklet") ?? 0) >= 2) { __result = (float)EffectsFor("Weighted Anklet").InvulnMultiplier; } } public static void BarbedWireDealtMultPostfix(ref float __result) { if (Gameplay.BarbedWireTool.Status.IsEquipped && (GetLevel?.Invoke("Barbed Wire") ?? 0) >= 2) { __result = (float)EffectsFor("Barbed Wire").BarbedWireDamageDealtMultiplier; } } public static void BarbedWireTakenMultPostfix(ref float __result) { if (Gameplay.BarbedWireTool.Status.IsEquipped) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Barbed Wire") && EffectsFor("Barbed Wire").BarbedWireWhiteNoDamageTakenMultiplier) { __result = 1f; } else if ((GetLevel?.Invoke("Barbed Wire") ?? 0) >= 2) { __result = (float)EffectsFor("Barbed Wire").BarbedWireDamageTakenMultiplier; } } } public static void DeadPurseHoldPercentPostfix(ref float __result) { if (Gameplay.DeadPurseTool.Status.IsEquipped && (GetLevel?.Invoke("Dead Mans Purse") ?? 0) >= 2) { __result = (float)EffectsFor("Dead Mans Purse").DeadPurseHoldPercent; } } public static void CocoonBrokenMinOneCocoonPrefix(HeroController __instance) { try { if (Gameplay.DeadPurseTool.Status.IsEquipped && (GetLevel?.Invoke("Dead Mans Purse") ?? 0) >= 3 && EffectsFor("Dead Mans Purse").DeadPurseMinOneCocoon) { PlayerData val = __instance.playerData ?? PlayerData.instance; if (val != null && val.HeroCorpseMoneyPool == 0 && val.geo > 0) { val.geo--; val.HeroCorpseMoneyPool = 1; } } } catch (Exception ex) { WarnOnce(ex); } } public static void CompassStoragePostfix(ToolItem __instance, ref int __result) { if ((Object)(object)__instance == (Object)null || !IsCompassEquippedAnywhere()) { return; } int num = GetLevel?.Invoke("Compass") ?? 0; if (num < 2 || !IsCogworkStorageTool(__instance.name)) { return; } __result = ((num >= 3) ? 12 : 8); if (__result != _lastLoggedCompassCap) { _lastLoggedCompassCap = __result; ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)$"[CompassStorage] cogfly capacity -> {__result} (level {num})."); } } } public static void ToolReplenishCostPostfix(ref int __result) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Shell Satchel") && EffectsFor("Shell Satchel").ShellSatchelWhiteFixedShardCost) { __result = 1; } } public static void ReplenishUsageMultiplierPostfix(ref float __result) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Shell Satchel") && EffectsFor("Shell Satchel").ShellSatchelWhiteFixedShardCost) { __result = 1f; } } public static void LuckyDiceThresholdPostfix(int hits, ref int __result) { if (Gameplay.LuckyDiceTool.Status.IsEquipped) { int num = GetLevel?.Invoke("Magnetite Dice") ?? 0; if (num >= 2) { int num2 = ((num >= 3) ? 8 : 4); __result = Math.Min(hits * num2, num2 * 5); } } } public static void LuckyDiceTakeDamagePrefix(HeroController __instance, ref int __state) { __state = ((_luckyDiceShieldedHits != null && (Object)(object)__instance != (Object)null) ? ((int)(_luckyDiceShieldedHits.GetValue(__instance) ?? ((object)0))) : 0); } public static void LuckyDiceTakeDamagePostfix(HeroController __instance, HazardType hazardType, int __state) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 if ((Object)(object)__instance == (Object)null || __state <= 0 || (int)hazardType != 1) { return; } Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot == null || !isEquippedInWhiteSlot("Magnetite Dice") || !EffectsFor("Magnetite Dice").LuckyDiceWhiteNoReset || _luckyDiceShieldedHits == null) { return; } try { if ((int)(_luckyDiceShieldedHits.GetValue(__instance) ?? ((object)0)) == 0) { _luckyDiceShieldedHits.SetValue(__instance, __state); } } catch (Exception ex) { WarnOnce(ex); } } public static void IsWandererLuckyDicePostfix(HeroController __instance, ref bool __result) { if (!__result && !((Object)(object)__instance == (Object)null) && !__instance.cState.isMaggoted && ShouldForceDiceLucky(Gameplay.LuckyDiceTool.Status.IsEquipped, GetLevel?.Invoke("Magnetite Dice") ?? 0, IsDiceCritWindow)) { __result = true; } } public static void DoDamagePrefix(GameObject target, bool isFirstHit) { if (ShouldForceDiceLucky(Gameplay.LuckyDiceTool.Status.IsEquipped, GetLevel?.Invoke("Magnetite Dice") ?? 0, inCritWindow: true)) { IsDiceCritWindow = true; } } public static void DoDamagePostfix(GameObject target, bool isFirstHit) { IsDiceCritWindow = false; } public static void DoDamageFinalizer(GameObject target, bool isFirstHit, Exception __exception) { IsDiceCritWindow = false; } public static void WandererCritChanceDicePostfix(ref float __result) { if (Gameplay.LuckyDiceTool.Status.IsEquipped && (GetLevel?.Invoke("Magnetite Dice") ?? 0) >= 2) { ToolEffectSpec toolEffectSpec = EffectsFor("Magnetite Dice"); double num = toolEffectSpec.LuckyDiceCritChance; HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null && ((ToolBase)Gameplay.WandererCrest).IsEquipped && instance.playerData != null && instance.playerData.silk >= 9) { num += toolEffectSpec.LuckyDiceWandererCritBonus; } __result = (float)num; } } public static void GetLuckModifierDicePostfix(ref float __result) { if (Gameplay.LuckyDiceTool.Status.IsEquipped && (GetLevel?.Invoke("Magnetite Dice") ?? 0) >= 2) { __result = 1f; } } public static void MusicianRangeMultPostfix(ref float __result) { if (Gameplay.MusicianCharmTool.Status.IsEquipped && (GetLevel?.Invoke("Musician Charm") ?? 0) >= 2) { __result = (float)EffectsFor("Musician Charm").MusicianRangeMultiplier; } } public static void MusicianDrainTimeMultPostfix(ref float __result) { if (Gameplay.MusicianCharmTool.Status.IsEquipped && (GetLevel?.Invoke("Musician Charm") ?? 0) >= 2) { __result = (float)EffectsFor("Musician Charm").MusicianSongTimeMultiplier; } } public static void SilkDrainDisabledPostfix(ref bool __result) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Sprintmaster") && EffectsFor("Sprintmaster").SprintmasterWhiteNoSilkCost) { __result = true; } } public static void ThiefSmallGeoIncreasePostfix(ref float __result) { ApplyThiefGeoIncrease(ref __result, (ToolEffectSpec fx) => fx.ThiefSmallGeoIncrease); } public static void ThiefMedGeoIncreasePostfix(ref float __result) { ApplyThiefGeoIncrease(ref __result, (ToolEffectSpec fx) => fx.ThiefMedGeoIncrease); } public static void ThiefLargeGeoIncreasePostfix(ref float __result) { ApplyThiefGeoIncrease(ref __result, (ToolEffectSpec fx) => fx.ThiefLargeGeoIncrease); } public static void ThiefGeoLossPostfix(ref object __result) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Thief Charm") && EffectsFor("Thief Charm").ThiefWhiteNoLoss && _zeroMinMaxFloat != null) { __result = _zeroMinMaxFloat; } } public static void ThiefGeoLossLooseChancePostfix(ref float __result) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Thief Charm") && EffectsFor("Thief Charm").ThiefWhiteNoLoss) { __result = 0f; } } public static bool TakeSilkClawlinePrefix(HeroController __instance, int amount, SilkTakeSource source) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot == null || !isEquippedInWhiteSlot("Thief Claw")) { return true; } if (!EffectsFor("Thief Claw").ClawlineNoSilkCost) { return true; } try { object obj = _heroCState?.GetValue(__instance); if (obj != null && _whipLashing != null && (bool)(_whipLashing.GetValue(obj) ?? ((object)false))) { return false; } } catch (Exception ex) { WarnOnce(ex); } return true; } public static void ThrowToolPrefix(HeroController __instance, bool isAutoThrow) { try { if (!Gameplay.QuickSlingTool.Status.IsEquipped) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot == null || !isEquippedInWhiteSlot("Quick Sling")) { goto IL_0033; } } if (!ToolItemManager.IsCustomToolOverride) { object? obj = _willThrowTool?.GetValue(__instance); ToolItem val = (ToolItem)((obj is ToolItem) ? obj : null); if ((Object)(object)val == (Object)null) { _quickSlingRemaining = 0; _quickSlingTool = null; return; } ToolEffectSpec toolEffectSpec = EffectsFor("Quick Sling"); int num = ((toolEffectSpec.QuickSlingThrowCount > 0) ? toolEffectSpec.QuickSlingThrowCount : 2); if (QuickSlingWhiteSlotRules.ShouldApplyWhiteSlotModifier(toolEffectSpec, IsEquippedInWhiteSlot?.Invoke("Quick Sling") ?? false, val.name)) { _quickSlingRemaining = 0; _quickSlingTool = null; if (!isAutoThrow) { _pendingWhiteSlotTool = val.name; _pendingWhiteSlotMultiplier = (string.Equals(val.name, "Lifeblood Syringe", StringComparison.Ordinal) ? ((double)QuickSlingWhiteSlotRules.SyringeBlueHealth(toolEffectSpec)) : QuickSlingWhiteSlotRules.DurationMultiplier(toolEffectSpec)); } } else if (QuickSlingWhiteSlotRules.ShouldStartThrowChain(isWhiteSlotModifiedTool: false, num)) { _quickSlingTool = val; if (!isAutoThrow) { _quickSlingRemaining = num - 1; } } else { _quickSlingRemaining = 0; _quickSlingTool = null; } return; } goto IL_0033; IL_0033: _quickSlingRemaining = 0; _quickSlingTool = null; } catch (Exception ex) { WarnOnce(ex); } } public static void ThrowToolPostfix(HeroController __instance, bool isAutoThrow) { try { if (_quickSlingRemaining <= 0) { return; } if (!Gameplay.QuickSlingTool.Status.IsEquipped) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot == null || !isEquippedInWhiteSlot("Quick Sling")) { goto IL_0040; } } if (!ToolItemManager.IsCustomToolOverride) { if (isAutoThrow) { object? obj = _willThrowTool?.GetValue(__instance); if ((Object)((obj is ToolItem) ? obj : null) != (Object)null) { _quickSlingRemaining = 0; _quickSlingTool = null; _queuedAutoThrowTool?.SetValue(__instance, false); } else if (_quickSlingRemaining > 1 && (Object)(object)_quickSlingTool != (Object)null) { _quickSlingRemaining--; _willThrowTool?.SetValue(__instance, _quickSlingTool); _queuedAutoThrowTool?.SetValue(__instance, true); } else { _quickSlingRemaining = 0; _quickSlingTool = null; } } return; } goto IL_0040; IL_0040: _quickSlingRemaining = 0; _quickSlingTool = null; } catch (Exception ex) { WarnOnce(ex); } } public static void ThrowToolEndPostfix(HeroController __instance) { if (_quickSlingRemaining <= 0) { _quickSlingTool = null; } } public static void ActivateQuickeningPostfix(HeroController __instance) { if (_pendingWhiteSlotTool != "Flea Brew" || _quickeningTimeLeft == null) { return; } try { if (!(_pendingWhiteSlotMultiplier <= 1.0)) { float num = (float)(_quickeningTimeLeft.GetValue(__instance) ?? ((object)0f)); _quickeningTimeLeft.SetValue(__instance, num * (float)_pendingWhiteSlotMultiplier); } } catch (Exception ex) { WarnOnce(ex); } finally { ClearPendingWhiteSlot(); } } public static void NailImbuementSetElementPostfix(HeroNailImbuement __instance) { if (_pendingWhiteSlotTool != "Flintstone" || _imbuementTimeLeft == null) { return; } try { if (!(_pendingWhiteSlotMultiplier <= 1.0)) { float num = (float)(_imbuementTimeLeft.GetValue(__instance) ?? ((object)0f)); _imbuementTimeLeft.SetValue(__instance, num * (float)_pendingWhiteSlotMultiplier); } } catch (Exception ex) { WarnOnce(ex); } finally { ClearPendingWhiteSlot(); } } public static void OnToolUsed(ToolUsedEvent toolUsed) { if (!(_pendingWhiteSlotTool != "Lifeblood Syringe") && string.Equals(toolUsed.ToolName, "Lifeblood Syringe", StringComparison.Ordinal)) { int num = Math.Max(1, (int)Math.Round(_pendingWhiteSlotMultiplier)); int num2 = Math.Max(0, num - 1); if (num2 > 0) { AddExtraBlueHealth?.Invoke(num2); } ClearPendingWhiteSlot(); } } private static void ClearPendingWhiteSlot() { _pendingWhiteSlotTool = null; _pendingWhiteSlotMultiplier = 1.0; } private static void ClearQuickSling(HeroController? hero = null) { bool flag = _quickSlingRemaining > 0 || (Object)(object)_quickSlingTool != (Object)null; _quickSlingRemaining = 0; _quickSlingTool = null; if ((Object)(object)hero == (Object)null || !flag) { return; } try { _queuedAutoThrowTool?.SetValue(hero, false); } catch (Exception ex) { WarnOnce(ex); } } public static bool AddToMaggotCharmTimerPrefix() { return !MaggotImmuneActive(); } public static bool DidMaggotCharmHitPrefix() { return !MaggotImmuneActive(); } public static bool IsMaggotRepairOnHealActive() { if (ToolItemManager.IsToolEquipped("Maggot Charm")) { return EffectsFor("Maggot Charm").MaggotRepairOnHeal; } return false; } public static bool IsMaggotRepairOnKillActive() { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Maggot Charm")) { return EffectsFor("Maggot Charm").MaggotRepairOnKill; } return false; } public static void ResetMaggotCharmNow() { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null || _resetMaggotCharm == null) { return; } try { _resetMaggotCharm.Invoke(instance, null); } catch (Exception ex) { WarnOnce(ex); } } public static bool DoMossToolHitPrefix(HeroController __instance) { try { PlayerData instance = PlayerData.instance; if (instance == null || instance.health <= 0 || instance.silk >= instance.CurrentSilkMax) { return true; } if (HandleMossTool(__instance, Gameplay.MossCreep1Tool, _moss1Hits, 1)) { return false; } if (HandleMossTool(__instance, Gameplay.MossCreep2Tool, _moss2Hits, 2)) { return false; } } catch (Exception ex) { WarnOnce(ex); } return true; } public static bool HeroTakeDamagePrefix(HeroController __instance, ref int damageAmount, HazardType hazardType, DamagePropertyFlags damagePropertyFlags) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) try { if (HealUninterruptibleActive(__instance)) { return false; } ToolDefinition toolDefinition = ToolCatalog.Find("Lava Charm"); if (toolDefinition != null && ((int)hazardType == 4 || (damagePropertyFlags & 4) != 0)) { int level = GetLevel?.Invoke("Lava Charm") ?? 0; bool inWhiteSlot = IsEquippedInWhiteSlot?.Invoke("Lava Charm") ?? false; if (toolDefinition.ResolveEffects(level, inWhiteSlot).LavaDamageImmune) { damageAmount = 0; } } ToolDefinition toolDefinition2 = ToolCatalog.Find("Weighted Anklet"); if (toolDefinition2 != null) { int level2 = GetLevel?.Invoke("Weighted Anklet") ?? 0; bool inWhiteSlot2 = IsEquippedInWhiteSlot?.Invoke("Weighted Anklet") ?? false; if (toolDefinition2.ResolveEffects(level2, inWhiteSlot2).TwoDamageToOne && damageAmount == 2) { damageAmount = 1; } } } catch (Exception ex) { WarnOnce(ex); } return true; } public static bool CanBeGrabbedPrefix(HeroController __instance, bool ignoreParryState, ref bool __result) { if (HealUninterruptibleActive(__instance)) { __result = false; return false; } return true; } public static bool CanBeBarnacleGrabbedPrefix(HeroController __instance, ref bool __result) { if (HealUninterruptibleActive(__instance)) { __result = false; return false; } return true; } private static bool HealUninterruptibleActive(HeroController hero) { if ((Object)(object)hero == (Object)null || hero.cState == null) { return false; } if (!hero.cState.focusing && !hero.cState.isBinding) { return false; } Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot == null || !isEquippedInWhiteSlot("Bone Necklace")) { return false; } return EffectsFor("Bone Necklace").HealUninterruptible; } public static void UseLavaBellPostfix(HeroController __instance) { try { ToolDefinition toolDefinition = ToolCatalog.Find("Lava Charm"); if (!(toolDefinition == null)) { int level = GetLevel?.Invoke("Lava Charm") ?? 0; bool inWhiteSlot = IsEquippedInWhiteSlot?.Invoke("Lava Charm") ?? false; int lavaCooldownSeconds = toolDefinition.ResolveEffects(level, inWhiteSlot).LavaCooldownSeconds; if (lavaCooldownSeconds > 0) { _lavaCooldown?.SetValue(__instance, (float)lavaCooldownSeconds); } } } catch (Exception ex) { WarnOnce(ex); } } public static bool TakeFrostDamagePrefix() { return !ColdDebuffImmune(); } public static bool AddFrostPrefix() { return !ColdDebuffImmune(); } public static bool SetFrostAmountPrefix() { return !ColdDebuffImmune(); } public static void DamageTagOnHitPrefix(DamageTag __instance) { if ((Object)(object)__instance == (Object)(object)Gameplay.PoisonPouchDamageTag) { _pendingPoisonMultiplier = (GetLevel?.Invoke("Poison Pouch") ?? 0) switch { 2 => 2.0, 3 => 3.0, _ => 1.0, }; } } public static void HealthManagerApplyTagDamagePrefix(ref DamageTagInstance damageTagInstance) { if (_pendingPoisonMultiplier > 1.0) { damageTagInstance.amount = (int)Math.Round((double)damageTagInstance.amount * _pendingPoisonMultiplier); _pendingPoisonMultiplier = 1.0; } } public static void TagDamageTakerAddDamageTagToStackPostfix(TagDamageTaker __instance, DamageTag damageTag, int hitAmountOverride) { if (hitAmountOverride <= 0 || (Object)(object)damageTag != (Object)(object)Gameplay.PoisonPouchDamageTag) { return; } ToolDefinition toolDefinition = ToolCatalog.Find("Poison Pouch"); if (toolDefinition == null) { return; } int level = GetLevel?.Invoke("Poison Pouch") ?? 0; bool inWhiteSlot = IsEquippedInWhiteSlot?.Invoke("Poison Pouch") ?? false; if (!toolDefinition.ResolveEffects(level, inWhiteSlot).PoisonStacking) { return; } try { if (_taggedDamage?.GetValue(__instance) is Dictionary<DamageTag, DamageTagInfo> dictionary && dictionary.TryGetValue(damageTag, out var value)) { DamageTagInfo obj = value; obj.HitsLeft += hitAmountOverride; } } catch (Exception ex) { WarnOnce(ex); } } private static bool HandleMossTool(HeroController hero, ToolItem tool, FieldInfo? hitsField, int silk) { if ((Object)(object)tool == (Object)null) { return false; } string name = tool.name; if (!WhiteToolSystem.IsEquippedAnywhere(name)) { return false; } int num = GetLevel?.Invoke(name) ?? 0; bool flag = IsEquippedInWhiteSlot?.Invoke(name) ?? false; ToolEffectSpec toolEffectSpec = ToolCatalog.Find(name)?.ResolveEffects(num, flag) ?? ToolEffectSpec.None; if (num == 0 && !flag) { return false; } int num2 = MossRules.PerHitSpools(toolEffectSpec.MossSpoolsPerHit, silk); if (toolEffectSpec.MossSilkPerWhiteHit > 0) { hero.AddSilk(toolEffectSpec.MossSilkPerWhiteHit, false, (SilkAddSource)1); hitsField?.SetValue(hero, 0); GameCameras.instance.silkSpool.SetMossState(0, 1); ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)($"[Moss][probe] tool={name} level={num} white={flag} " + $"branch=white-direct silk={toolEffectSpec.MossSilkPerWhiteHit}")); } return true; } int num3 = (int)(hitsField?.GetValue(hero) ?? ((object)0)) + 1; if (num3 >= 2) { hitsField?.SetValue(hero, 0); hero.AddSilk(num2, false, (SilkAddSource)1); ManualLogSource? log2 = _log; if (log2 != null) { log2.LogDebug((object)($"[Moss][probe] tool={name} level={num} white={flag} " + $"branch=convert silk={num2}")); } } else { hitsField?.SetValue(hero, num3); GameCameras.instance.silkSpool.SetMossState(1, num2); ManualLogSource? log3 = _log; if (log3 != null) { log3.LogDebug((object)($"[Moss][probe] tool={name} level={num} white={flag} " + $"branch=spools count={num2}")); } } return true; } private static bool ColdDebuffImmune() { ToolDefinition toolDefinition = ToolCatalog.Find("Lava Charm"); if (toolDefinition == null) { return false; } int level = GetLevel?.Invoke("Lava Charm") ?? 0; bool inWhiteSlot = IsEquippedInWhiteSlot?.Invoke("Lava Charm") ?? false; return toolDefinition.ResolveEffects(level, inWhiteSlot).ColdDebuffImmune; } public static ToolEffectSpec EffectsFor(string toolName) { ToolDefinition toolDefinition = ToolCatalog.Find(toolName); if (toolDefinition == null) { return ToolEffectSpec.None; } int level = GetLevel?.Invoke(toolName) ?? 0; bool inWhiteSlot = IsEquippedInWhiteSlot?.Invoke(toolName) ?? false; return toolDefinition.ResolveEffects(level, inWhiteSlot); } private static bool IsWitchCrest() { return string.Equals(HeroController.instance?.playerData?.CurrentCrestID, "Witch", StringComparison.OrdinalIgnoreCase); } private static bool IsBrollyHit(HitInstance hit) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_0036: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hit.RepresentingTool != (Object)null && string.Equals(hit.RepresentingTool.name, "Brolly Spike", StringComparison.Ordinal)) { return true; } if ((Object)(object)hit.Source != (Object)null) { return ((Object)hit.Source).name.IndexOf("Brolly", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static bool IsDazzleHit(HitInstance hit) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hit.RepresentingTool != (Object)null && (string.Equals(hit.RepresentingTool.name, "Dazzle Bind", StringComparison.Ordinal) || string.Equals(hit.RepresentingTool.name, "Dazzle Bind Upgraded", StringComparison.Ordinal))) { return true; } if ((Object)(object)hit.Source != (Object)null) { return ((Object)hit.Source).name.IndexOf("dazzle", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } public static bool IsBellBindSource(string? toolName, string? sourceName) { if (!string.IsNullOrEmpty(toolName) && string.Equals(toolName, "Bell Bind", StringComparison.Ordinal)) { return true; } if (!string.IsNullOrEmpty(sourceName)) { if (sourceName.IndexOf("bell", StringComparison.OrdinalIgnoreCase) < 0) { return sourceName.IndexOf("shield", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return false; } private static bool IsBellBindHit(HitInstance hit) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) ToolItem representingTool = hit.RepresentingTool; string toolName = ((representingTool != null) ? representingTool.name : null); GameObject source = hit.Source; return IsBellBindSource(toolName, (source != null) ? ((Object)source).name : null); } public static void BellBindHitPrefix(ref HitInstance hitInstance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (IsBellBindHit(hitInstance) && ToolItemManager.IsToolEquipped("Bell Bind") && ShouldApplyEffectDamagePrefix((Object)(object)hitInstance.RepresentingTool != (Object)null)) { ToolEffectSpec toolEffectSpec = EffectsFor("Bell Bind"); if (!(toolEffectSpec.DamageMultiplier <= 1.0)) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * toolEffectSpec.DamageMultiplier); } } } private static ToolDefinition? FindDazzleDefinition() { string text = DazzleWhiteRules.SelectDefinitionName(WhiteToolSystem.IsEquippedAnywhere("Dazzle Bind Upgraded"), WhiteToolSystem.IsEquippedAnywhere("Dazzle Bind")); if (text != null) { return ToolCatalog.Find(text); } return null; } private static GameObject? ReadDazzleFlashPrefab(PlayMakerFSM? bindFsm, bool isUpgraded) { if ((Object)(object)bindFsm == (Object)null) { return null; } FsmState state = FsmUtil.GetState(bindFsm, "Dazzle?"); if (state == null) { return null; } string text = (isUpgraded ? "Dazzle Flash Upgraded" : "Dazzle Flash"); FsmStateAction[] actions = state.Actions; foreach (FsmStateAction obj in actions) { SetGameObject val = (SetGameObject)(object)((obj is SetGameObject) ? obj : null); if (val != null && val.variable != null && ((NamedVariable)val.variable).Name == text && val.gameObject != null) { return val.gameObject.Value; } } return null; } private static bool MaggotImmuneActive() { if (ToolItemManager.IsToolEquipped("Maggot Charm")) { return EffectsFor("Maggot Charm").MaggotImmune; } return false; } private static void ApplyNailChargeFactor(HeroController hero, ref float result, float baseValue) { if (ToolItemManager.IsToolEquipped("Pinstress Tool")) { ToolEffectSpec toolEffectSpec = EffectsFor("Pinstress Tool"); if (!(toolEffectSpec.NailChargeTimeFactor <= 0.0) && !(toolEffectSpec.NailChargeTimeFactor >= 1.0)) { result = (float)((double)baseValue * toolEffectSpec.NailChargeTimeFactor); } } } private static void SetWispCooldown(HeroWispLantern instance, float seconds) { if (_wispCooldownField == null || _minMaxStartField == null || _minMaxEndField == null) { return; } try { if (!_wispCooldownCache.TryGetValue(instance, out WispCooldownCache value)) { object value2 = _wispCooldownField.GetValue(instance); value = new WispCooldownCache { Min = (float)_minMaxStartField.GetValue(value2), Max = (float)_minMaxEndField.GetValue(value2) }; _wispCooldownCache.Add(instance, value); } object value3 = _wispCooldownField.GetValue(instance); _minMaxStartField.SetValue(value3, seconds); _minMaxEndField.SetValue(value3, seconds); _wispCooldownField.SetValue(instance, value3); } catch (Exception ex) { WarnOnce(ex); } } private static void RestoreWispCooldown(HeroWispLantern instance) { if (_wispCooldownField == null || _minMaxStartField == null || _minMaxEndField == null || !_wispCooldownCache.TryGetValue(instance, out WispCooldownCache value)) { return; } try { object value2 = _wispCooldownField.GetValue(instance); _minMaxStartField.SetValue(value2, value.Min); _minMaxEndField.SetValue(value2, value.Max); _wispCooldownField.SetValue(instance, value2); } catch (Exception ex) { WarnOnce(ex); } } private static void ApplyThiefGeoIncrease(ref float result, Func<ToolEffectSpec, double> selector) { if (Gameplay.ThiefCharmTool.Status.IsEquipped && (GetLevel?.Invoke("Thief Charm") ?? 0) >= 2) { double num = selector(EffectsFor("Thief Charm")); if (num > 0.0) { result = (float)num; } } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[BlueToolEffects] patch failed: " + ex.Message)); } } } } public static class CogflyAutoReleasePatch { public static Func<bool>? IsEnabled; public static CogflySpawner? Spawner; public static void Tick() { Func<bool>? isEnabled = IsEnabled; if (isEnabled != null && isEnabled() && Spawner != null) { Spawner.Tick(); } } public static void OnSceneChanged() { Spawner?.OnSceneChanged(); } } public static class FlintBrewStackBridge { public static Func<bool>? IsActive; public static FlintBrewStackState FlintState { get; } = new FlintBrewStackState(); public static FlintBrewStackState BrewState { get; } = new FlintBrewStackState(); public static bool IsEnabled => IsActive?.Invoke() ?? false; } public static class FlintBrewStackPatch { private static FieldRef<HeroNailImbuement, float>? _imbuementTimeLeftRef; private static FieldRef<HeroController, float>? _quickeningTimeLeftRef; private static bool _warned; public static void Configure(ManualLogSource log) { _warned = false; _imbuementTimeLeftRef = AccessTools.FieldRefAccess<HeroNailImbuement, float>("imbuementTimeLeft"); _quickeningTimeLeftRef = AccessTools.FieldRefAccess<HeroController, float>("quickeningTimeLeft"); } public static void SetElementPrefix(HeroNailImbuement __instance, ref float __state) { __state = ((_imbuementTimeLeftRef != null) ? _imbuementTimeLeftRef.Invoke(__instance) : 0f); } public static void SetElementPostfix(HeroNailImbuement __instance, NailElements element, float __state) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if (!FlintBrewStackBridge.IsEnabled) { return; } if ((int)element == 0) { FlintBrewStackBridge.FlintState.Reset(); } else { if ((int)element != 1) { return; } try { float num = FlintBrewStackBridge.FlintState.AddUse(); if (num > 0f && _imbuementTimeLeftRef != null) { _imbuementTimeLeftRef.Invoke(__instance) = __state + num; } } catch (Exception ex) { WarnOnce(ex); } } } public static void ActivateQuickeningPrefix(HeroController __instance, ref float __state) { __state = ((_quickeningTimeLeftRef != null) ? _quickeningTimeLeftRef.Invoke(__instance) : 0f); } public static void ActivateQuickeningPostfix(HeroController __instance, float __state) { if (!FlintBrewStackBridge.IsEnabled) { return; } try { float num = FlintBrewStackBridge.BrewState.AddUse(); if (num > 0f && _quickeningTimeLeftRef != null) { _quickeningTimeLeftRef.Invoke(__instance) = __state + num; } } catch (Exception ex) { WarnOnce(ex); } } public static void StopQuickeningPostfix(HeroController __instance) { if (FlintBrewStackBridge.IsEnabled) { FlintBrewStackBridge.BrewState.Reset(); } } public static void QuickAttackSpeedMultPostfix(HeroControllerConfig __instance, ref float __result) { if (FlintBrewStackBridge.IsEnabled && FlintBrewStackBridge.BrewState.Stacks > 1) { HeroController instance = HeroController.instance; if (!((Object)(object)instance == (Object)null) && instance.Config == __instance && instance.IsUsingQuickening) { __result *= FlintBrewStackBridge.BrewState.Stacks; } } } public static void QuickAttackCooldownTimePostfix(HeroControllerConfig __instance, ref float __result) { if (FlintBrewStackBridge.IsEnabled && FlintBrewStackBridge.BrewState.Stacks > 1) { HeroController instance = HeroController.instance; if (!((Object)(object)instance == (Object)null) && instance.Config == __instance && instance.IsUsingQuickening && __result > 0f) { __result /= FlintBrewStackBridge.BrewState.Stacks; } } } public static void HitPrefix(ref HitInstance hitInstance) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 if (FlintBrewStackBridge.IsEnabled && FlintBrewStackBridge.FlintState.Stacks > 1 && !((Object)(object)hitInstance.NailImbuement == (Object)null)) { HeroController instance = HeroController.instance; if (!((Object)(object)((instance != null) ? instance.NailImbuement : null) == (Object)null) && (int)instance.NailImbuement.CurrentElement == 1 && hitInstance.NailImbuement == instance.NailImbuement.CurrentImbuement) { hitInstance.DamageDealt = (int)Math.Round((double)hitInstance.DamageDealt * (double)FlintBrewStackBridge.FlintState.Stacks); } } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; Debug.LogWarning((object)("[FlintBrewStack] patch failed: " + ex.Message)); } } } public static class FracturedMaskBreakPatch { public static void Prefix(PlayerData __instance, ref int __state) { __state = ReadAmountLeft(); } public static void Postfix(PlayerData __instance, int __state) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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) if (__state <= 0 || ReadAmountLeft() != 0) { return; } Func<bool> isEquipped = FracturedMaskBridge.IsEquipped; if (isEquipped == null || !isEquipped()) { return; } Func<bool> tryRepair = FracturedMaskBridge.TryRepair; if (tryRepair == null || !tryRepair()) { return; } ToolItem fracturedMaskTool = Gameplay.FracturedMaskTool; if (!((Object)(object)fracturedMaskTool == (Object)null)) { Data savedData = fracturedMaskTool.SavedData; savedData.AmountLeft = ToolItemManager.GetToolStorageAmount(fracturedMaskTool); if (savedData.AmountLeft <= 0) { savedData.AmountLeft = 1; } fracturedMaskTool.SavedData = savedData; EventRegister.SendEvent(EventRegisterEvents.HealthUpdate, (GameObject)null); } } private static int ReadAmountLeft() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) try { ToolItem fracturedMaskTool = Gameplay.FracturedMaskTool; return ((Object)(object)fracturedMaskTool != (Object)null) ? fracturedMaskTool.SavedData.AmountLeft : 0; } catch (Exception ex) { Debug.LogWarning((object)("[FracturedMask] AmountLeft read failed: " + ex.Message)); return -1; } } } public static class MagnetiteDiceMagnetPatch { private sealed class MagnetDelayCache { public bool Stored; public float StartDelayMin; public float StartDelayMax; public float AttractDelayMin; public float AttractDelayMax; public float StartMoveDuration; } public static Func<string, int>? GetLevel; public static Func<string, bool>? IsEquippedInWhiteSlot; private static ManualLogSource? _log; private static bool _warned; private static FieldInfo? _startDelayField; private static FieldInfo? _attractDelayField; private static FieldInfo? _startMoveDurationField; private static FieldInfo? _minMaxStartField; private static FieldInfo? _minMaxEndField; private static readonly ConditionalWeakTable<CurrencyObjectBase, MagnetDelayCache> _cache = new ConditionalWeakTable<CurrencyObjectBase, MagnetDelayCache>(); public static void Configure(ManualLogSource log) { _log = log; _warned = false; _startDelayField = AccessTools.Field(typeof(CurrencyObjectBase), "magnetStartDelay"); _attractDelayField = AccessTools.Field(typeof(CurrencyObjectBase), "magnetAttractDelay"); _startMoveDurationField = AccessTools.Field(typeof(CurrencyObjectBase), "magnetStartMoveDuration"); Type type = Type.GetType("TeamCherry.SharedUtils.MinMaxFloat, TeamCherry.SharedUtils"); if (type != null) { _minMaxStartField = AccessTools.Field(type, "Start"); _minMaxEndField = AccessTools.Field(type, "End"); } } public static void OnToolEquipsUpdatedPrefix(CurrencyObjectBase __instance) { try { if ((Object)(object)__instance == (Object)null) { return; } int num = GetLevel?.Invoke("Magnetite Dice") ?? 0; bool num2 = num >= 2 && Gameplay.LuckyDiceTool.Status.IsEquipped; ToolEffectSpec toolEffectSpec = ToolCatalog.Find("Magnetite Dice")?.ResolveEffects(num, inWhiteSlot: false) ?? ToolEffectSpec.None; MagnetDelayCache orCreateValue = _cache.GetOrCreateValue(__instance); if (!orCreateValue.Stored) { orCreateValue.Stored = true; orCreateValue.StartDelayMin = ReadMin(_startDelayField, __instance); orCreateValue.StartDelayMax = ReadMax(_startDelayField, __instance); orCreateValue.AttractDelayMin = ReadMin(_attractDelayField, __instance); orCreateValue.AttractDelayMax = ReadMax(_attractDelayField, __instance); orCreateValue.StartMoveDuration = ((_startMoveDurationField != null) ? ((float)(_startMoveDurationField.GetValue(__instance) ?? ((object)0f))) : 0f); } if (!num2) { Restore(__instance, orCreateValue); } else if (toolEffectSpec.LuckyDiceMagnetInstant) { WriteMinMax(_startDelayField, __instance, 0f, 0f); WriteMinMax(_attractDelayField, __instance, 0f, 0f); if (_startMoveDurationField != null) { _startMoveDurationField.SetValue(__instance, 0f); } } else { double luckyDiceMagnetDelayFactor = toolEffectSpec.LuckyDiceMagnetDelayFactor; if (luckyDiceMagnetDelayFactor > 0.0 && luckyDiceMagnetDelayFactor < 1.0) { WriteMinMax(_startDelayField, __instance, orCreateValue.StartDelayMin * (float)luckyDiceMagnetDelayFactor, orCreateValue.StartDelayMax * (float)luckyDiceMagnetDelayFactor); WriteMinMax(_attractDelayField, __instance, orCreateValue.AttractDelayMin * (float)luckyDiceMagnetDelayFactor, orCreateValue.AttractDelayMax * (float)luckyDiceMagnetDelayFactor); } } } catch (Exception ex) { WarnOnce(ex); } } public static void MagnetToolIsEquippedPostfix(CurrencyObjectBase __instance, ref bool __result) { if (!__result && __instance is ShellShard) { Func<string, bool>? isEquippedInWhiteSlot = IsEquippedInWhiteSlot; if (isEquippedInWhiteSlot != null && isEquippedInWhiteSlot("Rosary Magnet")) { __result = true; } } } private static void Restore(CurrencyObjectBase instance, MagnetDelayCache cache) { WriteMinMax(_startDelayField, instance, cache.StartDelayMin, cache.StartDelayMax); WriteMinMax(_attractDelayField, instance, cache.AttractDelayMin, cache.AttractDelayMax); if (_startMoveDurationField != null) { _startMoveDurationField.SetValue(instance, cache.StartMoveDuration); } } private static float ReadMin(FieldInfo? field, CurrencyObjectBase instance) { if (field == null || _minMaxStartField == null) { return 0f; } object value = field.GetValue(instance); if (value != null) { return (float)(_minMaxStartField.GetValue(value) ?? ((object)0f)); } return 0f; } private static float ReadMax(FieldInfo? field, CurrencyObjectBase instance) { if (field == null || _minMaxEndField == null) { return 0f; } object value = field.GetValue(instance); if (value != null) { return (float)(_minMaxEndField.GetValue(value) ?? ((object)0f)); } return 0f; } private static void WriteMinMax(FieldInfo? field, CurrencyObjectBase instance, float min, float max) { if (!(field == null) && !(_minMaxStartField == null) && !(_minMaxEndField == null)) { object value = field.GetValue(instance); if (value != null) { _minMaxStartField.SetValue(value, min); _minMaxEndField.SetValue(value, max); } } } private static void WarnOnce(Exception ex) { if (!_warned) { _warned = true; ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("[MagnetiteDice] magnet patch disabled: " + ex.GetType().Name + ": " + ex.Message)); } } } } public static class MultibindFsmPatch { private sealed class ActionValueCache { public bool Stored; public bool IsInt; public bool IsFloat; public int OriginalInt; public float OriginalFloat; } private sealed class MultibindFsmMarker : MonoBehaviour { } public static Func<string, int>? GetLevel; public static Func<string, bool>? IsEquippedInWhiteSlot; public static Func<string, bool>? IsEquipped; private static ManualLogSource? _log; private static bool _warned; private static readonly List<PlayMakerFSM> _patched = new List<PlayMakerFSM>(); private static readonly ConditionalWeakTable<FsmStateAction, ActionValueCache> _cache = new ConditionalWeakTable<FsmStateAction, ActionValueCache>(); public static void Configure(ManualLogSource log) { _log = log; _warned = false; } public static void OnFsmStart(PlayMakerFSM __instance) { if ((Object)(object)__instance == (Object)null || __instance.Fsm == null) { return; } string fsmName = __instance.FsmName; if ((fsmName != "Bind" && fsmName != "Spell Control") || (Object)(object)((Component)__instance).gameObject == (Object)null || !((Object)((Component)__instance).gameObject).name.StartsWith("Hero_Hornet", StringComparison.Ordinal)) {