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.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 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 _originals = new Dictionary(); 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 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 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 IsActive { get; set; } = () => false; public static Func ToolDamageMultiplier { get; set; } = () => 1.75; public static Func IsSuperRageActive { get; set; } = () => false; public static Func ToolSilkPerHit { get; set; } = () => 0; } public static class BeastRageBridge { public static Func IsActive { get; set; } = () => false; public static Func IsSuperRageActive { get; set; } = () => false; public static Func AttackRangeMultiplier { get; set; } = () => 1.25; public static Func AttackDamageMultiplier { get; set; } = () => 1.25; public static Func AttackSpeedBonus { get; set; } = () => 0.18; public static Func IsHealBlocked { get; set; } = () => false; public static Action SetHealBlocked { get; set; } = delegate { }; public static Action SetRageActive { get; set; } = delegate { }; } public static class CloaklessRageBridge { public static Func IsActive { get; set; } = () => false; public static Func BlockChancePercent { get; set; } = () => 80; public static Func IsSuperRageActive { get; set; } = () => false; public static Func CritChance { get; set; } = () => 0.8; } public static class CursedRageBridge { public static Func IsActive { get; set; } = () => false; public static Func SkillDamageMultiplier { get; set; } = () => 1.75; public static Func IsSuperRageActive { get; set; } = () => false; } public static class HunterRageBridge { public static Func IsActive { get; set; } = () => false; public static Func IsSuperRageActive { get; set; } = () => false; public static Func ComboLayerCount { get; set; } = () => 0; public static Func SilkPerHit { get; set; } = () => 1; public static Func FocusThresholdRage { get; set; } = () => 6; public static Func FocusThresholdSuperRage { get; set; } = () => 12; } public static class RageToolBridge { public static Func IsActive { get; set; } = () => false; } public static class ReaperRageBridge { public static Func IsActive { get; set; } = () => false; public static Func IsSuperRageActive { get; set; } = () => false; public static Func SilkOrbPartsMultiplier { get; set; } = () => 3; public static Func SpawnExplosionAt { get; set; } = (Vector3 _) => false; } public static class ReaperSuperRageBridge { public static Func IsActive { get; set; } = () => false; public static Func ExplosionDamageMultiplier { get; set; } = () => 1.0; public static Func ExplosionRadius { get; set; } = () => 3f; public static Func SpawnExplosionAt { get; set; } = (double _, float _, Vector3 _) => false; } public static class ShamanRageBridge { public static Func IsActive { get; set; } = () => false; public static Func CritChance { get; set; } = () => 0.1; public static Func WaveSilkPerHit { get; set; } = () => 1; public static Func IsSuperRageActive { get; set; } = () => false; public static Func SkillCostReduction { get; set; } = () => 1; } public static class WandererRageBridge { public static Func IsActive { get; set; } = () => false; public static Func DamageMultiplier { get; set; } = () => 1.0; public static Func IsSuperRageActive { get; set; } = () => false; public static Func CritDamageMultiplier { get; set; } = () => 2.0; public static Func KnockbackMultiplier { get; set; } = () => 2.0; } public static class WandererSuperRageBridge { public static Func IsActive { get; set; } = () => false; public static Func BonusCount { get; set; } = () => 0; public static Func CritPerBonus { get; set; } = () => 0.02; } public static class WitchRageBridge { public static Func IsActive { get; set; } = () => false; public static Func DashDamageMultiplier { get; set; } = () => 1.0; public static Func DashSilkPerHit { get; set; } = () => 0; public static Func SawbladeCritChance { get; set; } = () => 0.0; public static Func SawbladeToolId { get; set; } = () => "Brolly Spike"; public static Func BindDamageMultiplier { get; set; } = () => 1.5; public static Func BindRangeMultiplier { get; set; } = () => 1.5; public static Func ScaleBindRange { get; set; } = () => true; public static Func IsSuperRageActive { get; set; } = () => false; public static Func MultiHitSilkPerHit { get; set; } = () => 1; public static Func 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 _isRageToolEquipped; private bool _started; public RageStatusDriver(RageFeature rage, StatusIconLedger ledger, GameEventHub events, Func? isRageToolEquipped = null) { _rage = rage ?? throw new ArgumentNullException("rage"); _ledger = ledger ?? throw new ArgumentNullException("ledger"); _events = events ?? throw new ArgumentNullException("events"); _isRageToolEquipped = isRageToolEquipped ?? ((Func)(() => 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 _gateActive; public string CrestId => "toolmaster"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public ArchitectRageBehavior(Func? gateActive = null) { _gateActive = gateActive ?? ((Func)(() => 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 _gateActive; public string CrestId => "warrior"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public BeastRageBehavior(Func? gateActive = null) { _gateActive = gateActive ?? ((Func)(() => 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 _gateActive; public string CrestId => "cloakless"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public CloaklessRageBehavior(Func? gateActive = null) { _gateActive = gateActive ?? ((Func)(() => 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 _gateActive; public string CrestId => "cursed"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public CursedRageBehavior(Func? gateActive = null) { _gateActive = gateActive ?? ((Func)(() => 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 _isFuryInWhiteSlot; private readonly Func _isRageEnabled; private bool _projectedByWhiteSlot; private bool _started; public FuryWhiteSlotDriver(ManualLogSource log, GameEventHub events, IGameApi game, Func isFuryInWhiteSlot, Func 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 _gateActive; public string CrestId => "hunter"; public double SuperRageDurationSeconds => _balance.SuperRageDurationSeconds; public int AttacksPerBonus => _balance.SuperRageHitsPerBonus; public HunterRageBehavior(HunterBalanceOptions balance, Func? gateActive = null) { _balance = balance ?? throw new ArgumentNullException("balance"); _gateActive = gateActive ?? ((Func)(() => 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("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 _isRageToolEquipped; private readonly Func _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? Changed; public RageFeature(ManualLogSource log, GameEventHub events, IGameApi game, RageBehaviorRegistry behaviors, RageBalanceOptions options, Func? 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)(() => 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 _gateActive; public string CrestId => "reaper"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public ReaperRageBehavior(Func? gateActive = null) { _gateActive = gateActive ?? ((Func)(() => 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 _gateActive; public string CrestId => "spell"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public ShamanRageBehavior(Func? gateActive = null) { _gateActive = gateActive ?? ((Func)(() => 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 _gateActive; public string CrestId => "wanderer"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public WandererRageBehavior(Func? gateActive = null) { _gateActive = gateActive ?? ((Func)(() => 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 _gateActive; public string CrestId => "witch"; public double SuperRageDurationSeconds => 10.0; public int AttacksPerBonus => 6; public WitchRageBehavior(Func? gateActive = null) { _gateActive = gateActive ?? ((Func)(() => 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 _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? 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)(() => true)); } public DefaultRageBehavior(RageBalanceOptions options, Func? 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 _behaviors = new Dictionary(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 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? _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? BonusReached; public RageStateMachine(IRageBehavior behavior, Action? 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.$(); 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)delegate(float delta) { _rage.Tick(delta); }); services.StatusIcons.AddTick((Action)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(); 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 rageGate(Func 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 dictionary = new Dictionary(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>)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(); } } } }