using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using TeamCherry.Localization; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Final Judge")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Final Judge")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("554080D8-4C10-49A2-BBDF-DB2662497225")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("name.ryanblocker.finaljudge", "Final Judge", "1.0.0")] public sealed class FinalJudge : BaseUnityPlugin { private const string ArenaSceneName = "Coral_Judge_Arena"; private const string BossObjectName = "Last Judge"; private const string BossControlFsmName = "Control"; private const string FlamedUpVariableName = "Flamed Up"; private const string DockPrefabBundleSearchName = "localpoolprefabs_assets_areadocks"; private static readonly string[] LavaRockDependencyBundleNames = new string[4] { "tk2dcollections_assets_areadocks.bundle", "tk2danimations_assets_areadocks.bundle", "textures_assets_areadocks.bundle", "materials_assets_areadocks.bundle" }; private const string LavaRockAssetPath = "Assets/Prefabs/Enemies/Projectiles/DF Lava Rock.prefab"; private const string BombRockAssetPath = "Assets/Prefabs/Enemies/Projectiles/DF Bomb Rock.prefab"; private const string NativeFlameWavePrefabName = "Last Judge Flame Wave"; private const string NativeStompWavePrefabName = "Last Judge Stomp Wave"; private const string NativeChargePillarPrefabName = "Last Judge Charge Bomb"; private const string NativeBossHeader = "The"; private const string NativeBossTitle = "Last Judge"; private const string FinalBossHeader = "Harbinger of Sin"; private const string FinalBossTitle = "Final Judge"; private const int FinalBossHealth = 1500; private const int FinalPhaseHealth = 600; private const int SpinLavaRockCount = 6; private const float SpinLavaRockSpeed = 30f; private const float SpinLavaRockSpawnRadius = 1.5f; private const float LavaRockInterval = 0.18f; private const float CenserShockwaveDelay = 0.5f; private const float CenserParticleWaveSpeed = 180f; private const float SlamPillarInterval = 0.1f; private const float DefaultChargePillarWidth = 4.1f; private const float SlamPillarSpacing = 3.8f; private const float NativeChargeSpeed = 40f; private const float ChargePillarInterval = 0.095f; private const float NativeChargeDuration = 0.45f; private const float ExtendedChargeDuration = 1.5f; private const float PostFinalPhaseRoarChargeDuration = 4f; private const float BombSpiralChance = 0.5f; private const float BombSpiralDuration = 3.5f; private const float BombRockInterval = 0.3f; private const float BombRockDownwardExclusionAngle = 35f; private static bool inJudgeFight; private static bool replaceBossTitleLocalization; private static bool healthSetupRunning; private static bool healthConfigured; private static bool fieryPhaseConfigured; private static bool lavaBurstSpawnedThisSpin; private static bool lavaRockPoolConfigured; private static bool throwImpactHandled; private static bool suppressNativeStompWaves; private static bool slamPillarWaveStarted; private static bool nativeStompAnticRemoved; private static bool chargePillarTrailConfigured; private static bool rageAt1100Triggered; private static bool rageAt400Triggered; private static bool rageTransitionScheduled; private static bool restoreRageRoarTransitionOnCharge; private static bool extendedChargeRunning; private static bool noStaggersConfigured; private static bool staggerIndicatorSuppressionRunning; private static bool staggerEscapeScheduled; private static bool bombSpiralActive; private static bool bombSpiralStarted; private static bool finalPhaseRoarCompleted; private static bool postFinalPhaseRoarChargePending; private static float activeExtendedChargeDuration = 1.5f; private static float judgeGravityScale = 1f; private static bool chargeDamageMaintenanceRunning; private static bool finalPhaseContactMaintenanceRunning; private static PlayMakerFSM bossControlFsm; private static HealthManager judgeHealthManager; private static GameObject lavaRockPrefab; private static GameObject bombRockPrefab; private static GameObject stompWavePrefab; private static GameObject chargePillarPrefab; private static float chargePillarWidth = 4.1f; private static readonly GameObject[] stompParticleTemplates = (GameObject[])(object)new GameObject[2]; private static readonly HashSet doubledLavaRockDamagers = new HashSet(); private static readonly List baselineContactDamagers = new List(); private static readonly FieldInfo DamageDealtField = typeof(DamageHero).GetField("damageDealt", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static float stompWaveSpeed = 12f; private static AsyncOperationHandle dockBundleHandle; private static bool dockBundleHandleValid; private static readonly List> lavaRockDependencyHandles = new List>(); private static bool warnedMissingLavaRock; public static FinalJudge Instance { get; private set; } public ManualLogSource Log => ((BaseUnityPlugin)this).Logger; private void Awake() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) Instance = this; Harmony.CreateAndPatchAll(typeof(FinalJudge), (string)null); SceneManager.sceneLoaded += OnSceneLoaded; OnSceneLoaded(SceneManager.GetActiveScene(), (LoadSceneMode)0); ((MonoBehaviour)this).StartCoroutine(LoadLavaRockPrefab()); Log.LogInfo((object)"Final Judge loaded and initialized."); } private void OnDestroy() { //IL_001d: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) SceneManager.sceneLoaded -= OnSceneLoaded; if (dockBundleHandleValid) { Addressables.Release(dockBundleHandle); dockBundleHandleValid = false; } foreach (AsyncOperationHandle lavaRockDependencyHandle in lavaRockDependencyHandles) { Addressables.Release(lavaRockDependencyHandle); } lavaRockDependencyHandles.Clear(); } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { inJudgeFight = ((Scene)(ref scene)).name == "Coral_Judge_Arena"; replaceBossTitleLocalization = false; healthSetupRunning = false; healthConfigured = false; fieryPhaseConfigured = false; lavaBurstSpawnedThisSpin = false; lavaRockPoolConfigured = false; throwImpactHandled = false; suppressNativeStompWaves = false; slamPillarWaveStarted = false; nativeStompAnticRemoved = false; chargePillarTrailConfigured = false; rageAt1100Triggered = false; rageAt400Triggered = false; rageTransitionScheduled = false; restoreRageRoarTransitionOnCharge = false; extendedChargeRunning = false; noStaggersConfigured = false; staggerIndicatorSuppressionRunning = false; staggerEscapeScheduled = false; bombSpiralActive = false; bombSpiralStarted = false; finalPhaseRoarCompleted = false; postFinalPhaseRoarChargePending = false; activeExtendedChargeDuration = 1.5f; judgeGravityScale = 1f; chargeDamageMaintenanceRunning = false; finalPhaseContactMaintenanceRunning = false; bossControlFsm = null; judgeHealthManager = null; stompWavePrefab = null; chargePillarPrefab = null; chargePillarWidth = 4.1f; stompParticleTemplates[0] = null; stompParticleTemplates[1] = null; doubledLavaRockDamagers.Clear(); baselineContactDamagers.Clear(); if (inJudgeFight) { Instance.Log.LogInfo((object)"Final Judge: Last Judge arena loaded."); } } [HarmonyPrefix] [HarmonyPatch(typeof(FsmState), "OnEnter")] private static bool PreventJudgeStaggerStateActions(FsmState __instance) { object obj; if (__instance == null) { obj = null; } else { Fsm fsm = __instance.Fsm; obj = ((fsm != null) ? fsm.FsmComponent : null); } PlayMakerFSM val = (PlayMakerFSM)obj; if (!inJudgeFight || (Object)(object)val == (Object)null || val.FsmName != "Control" || !IsJudgeHierarchy(((Component)val).transform)) { return true; } return __instance.Name.IndexOf("Stun", StringComparison.OrdinalIgnoreCase) < 0 && __instance.Name.IndexOf("Stagger", StringComparison.OrdinalIgnoreCase) < 0; } [HarmonyPrefix] [HarmonyPatch(typeof(FsmState), "OnEnter")] private static void PrepareChargeState(FsmState __instance) { object obj; if (__instance == null) { obj = null; } else { Fsm fsm = __instance.Fsm; obj = ((fsm != null) ? fsm.FsmComponent : null); } PlayMakerFSM val = (PlayMakerFSM)obj; if (!inJudgeFight || (Object)(object)val == (Object)null || val.FsmName != "Control" || !IsJudgeHierarchy(((Component)val).transform) || __instance.Name != "Charge") { return; } bool flag = postFinalPhaseRoarChargePending || (finalPhaseRoarCompleted && (Object)(object)judgeHealthManager != (Object)null && judgeHealthManager.hp <= 600); activeExtendedChargeDuration = ((flag && postFinalPhaseRoarChargePending) ? 4f : 1.5f); if (flag) { postFinalPhaseRoarChargePending = false; } FsmStateAction[] actions = __instance.Actions; foreach (FsmStateAction val2 in actions) { if (((object)val2).GetType().Name == "Wait") { SetFsmFloatField(val2, "time", flag ? activeExtendedChargeDuration : 0.45f); } else if (((object)val2).GetType().Name == "RayCast2dV2") { val2.Enabled = !flag; } } } [HarmonyPrefix] [HarmonyPatch(typeof(FsmState), "OnEnter")] private static void PrepareFinalPhaseStompAntic(FsmState __instance) { object obj; if (__instance == null) { obj = null; } else { Fsm fsm = __instance.Fsm; obj = ((fsm != null) ? fsm.FsmComponent : null); } PlayMakerFSM val = (PlayMakerFSM)obj; if (!inJudgeFight || (Object)(object)val == (Object)null || val.FsmName != "Control" || !IsJudgeHierarchy(((Component)val).transform) || __instance.Name != "Stomp Flame Antic") { return; } bool enabled = (Object)(object)judgeHealthManager != (Object)null && judgeHealthManager.hp < 600; FsmStateAction[] actions = __instance.Actions; foreach (FsmStateAction val2 in actions) { if (((object)val2).GetType().Name != "Wait") { val2.Enabled = enabled; } } } [HarmonyPrefix] [HarmonyPatch(typeof(FsmState), "OnEnter")] private static bool BeginBombSpiralInstead(FsmState __instance) { object obj; if (__instance == null) { obj = null; } else { Fsm fsm = __instance.Fsm; obj = ((fsm != null) ? fsm.FsmComponent : null); } PlayMakerFSM val = (PlayMakerFSM)obj; if (!inJudgeFight || (Object)(object)val == (Object)null || val.FsmName != "Control" || !IsJudgeHierarchy(((Component)val).transform) || __instance.Name != "Spin Antic" || bombSpiralActive || (Object)(object)judgeHealthManager == (Object)null || judgeHealthManager.hp >= 600 || Random.value > 0.5f) { return true; } bombSpiralActive = true; bombSpiralStarted = false; Instance.Log.LogInfo((object)"Final Judge: converted the current spiral into a bomb-rock spiral."); return true; } [HarmonyPostfix] [HarmonyPatch(typeof(FsmState), "OnEnter")] private static void OnFsmStateEntered(FsmState __instance) { //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) object obj; if (__instance == null) { obj = null; } else { Fsm fsm = __instance.Fsm; obj = ((fsm != null) ? fsm.FsmComponent : null); } PlayMakerFSM val = (PlayMakerFSM)obj; if (!inJudgeFight || (Object)(object)val == (Object)null || val.FsmName != "Control" || !IsJudgeHierarchy(((Component)val).transform)) { return; } bossControlFsm = val; Rigidbody2D component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && component.gravityScale > 0f) { judgeGravityScale = component.gravityScale; } CacheStompParticleTemplates(val); CacheChargePillarPrefab(val); ConfigureChargePillarTrail(val); ConfigureNoStaggers(val); RemoveNativeStompAntic(val); ConfigureFieryPhase(val); if (__instance.Name == "Idle" && (Object)(object)judgeHealthManager != (Object)null && judgeHealthManager.hp > 1100) { CacheBaselineContactDamagers(((Component)val).transform); } if (__instance.Name == "Stomp JumpAntic") { slamPillarWaveStarted = false; suppressNativeStompWaves = (Object)(object)judgeHealthManager == (Object)null || judgeHealthManager.hp >= 600; } else if (__instance.Name == "Stomp Land" && !slamPillarWaveStarted) { slamPillarWaveStarted = true; Vector3 origin = FindNamedChildPosition(((Component)val).transform, "Stomp Flame Point"); ((MonoBehaviour)Instance).StartCoroutine(SpawnSlamPillarWaves(origin)); } else if (__instance.Name == "Stomp Flame Antic") { suppressNativeStompWaves = (Object)(object)judgeHealthManager == (Object)null || judgeHealthManager.hp >= 600; } else if (__instance.Name == "Stomp Recover" || __instance.Name == "Idle") { suppressNativeStompWaves = false; } if (__instance.Name == "Throw Antic") { throwImpactHandled = false; } else if (__instance.Name == "Censer Slam" && !throwImpactHandled) { throwImpactHandled = true; Vector3 impactPosition = FindCenserImpactPosition(((Component)val).transform); SpawnCenserImpactAttack(impactPosition); } if (__instance.Name == "Spin Antic") { lavaBurstSpawnedThisSpin = false; if (!bombSpiralActive && (Object)(object)judgeHealthManager != (Object)null && judgeHealthManager.hp < 1100) { ArmNativeFourWaveSpin(val); } } else if (__instance.Name == "Flame Dir") { lavaBurstSpawnedThisSpin = false; } else if (__instance.Name == "Charge") { if (!chargeDamageMaintenanceRunning) { chargeDamageMaintenanceRunning = true; ((MonoBehaviour)Instance).StartCoroutine(MaintainChargeContactDamage(val)); } if ((activeExtendedChargeDuration == 4f || (finalPhaseRoarCompleted && (Object)(object)judgeHealthManager != (Object)null && judgeHealthManager.hp <= 600)) && !extendedChargeRunning) { extendedChargeRunning = true; ((MonoBehaviour)Instance).StartCoroutine(ControlExtendedCharge(val, activeExtendedChargeDuration)); } } else if (__instance.Name == "Charge Antic 1" && restoreRageRoarTransitionOnCharge) { finalPhaseRoarCompleted = true; if (!finalPhaseContactMaintenanceRunning) { finalPhaseContactMaintenanceRunning = true; ((MonoBehaviour)Instance).StartCoroutine(MaintainFinalPhaseContactDamage(val)); } RestoreRageRoarTransition(val); } if (__instance.Name == "Rage Check") { ScheduleThresholdRageIfNeeded(val); } if (bombSpiralActive && (__instance.Name == "Idle" || __instance.Name == "Throw Antic" || __instance.Name == "Stomp JumpAntic" || __instance.Name == "Charge Antic 1")) { bombSpiralActive = false; bombSpiralStarted = false; Instance.Log.LogInfo((object)"Final Judge: bomb-rock spiral finished and normal projectiles were restored."); } if ((__instance.Name.IndexOf("Stun", StringComparison.OrdinalIgnoreCase) >= 0 || __instance.Name.IndexOf("Stagger", StringComparison.OrdinalIgnoreCase) >= 0) && !staggerEscapeScheduled) { staggerEscapeScheduled = true; ((MonoBehaviour)Instance).StartCoroutine(EscapeStaggerState(val)); } if (!healthSetupRunning && !healthConfigured) { healthSetupRunning = true; ((MonoBehaviour)Instance).StartCoroutine(FindAndSetJudgeHealth()); } } [HarmonyPrefix] [HarmonyPatch(typeof(FSMActivator), "ActivateStaggered")] private static bool SuppressJudgeStaggerIndicator(FSMActivator __instance) { return !inJudgeFight || (Object)(object)__instance == (Object)null || !IsJudgeHierarchy(((Component)__instance).transform); } private static IEnumerator LoadLavaRockPrefab() { yield return Addressables.InitializeAsync(true); IResourceLocation dockBundleLocation = null; List dependencyLocations = new List(); HashSet dependencyIds = new HashSet(StringComparer.OrdinalIgnoreCase); IList locations = default(IList); foreach (IResourceLocator locator in Addressables.ResourceLocators) { foreach (object key in locator.Keys) { if (!locator.Locate(key, (Type)null, ref locations) || locations == null) { continue; } foreach (IResourceLocation location in locations) { if (location.ResourceType == typeof(IAssetBundleResource) && !string.IsNullOrEmpty(location.InternalId)) { string[] lavaRockDependencyBundleNames = LavaRockDependencyBundleNames; foreach (string dependencyName in lavaRockDependencyBundleNames) { if (location.InternalId.EndsWith(dependencyName, StringComparison.OrdinalIgnoreCase) && dependencyIds.Add(location.InternalId)) { dependencyLocations.Add(location); break; } } if (location.InternalId.IndexOf("areadocks", StringComparison.OrdinalIgnoreCase) >= 0 && (location.InternalId.IndexOf("textures_assets_", StringComparison.OrdinalIgnoreCase) >= 0 || location.InternalId.IndexOf("materials_assets_", StringComparison.OrdinalIgnoreCase) >= 0) && dependencyIds.Add(location.InternalId)) { dependencyLocations.Add(location); } } if (location.ResourceType == typeof(IAssetBundleResource) && !string.IsNullOrEmpty(location.InternalId) && location.InternalId.IndexOf("localpoolprefabs_assets_areadocks", StringComparison.OrdinalIgnoreCase) >= 0) { dockBundleLocation = location; break; } } locations = null; } } if (dockBundleLocation == null) { Instance.Log.LogError((object)"Final Judge [LAVA ROCK]: Deep Docks prefab bundle location was not found."); yield break; } foreach (IResourceLocation dependencyLocation in dependencyLocations) { AsyncOperationHandle dependencyHandle = Addressables.LoadAssetAsync(dependencyLocation); lavaRockDependencyHandles.Add(dependencyHandle); yield return dependencyHandle; if ((int)dependencyHandle.Status != 1) { Instance.Log.LogError((object)("Final Judge [LAVA ROCK]: failed to load dependency bundle " + dependencyLocation.InternalId)); yield break; } } Instance.Log.LogInfo((object)("Final Judge: loaded " + dependencyLocations.Count + " Deep Docks tk2d rendering dependency bundles.")); dockBundleHandle = Addressables.LoadAssetAsync(dockBundleLocation); dockBundleHandleValid = true; yield return dockBundleHandle; if ((int)dockBundleHandle.Status != 1 || dockBundleHandle.Result == null) { Instance.Log.LogError((object)"Final Judge [LAVA ROCK]: Addressables failed to load the Deep Docks prefab bundle."); yield break; } AssetBundle bundle = dockBundleHandle.Result.GetAssetBundle(); if ((Object)(object)bundle == (Object)null) { Instance.Log.LogError((object)"Final Judge [LAVA ROCK]: managed Deep Docks prefab bundle was null."); yield break; } AssetBundleRequest lavaRockRequest = bundle.LoadAssetAsync("Assets/Prefabs/Enemies/Projectiles/DF Lava Rock.prefab"); yield return lavaRockRequest; Object asset = lavaRockRequest.asset; lavaRockPrefab = (GameObject)(object)((asset is GameObject) ? asset : null); if ((Object)(object)lavaRockPrefab == (Object)null) { Instance.Log.LogError((object)"Final Judge [LAVA ROCK]: failed to deserialize DF Lava Rock after its dependencies."); yield break; } AssetBundleRequest bombRockRequest = bundle.LoadAssetAsync("Assets/Prefabs/Enemies/Projectiles/DF Bomb Rock.prefab"); yield return bombRockRequest; Object asset2 = bombRockRequest.asset; bombRockPrefab = (GameObject)(object)((asset2 is GameObject) ? asset2 : null); if ((Object)(object)bombRockPrefab == (Object)null) { AssetBundleRequest allGameObjectsRequest = bundle.LoadAllAssetsAsync(); yield return allGameObjectsRequest; Object[] allAssets = allGameObjectsRequest.allAssets; foreach (Object asset3 in allAssets) { GameObject candidate = (GameObject)(object)((asset3 is GameObject) ? asset3 : null); if ((Object)(object)candidate != (Object)null && ((Object)candidate).name == "DF Bomb Rock") { bombRockPrefab = candidate; break; } } } if ((Object)(object)bombRockPrefab == (Object)null) { Instance.Log.LogError((object)"Final Judge [BOMB ROCK]: DF Bomb Rock was not found in the managed bundle."); } else { Instance.Log.LogInfo((object)"Final Judge: loaded DF Lava Rock and DF Bomb Rock through the game-managed Addressables bundle."); } } [HarmonyPostfix] [HarmonyPatch(typeof(ObjectPool), "Spawn", new Type[] { typeof(GameObject), typeof(Transform), typeof(Vector3), typeof(Quaternion), typeof(bool) })] private static void HandleJudgePooledPrefab(GameObject prefab, GameObject __result) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00d2: 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_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) if (!inJudgeFight || (Object)(object)prefab == (Object)null) { return; } if (((Object)prefab).name == "Last Judge Stomp Wave") { stompWavePrefab = prefab; Rigidbody2D val = (((Object)(object)__result != (Object)null) ? __result.GetComponent() : null); if ((Object)(object)val != (Object)null && Mathf.Abs(val.linearVelocity.x) > 0.1f) { stompWaveSpeed = Mathf.Abs(val.linearVelocity.x); } } else if (((Object)prefab).name == "Last Judge Charge Bomb") { chargePillarPrefab = prefab; Renderer val2 = (((Object)(object)__result != (Object)null) ? __result.GetComponentInChildren(true) : null); if ((Object)(object)val2 != (Object)null) { Bounds bounds = val2.bounds; if (((Bounds)(ref bounds)).size.x > 0.1f) { bounds = val2.bounds; chargePillarWidth = ((Bounds)(ref bounds)).size.x; } } } if (bombSpiralActive && ((Object)prefab).name == "Last Judge Flame Wave") { if ((Object)(object)__result != (Object)null) { __result.SetActive(false); } if (!bombSpiralStarted && (Object)(object)bossControlFsm != (Object)null) { bombSpiralStarted = true; ((MonoBehaviour)Instance).StartCoroutine(RunBombSpiralWindow(bossControlFsm)); } } else if (!bombSpiralActive && !lavaBurstSpawnedThisSpin && !(((Object)prefab).name != "Last Judge Flame Wave") && !((Object)(object)bossControlFsm == (Object)null)) { lavaBurstSpawnedThisSpin = true; ((MonoBehaviour)Instance).StartCoroutine(SpawnSpinLavaRocks(((Component)bossControlFsm).transform.position)); Instance.Log.LogInfo((object)"Final Judge: first flame wave released; starting lava-rock sequence."); } } [HarmonyPrefix] [HarmonyPatch(typeof(ObjectPool), "Spawn", new Type[] { typeof(GameObject), typeof(Transform), typeof(Vector3), typeof(Quaternion), typeof(bool) })] private static bool SuppressOriginalSlamFireWaves(GameObject prefab, Vector3 __2, ref GameObject __result) { if (inJudgeFight && suppressNativeStompWaves && (Object)(object)prefab != (Object)null && ((Object)prefab).name == "Last Judge Stomp Wave") { __result = null; return false; } return true; } private static Vector3 FindNamedChildPosition(Transform root, string childName) { //IL_003a: 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_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_0043: Unknown result type (might be due to invalid IL or missing references) Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if (((Object)val).name == childName) { return val.position; } } return root.position; } private static IEnumerator SpawnSlamPillarWaves(Vector3 origin) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chargePillarPrefab == (Object)null) { GameObject[] array = Resources.FindObjectsOfTypeAll(); foreach (GameObject candidate in array) { if (((Object)candidate).name == "Last Judge Charge Bomb") { chargePillarPrefab = candidate; break; } } } if ((Object)(object)chargePillarPrefab == (Object)null) { Instance.Log.LogWarning((object)"Final Judge [SLAM]: native charge-pillar prefab was not found."); yield break; } float halfWidth = chargePillarWidth * 0.5f; int terrainMask = LayerMask.GetMask(new string[1] { "Terrain" }); Vector2 rayOrigin = new Vector2(origin.x, origin.y + 0.5f); RaycastHit2D leftHit = Physics2D.Raycast(rayOrigin, Vector2.left, 100f, terrainMask); RaycastHit2D rightHit = Physics2D.Raycast(rayOrigin, Vector2.right, 100f, terrainMask); float leftEdge = (((Object)(object)((RaycastHit2D)(ref leftHit)).collider != (Object)null) ? ((RaycastHit2D)(ref leftHit)).point.x : 8f); float rightEdge = (((Object)(object)((RaycastHit2D)(ref rightHit)).collider != (Object)null) ? ((RaycastHit2D)(ref rightHit)).point.x : 76f); List leftPositions = new List(); List rightPositions = new List(); for (float x = origin.x - 3.8f; x >= leftEdge + halfWidth; x -= 3.8f) { leftPositions.Add(x); } for (float x2 = origin.x + 3.8f; x2 <= rightEdge - halfWidth; x2 += 3.8f) { rightPositions.Add(x2); } int omittedLeft = ((leftPositions.Count > 1) ? Random.Range(1, leftPositions.Count) : (-1)); int omittedRight = ((rightPositions.Count > 1) ? Random.Range(1, rightPositions.Count) : (-1)); int tiers = Mathf.Max(leftPositions.Count, rightPositions.Count); for (int index = 0; index < tiers; index++) { if (index < leftPositions.Count && index != omittedLeft) { ObjectPoolExtensions.Spawn(chargePillarPrefab, new Vector3(leftPositions[index], origin.y, origin.z), Quaternion.identity); } if (index < rightPositions.Count && index != omittedRight) { ObjectPoolExtensions.Spawn(chargePillarPrefab, new Vector3(rightPositions[index], origin.y, origin.z), Quaternion.identity); } if (index + 1 < tiers) { yield return (object)new WaitForSeconds(0.1f); } } Instance.Log.LogInfo((object)"Final Judge: replaced slam fire waves with outward charge-pillar waves; one pillar was omitted on each side."); } private static Vector3 FindCenserImpactPosition(Transform judge) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00e1: 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_0061: Unknown result type (might be due to invalid IL or missing references) Transform val = null; Transform[] componentsInChildren = ((Component)judge).GetComponentsInChildren(true); foreach (Transform val2 in componentsInChildren) { if (((Object)val2).name.IndexOf("Censer", StringComparison.OrdinalIgnoreCase) >= 0 && ((Object)(object)val == (Object)null || (((Component)val2).gameObject.activeInHierarchy && !((Component)val).gameObject.activeInHierarchy) || val2.position.y < val.position.y)) { val = val2; } } if ((Object)(object)val != (Object)null) { Instance.Log.LogInfo((object)("Final Judge: using censer impact object '" + GetHierarchyPath(val) + "'.")); return val.position; } Instance.Log.LogWarning((object)"Final Judge [CENSER]: live censer transform was not found; using Judge position."); return judge.position; } private static void SpawnCenserImpactAttack(Vector3 impactPosition) { //IL_004f: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_019f: 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_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)judgeHealthManager != (Object)null && judgeHealthManager.hp <= 1100; int num = (flag ? 4 : Random.Range(2, 6)); if (flag && (Object)(object)bombRockPrefab != (Object)null) { for (int i = 0; i < num; i++) { Vector2 randomBombRockDirection = GetRandomBombRockDirection(); SpawnBombRock(impactPosition + Vector2.op_Implicit(randomBombRockDirection * 0.5f), randomBombRockDirection * 30f); } } else if (!flag && (Object)(object)lavaRockPrefab != (Object)null) { if (!lavaRockPoolConfigured) { ObjectPool.CreatePool(lavaRockPrefab, 6, false); lavaRockPoolConfigured = true; } float num2 = Random.Range(0f, 360f); Vector2 val = default(Vector2); for (int j = 0; j < num; j++) { float num3 = num2 + 360f * (float)j / (float)num; float num4 = num3 * ((float)Math.PI / 180f); ((Vector2)(ref val))..ctor(Mathf.Cos(num4), Mathf.Sin(num4)); GameObject val2 = ObjectPoolExtensions.Spawn(lavaRockPrefab, impactPosition + Vector2.op_Implicit(val * 0.5f)); RepairLavaRockSprite(val2); DoubleLavaRockDamage(val2); MakeProjectilePlayerOnly(val2); Rigidbody2D component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { component.linearVelocity = val * 30f; } } } ((MonoBehaviour)Instance).StartCoroutine(SpawnCenserParticleWaveAndShockwaves(impactPosition)); Instance.Log.LogInfo((object)("Final Judge: censer impact sprayed " + num + (flag ? " bomb rocks" : " lava rocks") + "; shockwaves follow after " + 0.5f + " seconds.")); } private static IEnumerator SpawnCenserParticleWaveAndShockwaves(Vector3 impactPosition) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) GameObject[] particleWaves = (GameObject[])(object)new GameObject[2]; for (int index = 0; index < particleWaves.Length; index++) { GameObject template = stompParticleTemplates[index]; if (!((Object)(object)template == (Object)null)) { int directionSign = ((index != 0) ? 1 : (-1)); particleWaves[index] = Object.Instantiate(template); particleWaves[index].transform.position = impactPosition + Vector3.right * (2f * (float)directionSign); particleWaves[index].SetActive(true); ParticleSystem[] componentsInChildren = particleWaves[index].GetComponentsInChildren(true); foreach (ParticleSystem particles in componentsInChildren) { particles.Play(true); } } } float elapsed = 0f; while (elapsed < 0.5f) { float step = Mathf.Min(Time.deltaTime, 0.5f - elapsed); for (int j = 0; j < particleWaves.Length; j++) { if ((Object)(object)particleWaves[j] != (Object)null) { int directionSign2 = ((j != 0) ? 1 : (-1)); particleWaves[j].transform.Translate(Vector3.right * ((float)directionSign2 * 180f * step), (Space)0); } } elapsed += step; yield return null; } if ((Object)(object)stompWavePrefab == (Object)null) { GameObject[] array = Resources.FindObjectsOfTypeAll(); foreach (GameObject candidate in array) { if (((Object)candidate).name == "Last Judge Stomp Wave") { stompWavePrefab = candidate; break; } } } if ((Object)(object)stompWavePrefab != (Object)null) { for (int l = -1; l <= 1; l += 2) { GameObject wave = ObjectPoolExtensions.Spawn(stompWavePrefab, impactPosition, Quaternion.identity); Rigidbody2D body = wave.GetComponent(); if ((Object)(object)body != (Object)null) { body.linearVelocity = new Vector2((float)l * stompWaveSpeed, body.linearVelocity.y); } Vector3 scale = wave.transform.localScale; scale.x = Mathf.Abs(scale.x) * (float)l; wave.transform.localScale = scale; } } else { Instance.Log.LogWarning((object)"Final Judge [CENSER]: stomp-wave prefab has not been observed yet."); } GameObject[] array2 = particleWaves; foreach (GameObject particleWave in array2) { if ((Object)(object)particleWave != (Object)null) { Object.Destroy((Object)(object)particleWave, 3f); } } } private static IEnumerator SpawnSpinLavaRocks(Vector3 origin) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)lavaRockPrefab == (Object)null) { if (!warnedMissingLavaRock) { warnedMissingLavaRock = true; Instance.Log.LogWarning((object)"Final Judge [LAVA ROCK]: projectile prefab was not ready for the spin."); } yield break; } if (!lavaRockPoolConfigured) { ObjectPool.CreatePool(lavaRockPrefab, 6, false); lavaRockPoolConfigured = true; } for (int index = 0; index < 6; index++) { float angle = Random.Range(0f, 360f); float radians = angle * ((float)Math.PI / 180f); Vector2 direction = new Vector2(Mathf.Cos(radians), Mathf.Sin(radians)); Vector3 spawnPosition = origin + Vector2.op_Implicit(direction * 1.5f); GameObject lavaRock = ObjectPoolExtensions.Spawn(lavaRockPrefab, spawnPosition); RepairLavaRockSprite(lavaRock); DoubleLavaRockDamage(lavaRock); MakeProjectilePlayerOnly(lavaRock); Rigidbody2D body = lavaRock.GetComponent(); if ((Object)(object)body != (Object)null) { body.linearVelocity = direction * 30f; } if (index + 1 < 6) { yield return (object)new WaitForSeconds(0.18f); } } Instance.Log.LogInfo((object)"Final Judge: spawned 6 high-speed DF Lava Rocks at fixed 0.18 second intervals."); } private static IEnumerator SpawnBombSpiralRocks(PlayMakerFSM controlFsm) { if ((Object)(object)bombRockPrefab == (Object)null) { Instance.Log.LogWarning((object)"Final Judge [BOMB SPIRAL]: bomb-rock prefab was not ready."); yield break; } for (int index = 0; index < 8; index++) { if (!inJudgeFight) { break; } if (!((Object)(object)controlFsm != (Object)null)) { break; } Vector2 direction = GetRandomBombRockDirection(); Vector3 origin = ((Component)controlFsm).transform.position + Vector2.op_Implicit(direction * 0.75f) + Vector3.up; SpawnBombRock(origin, direction * 30f); if (index + 1 < 8) { yield return (object)new WaitForSeconds(0.3f); } } Instance.Log.LogInfo((object)("Final Judge: bomb-rock spiral launched " + 8 + " rocks with random trajectories.")); } private static IEnumerator RunBombSpiralWindow(PlayMakerFSM controlFsm) { Rigidbody2D body = ((Component)controlFsm).GetComponent(); Vector3 holdPosition = ((Component)controlFsm).transform.position; ((Behaviour)controlFsm).enabled = false; ((MonoBehaviour)Instance).StartCoroutine(SpawnBombSpiralRocks(controlFsm)); Instance.Log.LogInfo((object)"Final Judge: thurible spiral began; starting the 3.5-second bomb-rock window."); float elapsed = 0f; while (elapsed < 3.5f && inJudgeFight && (Object)(object)controlFsm != (Object)null && bombSpiralActive) { ((Component)controlFsm).transform.position = holdPosition; if ((Object)(object)body != (Object)null) { body.linearVelocity = Vector2.zero; } elapsed += Time.deltaTime; yield return null; } if (!((Object)(object)controlFsm == (Object)null)) { ((Behaviour)controlFsm).enabled = true; if ((Object)(object)body != (Object)null) { body.linearVelocity = Vector2.zero; } if (inJudgeFight && bombSpiralActive) { bombSpiralActive = false; bombSpiralStarted = false; controlFsm.SetState("Idle"); Instance.Log.LogInfo((object)"Final Judge: completed the 3.5-second bomb-rock spiral."); } } } private static Vector2 GetRandomBombRockDirection() { //IL_0042: 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_004a: Unknown result type (might be due to invalid IL or missing references) float num; do { num = Random.Range(0f, 360f); } while (Mathf.Abs(Mathf.DeltaAngle(num, 270f)) < 35f); float num2 = num * ((float)Math.PI / 180f); return new Vector2(Mathf.Cos(num2), Mathf.Sin(num2)); } private static void RepairLavaRockSprite(GameObject lavaRock) { if ((Object)(object)lavaRock == (Object)null) { return; } tk2dSprite component = lavaRock.GetComponent(); tk2dSpriteAnimator component2 = lavaRock.GetComponent(); if ((Object)(object)component == (Object)null) { Instance.Log.LogWarning((object)"Final Judge [LAVA ROCK]: spawned rock has no tk2dSprite component."); return; } tk2dSpriteCollectionData val = ((tk2dBaseSprite)component).Collection; int spriteId = ((tk2dBaseSprite)component).spriteId; if (((Object)(object)val == (Object)null || !val.IsValidSpriteId(spriteId)) && (Object)(object)component2 != (Object)null) { tk2dSpriteAnimationClip defaultClip = component2.DefaultClip; if (defaultClip != null && defaultClip.frames != null && defaultClip.frames.Length != 0) { val = defaultClip.frames[0].spriteCollection; spriteId = defaultClip.frames[0].spriteId; } } if ((Object)(object)val == (Object)null || !val.IsValidSpriteId(spriteId)) { Instance.Log.LogWarning((object)"Final Judge [LAVA ROCK]: no valid tk2d sprite collection/frame was available."); return; } ((tk2dBaseSprite)component).SetSprite(val, spriteId); ((tk2dBaseSprite)component).ForceBuild(); Renderer component3 = ((Component)component).GetComponent(); if ((Object)(object)component3 != (Object)null) { component3.enabled = true; } } private static void DoubleLavaRockDamage(GameObject lavaRock) { if ((Object)(object)lavaRock == (Object)null || DamageDealtField == null) { return; } DamageHero[] componentsInChildren = lavaRock.GetComponentsInChildren(true); foreach (DamageHero val in componentsInChildren) { if (doubledLavaRockDamagers.Add(((Object)val).GetInstanceID())) { int num = (int)DamageDealtField.GetValue(val); val.SetDamageAmount(num * 2); } } } private static void SpawnBombRock(Vector3 position, Vector2 velocity) { //IL_0018: 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 (!((Object)(object)bombRockPrefab == (Object)null)) { GameObject val = ObjectPoolExtensions.Spawn(bombRockPrefab, position); RepairLavaRockSprite(val); RepairBombRockParticleMaterials(val); MakeProjectilePlayerOnly(val); Rigidbody2D component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.linearVelocity = velocity; } } } private static void RepairBombRockParticleMaterials(GameObject bombRock) { ParticleSystemRenderer[] componentsInChildren = bombRock.GetComponentsInChildren(true); foreach (ParticleSystemRenderer val in componentsInChildren) { bool flag = ((Renderer)val).sharedMaterials.Length == 0; Material[] sharedMaterials = ((Renderer)val).sharedMaterials; foreach (Material val2 in sharedMaterials) { if ((Object)(object)val2 == (Object)null || (Object)(object)val2.shader == (Object)null || !val2.shader.isSupported || (Object)(object)val2.mainTexture == (Object)null) { flag = true; break; } } if (flag) { ((Renderer)val).enabled = false; } } } private static void MakeProjectilePlayerOnly(GameObject projectile) { if (!((Object)(object)projectile == (Object)null)) { DamageEnemies[] componentsInChildren = projectile.GetComponentsInChildren(true); foreach (DamageEnemies val in componentsInChildren) { ((Behaviour)val).enabled = false; } FireballProjectile[] componentsInChildren2 = projectile.GetComponentsInChildren(true); foreach (FireballProjectile val2 in componentsInChildren2) { ((object)val2).GetType().GetField("enemyDamageAmount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(val2, 0); } } } private static void ArmNativeFourWaveSpin(PlayMakerFSM controlFsm) { FsmState state = controlFsm.Fsm.GetState("Rerage?"); if (state == null) { return; } FsmStateAction[] actions = state.Actions; foreach (FsmStateAction val in actions) { FieldInfo[] fields = ((object)val).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { object value = fieldInfo.GetValue(val); if (((object)val).GetType().Name == "BoolTest" && fieldInfo.Name == "boolVariable") { FsmBool val2 = (FsmBool)((value is FsmBool) ? value : null); if (val2 != null) { val2.Value = true; continue; } } if (((object)val).GetType().Name == "IntAdd" && fieldInfo.Name == "intVariable") { FsmInt val3 = (FsmInt)((value is FsmInt) ? value : null); if (val3 != null) { val3.Value = 4; } } } } Instance.Log.LogInfo((object)"Final Judge: armed the native four-wave rage spin below 1100 HP."); } private static IEnumerator ControlExtendedCharge(PlayMakerFSM controlFsm, float duration) { Rigidbody2D body = ((Component)controlFsm).GetComponent(); Transform chargeCollider = FindNamedChild(((Component)controlFsm).transform, "Charge Collider"); float elapsed = 0f; while (elapsed < duration && inJudgeFight && (Object)(object)controlFsm != (Object)null && controlFsm.ActiveStateName == "Charge") { EnsureChargeContactDamage(chargeCollider); if ((Object)(object)body != (Object)null) { float direction = Mathf.Sign(body.linearVelocity.x); if (direction == 0f) { direction = 0f - Mathf.Sign(((Component)controlFsm).transform.localScale.x); } int terrainMask = LayerMask.GetMask(new string[1] { "Terrain" }); Vector2 rayOrigin = new Vector2(((Component)controlFsm).transform.position.x, ((Component)controlFsm).transform.position.y + 1f); RaycastHit2D wall = Physics2D.Raycast(rayOrigin, Vector2.right * direction, 2.5f, terrainMask); if ((Object)(object)((RaycastHit2D)(ref wall)).collider != (Object)null) { Vector3 scale = ((Component)controlFsm).transform.localScale; scale.x *= -1f; ((Component)controlFsm).transform.localScale = scale; body.linearVelocity = new Vector2((0f - direction) * 40f, body.linearVelocity.y); Transform transform = ((Component)controlFsm).transform; transform.position += Vector3.right * ((0f - direction) * 0.2f); } wall = default(RaycastHit2D); } elapsed += Time.deltaTime; yield return null; } extendedChargeRunning = false; Instance.Log.LogInfo((object)("Final Judge: completed the " + duration + "-second wall-bouncing charge.")); } private static IEnumerator MaintainChargeContactDamage(PlayMakerFSM controlFsm) { Transform chargeCollider = FindNamedChild(((Component)controlFsm).transform, "Charge Collider"); while (inJudgeFight && (Object)(object)controlFsm != (Object)null && controlFsm.ActiveStateName == "Charge") { if ((Object)(object)judgeHealthManager != (Object)null) { judgeHealthManager.IsInvincible = false; } EnsureChargeContactDamage(chargeCollider); EnsureNamedChargeDamagers(((Component)controlFsm).transform); yield return null; } chargeDamageMaintenanceRunning = false; } private static IEnumerator MaintainFinalPhaseContactDamage(PlayMakerFSM controlFsm) { while (inJudgeFight && (Object)(object)controlFsm != (Object)null && (Object)(object)judgeHealthManager != (Object)null) { foreach (DamageHero damager in baselineContactDamagers) { if ((Object)(object)damager == (Object)null) { continue; } ((Component)damager).gameObject.SetActive(true); ((Behaviour)damager).enabled = true; if (DamageDealtField != null) { int damage = (int)DamageDealtField.GetValue(damager); if (damage < 1) { damager.SetDamageAmount(1); } } Collider2D[] componentsInChildren = ((Component)damager).GetComponentsInChildren(true); foreach (Collider2D collider in componentsInChildren) { ((Behaviour)collider).enabled = true; } } yield return null; } finalPhaseContactMaintenanceRunning = false; } private static void CacheBaselineContactDamagers(Transform bossRoot) { if ((Object)(object)bossRoot == (Object)null || baselineContactDamagers.Count > 0) { return; } DamageHero[] componentsInChildren = ((Component)bossRoot).GetComponentsInChildren(true); foreach (DamageHero val in componentsInChildren) { if (((Component)val).gameObject.activeInHierarchy && ((Behaviour)val).enabled) { baselineContactDamagers.Add(val); } } Instance.Log.LogInfo((object)("Final Judge: cached " + baselineContactDamagers.Count + " active pre-roar contact damagers for phase preservation.")); } private static Transform FindNamedChild(Transform root, string childName) { Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if (((Object)val).name == childName) { return val; } } return null; } private static void EnsureChargeContactDamage(Transform chargeCollider) { if ((Object)(object)chargeCollider == (Object)null) { return; } ((Component)chargeCollider).gameObject.SetActive(true); Collider2D[] componentsInChildren = ((Component)chargeCollider).GetComponentsInChildren(true); foreach (Collider2D val in componentsInChildren) { ((Behaviour)val).enabled = true; } DamageHero[] componentsInChildren2 = ((Component)chargeCollider).GetComponentsInChildren(true); foreach (DamageHero val2 in componentsInChildren2) { ((Behaviour)val2).enabled = true; if (DamageDealtField != null) { int num = (int)DamageDealtField.GetValue(val2); if (num < 1) { val2.SetDamageAmount(1); } } } } private static void EnsureNamedChargeDamagers(Transform bossRoot) { DamageHero[] componentsInChildren = ((Component)bossRoot).GetComponentsInChildren(true); foreach (DamageHero val in componentsInChildren) { if (GetHierarchyPath(((Component)val).transform).IndexOf("Charge", StringComparison.OrdinalIgnoreCase) < 0) { continue; } ((Component)val).gameObject.SetActive(true); ((Behaviour)val).enabled = true; Collider2D[] componentsInChildren2 = ((Component)val).GetComponentsInChildren(true); foreach (Collider2D val2 in componentsInChildren2) { ((Behaviour)val2).enabled = true; } if (DamageDealtField != null) { int num = (int)DamageDealtField.GetValue(val); if (num < 1) { val.SetDamageAmount(1); } } } } private static void SetFsmFloatField(FsmStateAction action, string fieldName, float value) { object? obj = ((object)action).GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(action); FsmFloat val = (FsmFloat)((obj is FsmFloat) ? obj : null); if (val != null) { val.Value = value; } } private static void ConfigureFieryPhase(PlayMakerFSM controlFsm) { if (!fieryPhaseConfigured && !((Object)(object)controlFsm == (Object)null)) { FsmBool val = controlFsm.FsmVariables.FindFsmBool("Flamed Up"); if (val == null) { Instance.Log.LogWarning((object)"Final Judge [PHASE]: Control FSM variable 'Flamed Up' was not found."); return; } val.Value = true; fieryPhaseConfigured = true; Instance.Log.LogInfo((object)"Final Judge: enabled the native 'Flamed Up' phase at fight startup; fiery attacks are active."); } } private static void CacheStompParticleTemplates(PlayMakerFSM controlFsm) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Invalid comparison between Unknown and I4 if ((Object)(object)controlFsm == (Object)null || (Object)(object)stompParticleTemplates[0] != (Object)null) { return; } FsmState state = controlFsm.Fsm.GetState("Stomp Flame Antic"); if (state == null) { return; } int num = 0; FsmStateAction[] actions = state.Actions; foreach (FsmStateAction val in actions) { if (((object)val).GetType().Name != "PlayParticleEmitter" || num >= stompParticleTemplates.Length) { continue; } FieldInfo[] fields = ((object)val).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { object? value = fieldInfo.GetValue(val); FsmOwnerDefault val2 = (FsmOwnerDefault)((value is FsmOwnerDefault) ? value : null); if (val2 != null) { GameObject val3 = ((val2.GameObject != null) ? val2.GameObject.Value : null); if ((Object)(object)val3 == (Object)null && (int)val2.OwnerOption == 0) { val3 = ((Component)controlFsm).gameObject; } if ((Object)(object)val3 != (Object)null) { stompParticleTemplates[num++] = val3; break; } } } } Instance.Log.LogInfo((object)("Final Judge: cached " + num + " native stomp particle-wave emitters.")); } private static void CacheChargePillarPrefab(PlayMakerFSM controlFsm) { if ((Object)(object)chargePillarPrefab != (Object)null || (Object)(object)controlFsm == (Object)null) { return; } FsmState state = controlFsm.Fsm.GetState("Charge"); if (state == null) { return; } FsmStateAction[] actions = state.Actions; foreach (FsmStateAction val in actions) { FieldInfo[] fields = ((object)val).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { object? value = fieldInfo.GetValue(val); FsmGameObject val2 = (FsmGameObject)((value is FsmGameObject) ? value : null); if (val2 != null && (Object)(object)val2.Value != (Object)null && ((Object)val2.Value).name == "Last Judge Charge Bomb") { chargePillarPrefab = val2.Value; Instance.Log.LogInfo((object)"Final Judge: cached native charge-pillar prefab from the Charge state."); return; } } } } private static void ConfigureChargePillarTrail(PlayMakerFSM controlFsm) { if (chargePillarTrailConfigured || (Object)(object)controlFsm == (Object)null) { return; } FsmState state = controlFsm.Fsm.GetState("Charge"); if (state == null) { return; } FsmStateAction[] actions = state.Actions; foreach (FsmStateAction val in actions) { if (!(((object)val).GetType().Name != "SpawnObjectFromGlobalPoolOverTime")) { object? obj = ((object)val).GetType().GetField("frequency", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val); FsmFloat val2 = (FsmFloat)((obj is FsmFloat) ? obj : null); if (val2 != null) { val2.Value = 0.095f; chargePillarTrailConfigured = true; Instance.Log.LogInfo((object)("Final Judge: changed charge pillar interval from 0.2 to " + 0.095f + " seconds for 3.8-unit spacing.")); return; } } } Instance.Log.LogWarning((object)"Final Judge [CHARGE]: over-time pillar spawn action was not found."); } private static void ScheduleThresholdRageIfNeeded(PlayMakerFSM controlFsm) { if (rageTransitionScheduled || (Object)(object)controlFsm == (Object)null || (Object)(object)judgeHealthManager == (Object)null) { return; } int threshold; if (!rageAt1100Triggered && judgeHealthManager.hp <= 1100) { rageAt1100Triggered = true; threshold = 1100; } else { if (!rageAt1100Triggered || rageAt400Triggered || judgeHealthManager.hp > 600) { return; } rageAt400Triggered = true; threshold = 600; } rageTransitionScheduled = true; ((MonoBehaviour)Instance).StartCoroutine(EnterNativeRageRoar(controlFsm, threshold)); } private static IEnumerator EnterNativeRageRoar(PlayMakerFSM controlFsm, int threshold) { yield return null; rageTransitionScheduled = false; if (!inJudgeFight || (Object)(object)controlFsm == (Object)null) { yield break; } CacheBaselineContactDamagers(((Component)controlFsm).transform); if (!finalPhaseContactMaintenanceRunning) { finalPhaseContactMaintenanceRunning = true; ((MonoBehaviour)Instance).StartCoroutine(MaintainFinalPhaseContactDamage(controlFsm)); } if (threshold == 600) { postFinalPhaseRoarChargePending = true; FsmState rageRoar2 = controlFsm.Fsm.GetState("Rage Roar 2"); if (rageRoar2 != null) { FsmTransition[] transitions = rageRoar2.Transitions; foreach (FsmTransition transition in transitions) { if (transition.EventName == "FINISHED") { transition.ToState = "Charge Antic 1"; transition.ToFsmState = controlFsm.Fsm.GetState("Charge Antic 1"); restoreRageRoarTransitionOnCharge = true; break; } } } } controlFsm.SetState("Rage Roar 1"); if (threshold == 600) { ((MonoBehaviour)Instance).StartCoroutine(ReplayNativeRoarAudio(controlFsm)); } Instance.Log.LogInfo((object)("Final Judge: triggered native rage roar and white circles at " + threshold + " HP.")); } private static IEnumerator ReplayNativeRoarAudio(PlayMakerFSM controlFsm) { yield return null; if (!inJudgeFight || (Object)(object)controlFsm == (Object)null) { yield break; } FsmState roarState = controlFsm.Fsm.GetState("Rage Roar 1"); int replayedActions = 0; if (roarState != null) { FsmStateAction[] actions = roarState.Actions; foreach (FsmStateAction action in actions) { string actionName = ((object)action).GetType().Name; if (action.Enabled && actionName.IndexOf("Audio", StringComparison.OrdinalIgnoreCase) >= 0 && actionName.IndexOf("Play", StringComparison.OrdinalIgnoreCase) >= 0) { action.OnEnter(); replayedActions++; } } } Instance.Log.LogInfo((object)("Final Judge: replayed " + replayedActions + " native roar audio actions for the 600 HP roar.")); } private static void RestoreRageRoarTransition(PlayMakerFSM controlFsm) { FsmState state = controlFsm.Fsm.GetState("Rage Roar 2"); if (state != null) { FsmTransition[] transitions = state.Transitions; foreach (FsmTransition val in transitions) { if (val.EventName == "FINISHED") { val.ToState = "Spin Antic"; val.ToFsmState = controlFsm.Fsm.GetState("Spin Antic"); break; } } } restoreRageRoarTransitionOnCharge = false; } private static void ConfigureNoStaggers(PlayMakerFSM controlFsm) { if (noStaggersConfigured || (Object)(object)controlFsm == (Object)null) { return; } FsmState state = controlFsm.Fsm.GetState("Idle"); if (state == null) { return; } int num = 0; FsmState[] states = controlFsm.Fsm.States; foreach (FsmState val in states) { FsmTransition[] transitions = val.Transitions; foreach (FsmTransition val2 in transitions) { if (val2.ToState.IndexOf("Stun", StringComparison.OrdinalIgnoreCase) >= 0 || val2.ToState.IndexOf("Stagger", StringComparison.OrdinalIgnoreCase) >= 0) { val2.ToState = "Idle"; val2.ToFsmState = state; num++; } } } noStaggersConfigured = true; ClearJudgeStaggerIndicators(((Component)controlFsm).transform); if (!staggerIndicatorSuppressionRunning) { staggerIndicatorSuppressionRunning = true; ((MonoBehaviour)Instance).StartCoroutine(MaintainStaggerIndicatorSuppression(((Component)controlFsm).transform)); } Instance.Log.LogInfo((object)("Final Judge: redirected " + num + " stagger transitions directly to Idle.")); } private static void ClearJudgeStaggerIndicators(Transform bossRoot) { FSMActivator[] componentsInChildren = ((Component)bossRoot).GetComponentsInChildren(true); foreach (FSMActivator val in componentsInChildren) { if (((object)val).GetType().GetMethod("DeactivateStaggered", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)?.Invoke(val, null) is IEnumerator enumerator) { ((MonoBehaviour)Instance).StartCoroutine(enumerator); } } DisableNamedStaggerVisuals(bossRoot); } private static IEnumerator MaintainStaggerIndicatorSuppression(Transform bossRoot) { while (inJudgeFight && (Object)(object)bossRoot != (Object)null) { DisableNamedStaggerVisuals(bossRoot); yield return null; } staggerIndicatorSuppressionRunning = false; } private static void DisableNamedStaggerVisuals(Transform bossRoot) { PlayMakerFSM[] componentsInChildren = ((Component)bossRoot).GetComponentsInChildren(true); foreach (PlayMakerFSM val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)bossControlFsm) && (IsStaggerIndicatorName(val.FsmName) || IsStaggerIndicatorName(((Object)((Component)val).gameObject).name))) { ((Behaviour)val).enabled = false; if ((Object)(object)((Component)val).transform != (Object)(object)bossRoot) { ((Component)val).gameObject.SetActive(false); } } } Renderer[] componentsInChildren2 = ((Component)bossRoot).GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren2) { if ((Object)(object)val2 != (Object)null && (Object)(object)((Component)val2).transform != (Object)(object)bossRoot && IsStaggerIndicatorName(GetHierarchyPath(((Component)val2).transform))) { val2.enabled = false; ((Component)val2).gameObject.SetActive(false); } } } private static bool IsStaggerIndicatorName(string value) { return !string.IsNullOrEmpty(value) && (value.IndexOf("Stun", StringComparison.OrdinalIgnoreCase) >= 0 || value.IndexOf("Stagger", StringComparison.OrdinalIgnoreCase) >= 0 || value.IndexOf("Daze", StringComparison.OrdinalIgnoreCase) >= 0 || value.IndexOf("Dizzy", StringComparison.OrdinalIgnoreCase) >= 0); } private static IEnumerator EscapeStaggerState(PlayMakerFSM controlFsm) { yield return null; staggerEscapeScheduled = false; if (inJudgeFight && (Object)(object)controlFsm != (Object)null) { RestoreJudgeGravity(controlFsm); controlFsm.SetState("Idle"); yield return null; RestoreJudgeGravity(controlFsm); } } private static void RestoreJudgeGravity(PlayMakerFSM controlFsm) { Rigidbody2D val = (((Object)(object)controlFsm != (Object)null) ? ((Component)controlFsm).GetComponent() : null); if (!((Object)(object)val == (Object)null)) { val.simulated = true; val.gravityScale = Mathf.Max(judgeGravityScale, 0.01f); Instance.Log.LogInfo((object)("Final Judge: restored gravity after stagger (scale " + val.gravityScale + ").")); } } private static void RemoveNativeStompAntic(PlayMakerFSM controlFsm) { if (nativeStompAnticRemoved || (Object)(object)controlFsm == (Object)null) { return; } FsmState state = controlFsm.Fsm.GetState("Stomp Flame Antic"); if (state == null) { return; } FsmStateAction[] actions = state.Actions; foreach (FsmStateAction val in actions) { if (((object)val).GetType().Name != "Wait") { val.Enabled = false; } } nativeStompAnticRemoved = true; Instance.Log.LogInfo((object)"Final Judge: removed the original slam particle antic while retaining its 0.5-second timing."); } [HarmonyPrefix] [HarmonyPatch(typeof(SetHP), "OnEnter")] private static void ReplaceJudgeStartingHealth(SetHP __instance) { if (inJudgeFight && __instance?.hp != null) { GameObject safe = FSMUtility.GetSafe(__instance.target, (FsmStateAction)(object)__instance); Fsm fsm = ((FsmStateAction)__instance).Fsm; PlayMakerFSM val = ((fsm != null) ? fsm.FsmComponent : null); if (((Object)(object)safe != (Object)null && IsJudgeHierarchy(safe.transform)) || ((Object)(object)val != (Object)null && IsJudgeHierarchy(((Component)val).transform))) { int value = __instance.hp.Value; __instance.hp.Value = 1500; Instance.Log.LogInfo((object)("Final Judge: changed Last Judge SetHP from " + value + " to " + 1500 + ".")); } } } [HarmonyPrefix] [HarmonyPatch(typeof(DisplayBossTitle), "OnEnter")] private static void ArmJudgeBossTitleReplacement(DisplayBossTitle __instance) { object obj; if (__instance == null) { obj = null; } else { Fsm fsm = ((FsmStateAction)__instance).Fsm; obj = ((fsm != null) ? fsm.FsmComponent : null); } PlayMakerFSM val = (PlayMakerFSM)obj; if (inJudgeFight && !((Object)(object)val == (Object)null) && IsJudgeHierarchy(((Component)val).transform)) { replaceBossTitleLocalization = true; Instance.Log.LogInfo((object)("Final Judge [TITLE]: armed replacement for boss-title event '" + __instance.bossTitle.Value + "'.")); ((MonoBehaviour)Instance).StartCoroutine(EndJudgeBossTitleReplacementWindow()); } } [HarmonyPostfix] [HarmonyPatch(typeof(Language), "Get", new Type[] { typeof(string), typeof(string) })] private static void ReplaceJudgeBossTitleLocalization(string key, string sheetTitle, ref string __result) { if (replaceBossTitleLocalization && !string.IsNullOrEmpty(__result)) { string text = __result.Trim(); bool flag = !string.IsNullOrEmpty(key) && key.IndexOf("LAST_JUDGE", StringComparison.OrdinalIgnoreCase) >= 0; bool flag2 = text.IndexOf("Last Judge", StringComparison.OrdinalIgnoreCase) >= 0 || (flag && (key.IndexOf("MAIN", StringComparison.OrdinalIgnoreCase) >= 0 || key.IndexOf("NAME", StringComparison.OrdinalIgnoreCase) >= 0)); Instance.Log.LogInfo((object)("Final Judge [TITLE]: localization key='" + key + "', sheet='" + sheetTitle + "', value='" + __result + "'.")); if (flag2) { __result = "Final Judge"; } else if (flag || string.Equals(text, "The", StringComparison.OrdinalIgnoreCase)) { __result = "Harbinger of Sin"; } } } private static IEnumerator EndJudgeBossTitleReplacementWindow() { yield return (object)new WaitForSeconds(3f); replaceBossTitleLocalization = false; Instance.Log.LogInfo((object)"Final Judge: applied localized two-line boss title replacement window."); } private static IEnumerator FindAndSetJudgeHealth() { for (int frame = 0; frame < 120; frame++) { HealthManager[] array = Resources.FindObjectsOfTypeAll(); foreach (HealthManager healthManager in array) { int num2; if (!((Object)(object)healthManager == (Object)null)) { Scene scene = ((Component)healthManager).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { scene = ((Component)healthManager).gameObject.scene; if (!(((Scene)(ref scene)).name != "Coral_Judge_Arena")) { num2 = ((!IsJudgeHierarchy(((Component)healthManager).transform)) ? 1 : 0); goto IL_00be; } } } num2 = 1; goto IL_00be; IL_00be: if (num2 == 0) { int originalHealth = healthManager.hp; healthManager.hp = 1500; Traverse.Create((object)healthManager).Field("initHp").SetValue((object)1500); judgeHealthManager = healthManager; healthSetupRunning = false; healthConfigured = true; Instance.Log.LogInfo((object)("Final Judge: changed live Last Judge health from " + originalHealth + " to " + 1500 + " at " + GetHierarchyPath(((Component)healthManager).transform) + ".")); yield break; } } yield return null; } healthSetupRunning = false; Instance.Log.LogWarning((object)"Final Judge [HEALTH]: no Last Judge HealthManager appeared during startup."); } private static bool IsJudgeHierarchy(Transform transform) { while ((Object)(object)transform != (Object)null) { if (string.Equals(((Object)transform).name, "Last Judge", StringComparison.Ordinal) || ((Object)transform).name.IndexOf("Last Judge", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } transform = transform.parent; } return false; } private static string GetHierarchyPath(Transform transform) { string text = (((Object)(object)transform == (Object)null) ? "" : ((Object)transform).name); while ((Object)(object)((transform != null) ? transform.parent : null) != (Object)null) { transform = transform.parent; text = ((Object)transform).name + "/" + text; } return text; } }