Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Final Judge v1.0.1
FinalJudge.dll
Decompiled an hour agousing 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<int> doubledLavaRockDamagers = new HashSet<int>(); private static readonly List<DamageHero> baselineContactDamagers = new List<DamageHero>(); private static readonly FieldInfo DamageDealtField = typeof(DamageHero).GetField("damageDealt", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static float stompWaveSpeed = 12f; private static AsyncOperationHandle<IAssetBundleResource> dockBundleHandle; private static bool dockBundleHandleValid; private static readonly List<AsyncOperationHandle<IAssetBundleResource>> lavaRockDependencyHandles = new List<AsyncOperationHandle<IAssetBundleResource>>(); 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<IAssetBundleResource>(dockBundleHandle); dockBundleHandleValid = false; } foreach (AsyncOperationHandle<IAssetBundleResource> lavaRockDependencyHandle in lavaRockDependencyHandles) { Addressables.Release<IAssetBundleResource>(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<Rigidbody2D>(); 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<IResourceLocation> dependencyLocations = new List<IResourceLocation>(); HashSet<string> dependencyIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); IList<IResourceLocation> locations = default(IList<IResourceLocation>); 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<IAssetBundleResource> dependencyHandle = Addressables.LoadAssetAsync<IAssetBundleResource>(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<IAssetBundleResource>(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<GameObject>("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<GameObject>("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<GameObject>(); 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<Rigidbody2D>() : 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<Renderer>(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<Transform>(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<GameObject>(); 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<float> leftPositions = new List<float>(); List<float> rightPositions = new List<float>(); 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<Transform>(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<Rigidbody2D>(); 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<GameObject>(template); particleWaves[index].transform.position = impactPosition + Vector3.right * (2f * (float)directionSign); particleWaves[index].SetActive(true); ParticleSystem[] componentsInChildren = particleWaves[index].GetComponentsInChildren<ParticleSystem>(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<GameObject>(); 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<Rigidbody2D>(); 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<Rigidbody2D>(); 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<Rigidbody2D>(); 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<tk2dSprite>(); tk2dSpriteAnimator component2 = lavaRock.GetComponent<tk2dSpriteAnimator>(); 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<Renderer>(); 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<DamageHero>(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<Rigidbody2D>(); if ((Object)(object)component != (Object)null) { component.linearVelocity = velocity; } } } private static void RepairBombRockParticleMaterials(GameObject bombRock) { ParticleSystemRenderer[] componentsInChildren = bombRock.GetComponentsInChildren<ParticleSystemRenderer>(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<DamageEnemies>(true); foreach (DamageEnemies val in componentsInChildren) { ((Behaviour)val).enabled = false; } FireballProjectile[] componentsInChildren2 = projectile.GetComponentsInChildren<FireballProjectile>(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<Rigidbody2D>(); 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<Collider2D>(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<DamageHero>(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<Transform>(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<Collider2D>(true); foreach (Collider2D val in componentsInChildren) { ((Behaviour)val).enabled = true; } DamageHero[] componentsInChildren2 = ((Component)chargeCollider).GetComponentsInChildren<DamageHero>(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<DamageHero>(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<Collider2D>(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<FSMActivator>(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<PlayMakerFSM>(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<Renderer>(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<Rigidbody2D>() : 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<HealthManager>(); 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) ? "<null>" : ((Object)transform).name); while ((Object)(object)((transform != null) ? transform.parent : null) != (Object)null) { transform = transform.parent; text = ((Object)transform).name + "/" + text; } return text; } }