Decompiled source of Pantheon Of Pharloom v1.3.2
PantheonOfPharloom.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using GlobalEnums; using GlobalSettings; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using InControl; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using MonoMod.RuntimeDetour; using PantheonOfPharloom.Actions; using TMProOld; using TeamCherry.Localization; using TeamCherry.NestedFadeGroup; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PantheonOfPharloom")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+b3d1a911a7e4c2c15f63eddf66dc13c6e87c03af")] [assembly: AssemblyProduct("PantheonOfPharloom")] [assembly: AssemblyTitle("PantheonOfPharloom")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } internal static class IsExternalInit { } } namespace PantheonOfPharloom { internal class UserState { public string Scene { get; init; } public string Marker { get; init; } public HeroItemsState Items { get; init; } } public static class FadeSceneInHook { private static Hook _hook; private static bool _enabled = true; public static void Init() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown _hook = new Hook((MethodBase)typeof(GameManager).GetMethod("FadeSceneIn", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Action<Action<GameManager>, GameManager>(FadeSceneInDetour)); } public static void SetEnabled(bool enabled) { _enabled = enabled; } private static void FadeSceneInDetour(Action<GameManager> orig, GameManager self) { if (!_enabled) { orig(self); PopLogger.Log("[FadeSceneInHook] Forcing fade-in."); } else { PopLogger.Log("[FadeSceneInHook] Skipping fade-in."); } } public static void ForceCall() { SetEnabled(enabled: false); GameManager.instance.FadeSceneIn(); SetEnabled(enabled: true); } public static void Unload() { Hook hook = _hook; if (hook != null) { hook.Dispose(); } _hook = null; } } public class BossRushController : MonoBehaviour { private static readonly bool DO_TELEPORT = true; private BossRushSequence _sequence; private bool _isActive; public static BossRushController Instance; private readonly bool _debug; private UserState _userState; public static Dictionary<string, bool> _playerDataBools = new Dictionary<string, bool>(); private static HashSet<string> _excludeList = new HashSet<string> { "hasDash", "hasBrolly", "hasSuperJump", "HasSeenEvaHeal", "hasHarpoonDash", "hasChargeSlash", "hasNeedolin", "hasWalljump", "hasDoubleJump", "hasNeedolinMemoryPowerup", "HasBoundCrestUpgrader", "UnlockedExtraBlueSlot", "UnlockedExtraYellowSlot", "UnlockedFastTravel", "UnlockedFastTravelTeleport", "hasQuill", "mapAllRooms", "HasAllMaps", "HasWhiteFlower", "completedTutorial", "SeenBindPrompt", "seenDreamNailPrompt", "SeenToolGetPrompt", "SeenToolWeaponGetPrompt", "SeenToolEquipPrompt", "SeenToolUsePrompt", "LightningToolToggle" }; public bool AllowUI { get { if (_isActive) { return _debug; } return true; } } public bool IsActive => _isActive; public Encounter CurrentEncounter => _sequence.Current(); private void Awake() { Instance = this; } private static void PatchPlayerData() { _playerDataBools = new Dictionary<string, bool>(); string[] array = (from x in typeof(PlayerData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) select x.Name into x where !_excludeList.Contains(x) select x).ToArray(); CacheUtils.CachePlayerDataBools(array); string[] array2 = array; foreach (string text in array2) { if (!(text == "visitedGreymoor") && !(text == "visitedCoral")) { _playerDataBools[text] = PlayerData.instance.GetBool(text); PlayerData.instance.SetBool(text, text.StartsWith("encountered", StringComparison.InvariantCulture)); } } } private static void RestorePlayerData() { if (PlayerData.instance != null) { CacheUtils.Restore(); } } private void RestoreSpawnScene() { PlayerData.instance.respawnScene = _userState.Scene; PlayerData.instance.respawnMarkerName = _userState.Marker; PlayerData.instance.tempRespawnScene = ""; PlayerData.instance.tempRespawnMarker = ""; PopLogger.Log("restoring scene and stuff: " + PlayerData.instance.respawnScene + ", " + PlayerData.instance.respawnMarkerName + ", " + PlayerData.instance.tempRespawnScene + ", " + PlayerData.instance.tempRespawnMarker); } private void AfterDeathOrCompletion() { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) if (_sequence == null) { PopLogger.LogError("Sequence was null here should never happen"); } _sequence.Cleanup?.Invoke(); Reset(); PlayerData.instance.respawnScene = _userState.Scene; PlayerData.instance.respawnMarkerName = _userState.Marker; PopLogger.Log("Setting after death or completion: " + PlayerData.instance.respawnScene + ", " + PlayerData.instance.respawnMarkerName); HeroItemsState items = _userState.Items; ((HeroItemsState)(ref items)).Apply(HeroController.instance); } private void OnHornetDeath() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if (_userState != null) { RestoreSpawnScene(); } CurrentEncounter.OnExit?.Invoke(); GameManager.instance.OnFinishedEnteringScene -= new EnterSceneEvent(OnNewEncounterLoad); GameManager.instance.OnFinishedEnteringScene += new EnterSceneEvent(Once); void Once() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown PopLogger.Log("called once..."); AfterDeathOrCompletion(); if (!string.IsNullOrEmpty(PlayerData.instance.HeroCorpseScene)) { HeroController.instance.CocoonBroken(); EventRegister.SendEvent(EventRegisterEvents.BreakHeroCorpse, (GameObject)null); } GameManager.instance.OnFinishedEnteringScene -= new EnterSceneEvent(Once); } } public static void SetupNewSequence(List<Encounter> encounters, bool transition, bool recordState = true, Action beforeEncounter = null, Action cleanup = null) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (encounters.Count == 0) { PopLogger.LogError("Cannot start boss rush with 0 encounters."); return; } if (Instance._isActive) { Instance.Reset(); PopLogger.LogError("Active sequence already exists, thsi should not happen..."); } if (recordState) { PopLogger.Log("recording: " + PlayerData.instance.respawnScene + ", " + PlayerData.instance.respawnMarkerName); Instance._userState = new UserState { Scene = PlayerData.instance.respawnScene, Marker = PlayerData.instance.respawnMarkerName, Items = HeroItemsState.Record(HeroController.instance) }; } PopLogger.Log($"Starting new boss sequence of length {encounters.Count}"); HeroController.instance.OnDeath += Instance.OnHornetDeath; Instance._sequence = new BossRushSequence(encounters, beforeEncounter, null, cleanup); Instance._isActive = true; PatchPlayerData(); FadeSceneInHook.Init(); FadeSceneInHook.SetEnabled(enabled: true); if (transition) { Instance.NextBoss(); } } private void BeginBossRushCompletion(float delay) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown PopLogger.Log("end reached"); PopLogger.Log("Completed boss rush!"); FadeSceneInHook.SetEnabled(enabled: false); if (_userState != null) { RestoreSpawnScene(); } string sceneName = default(string); string entryGateName = default(string); GameManager.instance.GetRespawnInfo(ref sceneName, ref entryGateName); StartTransitionWithCoroutine(new SceneLoadInfo { SceneName = sceneName, EntryGateName = entryGateName, PreventCameraFadeOut = true, AlwaysUnloadUnusedAssets = true }, delay, SceneAfterFinish(), delegate { GameManager.instance.RespawningHero = true; }); IEnumerator SceneAfterFinish() { _sequence.Previous().AfterExit?.Invoke(); AfterDeathOrCompletion(); yield return null; PopLogger.Log("created fireworks"); } } private void NextBoss() { if (!_sequence.TryGetNext(out var next)) { BeginBossRushCompletion(_sequence.Previous().TransitionDelay); return; } PopLogger.Log("calling NextBoss."); if (!_sequence.IsFirstInSequence) { _sequence.Previous().OnExit?.Invoke(); _sequence.AfterEncounter?.Invoke(); } _sequence.BeforeEncounter?.Invoke(); next.BeforeEnter?.Invoke(); PopLogger.Log($"Starting next boss scene: {next.SceneInfo}"); StartTransitionWithCallback(next.SceneInfo, _sequence.IsFirstInSequence ? 0f : _sequence.Previous().TransitionDelay, OnNewEncounterLoad, doTransition: true, delegate { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) string sceneName = CurrentEncounter.SceneInfo.SceneName; Scene activeScene = SceneManager.GetActiveScene(); return sceneName == ((Scene)(ref activeScene)).name; }); } private void TransitionFromBench() { if (!_sequence.TryGetNext(out var next)) { BeginBossRushCompletion(_sequence.Previous().TransitionDelay); return; } if (!_sequence.IsFirstInSequence) { _sequence.Previous().OnExit?.Invoke(); } next.BeforeEnter?.Invoke(); StartTransitionWithCallback(next.SceneInfo, 0f, OnNewEncounterLoad, doTransition: false); } private void PatchBadStates() { GameManager.instance.SetTimeScale(1f); FSMUtility.LocateMyFSM(((Component)((Component)GameCameras.instance.hudCamera).transform.Find("In-game/Anchor TL/Hud Canvas Offset/Hud Canvas")).gameObject, "Slide Out").SendEvent("IN"); EventRegister.SendEvent("SHOW HP", (GameObject)null); } private void TeleportPlayerToCustomSpawn(Vector3 position) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) Extensions.SetPosition2D(HeroController.instance.transform, Vector2.op_Implicit(position)); Vector3 position2 = HeroController.instance.transform.position; GameManager.instance.cameraCtrl.SnapTo(position2.x, position2.y); } private void OnNewEncounterLoad() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) PopLogger.Log("Encounter loaded..."); if (!_sequence.IsFirstInSequence) { _sequence.Previous().AfterExit?.Invoke(); } CurrentEncounter.AfterEnter?.Invoke(); PatchBadStates(); IEncounterCompletedNotification encounterCompletedNotification = EncounterCompletionRegistry.AddEncounterCompletion(Object.Instantiate<GameObject>(new GameObject("BossRushController")), CurrentEncounter); if (encounterCompletedNotification is BenchTransitionNotification benchTransitionNotification) { if (_sequence.TryPeekNext(out var next)) { benchTransitionNotification.nextScene = next.SceneInfo.SceneName; benchTransitionNotification.nextGate = next.SceneInfo.EntryGateName; encounterCompletedNotification.OnEncounterCompleted += delegate { PopLogger.Log("Transitioning from bench...."); TransitionFromBench(); }; } ((MonoBehaviour)GameManager.instance).StartCoroutine(WaitThenFade(0.1f)); return; } HealthManager[] array = BossEncounterCompleteNotification.GetHealthManagers(CurrentEncounter.BossPathFromRoot).ToArray(); if (DO_TELEPORT && !CurrentEncounter.SkipTeleport) { if (CurrentEncounter.CustomSpawnPoint.HasValue) { TeleportPlayerToCustomSpawn(Vector2.op_Implicit(CurrentEncounter.CustomSpawnPoint.Value)); } else if (array.Length != 0) { Vector3 position = HeroController.instance.FindGroundPoint(Vector2.op_Implicit(((Component)array[0]).transform.position), true); TeleportPlayerToCustomSpawn(position); } else { PopLogger.LogError("Could not find boss to teleport to, set custom spawn point"); } } else { PopLogger.Log("skipping teleport"); } HealthManager[] array2 = array; foreach (HealthManager val in array2) { CurrentEncounter.ScaleHealth?.Invoke(val); PopLogger.LogWarning($"Scaled HP for {val}"); } if (TransitionPoint.TransitionPoints != null) { foreach (TransitionPoint transitionPoint in TransitionPoint.TransitionPoints) { if (transitionPoint != null && !((Object)transitionPoint).name.Contains("door")) { transitionPoint.activated = false; if (transitionPoint.collider != null) { transitionPoint.collider.isTrigger = false; } PopLogger.Log($"patched transition: {transitionPoint}"); } } } if (TransitionPoint.TransitionPoints == null) { new List<TransitionPoint>(); } encounterCompletedNotification.OnEncounterCompleted += delegate { PopLogger.Log("calling current encounte exit...."); NextBoss(); }; ((MonoBehaviour)GameManager.instance).StartCoroutine(WaitThenFade(0.3f)); static IEnumerator WaitThenFade(float delay) { yield return (object)new WaitForSeconds(delay); FadeSceneInHook.ForceCall(); } } private void StartTransitionWithCallback(SceneLoadInfo info, float delay = 0f, Action callback = null, bool doTransition = true, Func<bool> predicate = null) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown if (doTransition) { ((MonoBehaviour)this).StartCoroutine(DoTransition(info, delay)); } GameManager.instance.OnFinishedEnteringScene += new EnterSceneEvent(Wrapper); void Wrapper() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if (predicate != null && !predicate()) { PopLogger.Log("did not satisfy condition...."); } else { GameManager.instance.OnFinishedEnteringScene -= new EnterSceneEvent(Wrapper); callback?.Invoke(); HeroController.instance.acceptingInput = true; } } } private void StartTransitionWithCoroutine(SceneLoadInfo info, float delay = 0f, IEnumerator callback = null, Action beforeTransition = null) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown ((MonoBehaviour)this).StartCoroutine(DoTransition(info, delay, beforeTransition)); GameManager.instance.OnFinishedEnteringScene += new EnterSceneEvent(Wrapper); void Wrapper() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown GameManager.instance.OnFinishedEnteringScene -= new EnterSceneEvent(Wrapper); if (callback != null) { ((MonoBehaviour)GameManager.instance).StartCoroutine(callback); } HeroController.instance.acceptingInput = true; } } private IEnumerator DoTransition(SceneLoadInfo info, float delay = 0f, Action beforeTransition = null) { yield return (object)new WaitForSeconds(delay); ScreenFaderUtils.Fade(ScreenFaderUtils.GetColour(), Color.black, 1.5f); yield return (object)new WaitForSeconds(1.5f); yield return (object)new WaitUntil((Func<bool>)(() => !GameManager.instance.IsInSceneTransition && !GameManager.instance.IsLoadingSceneTransition)); HeroController.instance.acceptingInput = false; beforeTransition?.Invoke(); GameManager.instance.BeginSceneTransition(info); } private void OnDestroy() { Instance = null; } public void Reset(bool patch = true) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown _sequence = null; _isActive = false; FadeSceneInHook.Unload(); if (patch) { RestorePlayerData(); HeroController.instance.OnDeath -= OnHornetDeath; GameManager.instance.OnFinishedEnteringScene -= new EnterSceneEvent(OnNewEncounterLoad); } } } public class BossRushSequence { private List<Encounter> _encounters = new List<Encounter>(encounters); public readonly Action BeforeEncounter; public readonly Action AfterEncounter; public readonly Action Cleanup; private int _encounterIndex; public bool IsFirstInSequence => _encounterIndex == 0; public BossRushSequence(IEnumerable<Encounter> encounters, Action beforeEncounter = null, Action afterEncounter = null, Action cleanup = null) { BeforeEncounter = beforeEncounter; AfterEncounter = afterEncounter; Cleanup = cleanup; _encounterIndex = -1; base..ctor(); } public bool TryGetNext([MaybeNullWhen(false)] out Encounter next) { _encounterIndex++; if (_encounterIndex == _encounters.Count) { next = default(Encounter); return false; } next = _encounters[_encounterIndex]; return true; } public bool TryPeekNext([MaybeNullWhen(false)] out Encounter next) { if (_encounterIndex + 1 >= _encounters.Count) { next = default(Encounter); return false; } next = _encounters[_encounterIndex + 1]; return true; } public Encounter Previous() { return _encounters[_encounterIndex - 1]; } public Encounter Current() { return _encounters[_encounterIndex]; } } internal class BenchTransitionNotification : MonoBehaviour, IEncounterCompletedNotification { public string nextScene; public string nextGate; public event Action OnEncounterCompleted; private void Start() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown TransitionPoint component = GameObject.Find("left1").GetComponent<TransitionPoint>(); component.targetScene = nextScene; component.entryPoint = nextGate; component.OnBeforeTransition += (BeforeTransitionEvent)delegate { this.OnEncounterCompleted?.Invoke(); }; } } public class BossDeathNotification : BossEncounterCompleteNotification { private int _bossesLeft; private void Start() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown HealthManager[] array = BossEncounterCompleteNotification.GetHealthManagers(bossPaths).ToArray(); _bossesLeft = array.Length; HealthManager[] array2 = array; foreach (HealthManager val in array2) { val.OnDeath += (DeathEvent)delegate { _bossesLeft--; PopLogger.Log("Boss death event received...."); if (_bossesLeft == 0) { BossDead(); } }; PopLogger.Log($"Added received death hook for {val}"); } } } public class BossEncounterCompleteNotification : MonoBehaviour, IEncounterCompletedNotification { public string[] bossPaths; public event Action OnEncounterCompleted; private static IEnumerable<HealthManager> FindBossesAutomatic() { IGrouping<int, HealthManager> grouping = (from x in Object.FindObjectsByType<HealthManager>((FindObjectsSortMode)0) orderby x.hp descending group x by x.hp).FirstOrDefault(); if (grouping == null) { PopLogger.LogError("Could not automatically find boss, set the boss path."); return Array.Empty<HealthManager>(); } PopLogger.Log("Found boss automatically: " + string.Join(",", grouping.Select((HealthManager x) => ((Object)x).name))); return grouping; } public static IEnumerable<HealthManager> GetHealthManagers(string[] bossPaths) { if (bossPaths.Length == 0) { PopLogger.Log("Boss paths were empty, trying to find automatically..."); return FindBossesAutomatic(); } return bossPaths.GetComponenFromPaths<HealthManager>(); } protected void BossDead() { PopLogger.Log("called BossDead()"); this.OnEncounterCompleted?.Invoke(); } } public class BossZeroHPNotification : BossEncounterCompleteNotification { private HealthManager[] _bosses; private bool _triggerd; private void Start() { _bosses = BossEncounterCompleteNotification.GetHealthManagers(bossPaths).ToArray(); } private void Update() { if (_triggerd) { return; } bool flag = true; HealthManager[] bosses = _bosses; for (int i = 0; i < bosses.Length; i++) { if (bosses[i].hp > 0) { flag = false; break; } } if (flag) { _triggerd = true; PopLogger.Log("Bosses reached zero hp...."); BossDead(); } } } internal class BelleaterDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObjectUtil.FindGameObjectFromScene("Boss Scene/Centipede Control"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Control") : null); if (val == null) { PopLogger.LogError("Could not find cetipede control"); return; } val.GetState("Bell Best Appear").InsertMethod(0, base.BossDead); PopLogger.Log("Patched bellwater"); } } internal class CloverDancersDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObjectUtil.FindGameObjectFromScene("Boss Scene/Dancer Control/Dancer A/Corpse Green Prince(Clone)"); PlayMakerFSM obj2 = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Death") : null); if (obj2 == null) { PopLogger.Log("could not find corpse control for clover dancers"); } FsmState state = ((RunFSMAction)obj2.GetState("Heart Death").GetAction<RunFSM>()).runFsm.GetState("Return"); state.RemoveAction<BeginSceneTransition>(); state.AddMethod(base.BossDead); } } internal class CogworkDancersDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObjectUtil.FindGameObjectFromScene("Dancer Control"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Control") : null); if (val == null) { PopLogger.LogError("Could not find last judge corpse fsm"); return; } val.GetState("End").InsertMethod(0, base.BossDead); PopLogger.Log("patched cogwork dancers"); } } internal class ConchflyDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObject.Find("Boss Scene"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Control") : null); if (val == null) { PopLogger.Log("Could not find control for conchfly boss scene"); return; } val.GetState("Battle End S").InsertMethod(0, base.BossDead); val.GetState("Battle End").InsertMethod(0, base.BossDead); PopLogger.Log("Patch conchflies"); } } internal class FatherDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObject.Find("Boss Scene/Wisp Pyre Effigy"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Summon Control") : null); if (val == null) { PopLogger.Log("could not find father"); return; } val.GetState("End").InsertMethod(0, base.BossDead); PopLogger.Log("patched father"); } } internal class FirstSinnerDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObjectUtil.FindGameObjectFromScene("Boss Scene/Shrine First Weaver NPC"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Inspection") : null); if (val == null) { PopLogger.LogError("could not finnd first sinner npc"); return; } FsmState state = val.GetState("To First Sinner Memory"); state.Actions = Array.Empty<FsmStateAction>(); state.InsertMethod(0, base.BossDead); PopLogger.Log("patched weaver fsm"); } } internal class GrandmotherDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObject.Find("Boss Scene/Death Sequence"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Control") : null); GameObject obj2 = GameObject.Find("Boss Scene/Death Sequence Cursed"); PlayMakerFSM val2 = ((obj2 != null) ? FSMUtility.LocateMyFSM(obj2, "Contorl") : null); if (val == null || val2 == null) { PopLogger.LogError("could not find a eath sequence control"); return; } val.CreateState("Custom Boss Dead").InsertMethod(0, base.BossDead); FsmState state = val.GetState("Idle"); state.Transitions = Array.Empty<FsmTransition>(); state.AddTransition("DEATH START", "Custom Boss Dead"); PopLogger.Log("patched grandmother silk 3"); } } internal class LastJudgeDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObjectUtil.FindGameObjectFromScene("Boss Scene/Last Judge/Corpse Last Judge(Clone)"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Control") : null); if (val == null) { PopLogger.LogError("Could not find last judge corpse fsm"); return; } val.GetState("Break 4").InsertMethod(0, base.BossDead); PopLogger.Log("patched last judge"); } } internal class LostLaceDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObject.Find("Boss Control/Superjump Sequence"); PlayMakerFSM fsm = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Control") : null); fsm.CreateState("Custom Boss Dead").InsertMethod(0, base.BossDead); FsmState state = fsm.GetState("Crash"); state.Transitions = Array.Empty<FsmTransition>(); state.AddTransition("FINISHED", "Custom Boss Dead"); PopLogger.Log("patched lost lace"); } } internal class PhantomDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObject.Find("Boss Scene/Phantom"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Control") : null); if (val == null) { PopLogger.LogError("could not finnd phantom"); return; } FsmState state = val.GetState("Fade To Black"); state.GetAction<Wait>().time = FsmFloat.op_Implicit(0f); val.CreateState("Custom Boss Dead").InsertMethod(0, base.BossDead); state.Transitions = Array.Empty<FsmTransition>(); state.AddTransition("FINISHED", "Custom Boss Dead"); PopLogger.Log("patched fsm"); } } internal class ShakraDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObjectUtil.FindGameObjectFromScene("Mapper Spar NPC"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Dialogue") : null); if (val == null) { PopLogger.LogError("couldnt find dialouge fsm for shakra"); return; } FsmState state = val.GetState("Away"); state.InsertMethod(state.Actions.Length - 1, base.BossDead); PopLogger.Log("Patched Unravelled"); } } internal class SingleConchflyDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObjectUtil.FindGameObjectFromScene("Battle Scene/Wave 1/Coral Conch Driller Giant Solo/Corpse Coral Conch Driller Giant Solo(Clone)"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Death") : null); if (val == null) { PopLogger.LogError("Could not find conch corpse fsm"); return; } val.GetState("Stagger").InsertMethod(0, base.BossDead); PopLogger.Log("patched single conchfly"); } } internal class UnravelledDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObject.Find("Boss Scene/Conductor Boss"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Control") : null); if (val == null) { PopLogger.LogError("Could not find control for Unravelled"); return; } FsmState state = val.GetState("Death Blow"); state.InsertMethod(state.Actions.Length - 1, base.BossDead); PopLogger.Log("Patched Unravelled"); } } internal class WidowDeadNotification : BossEncounterCompleteNotification { private void Start() { GameObject obj = GameObject.Find("Boss Scene/Spinner Boss"); PlayMakerFSM val = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Control") : null); if (val == null) { PopLogger.LogError("could not finnd window"); return; } FsmState state = val.GetState("Fade To Black"); state.GetAction<Wait>(6).time = FsmFloat.op_Implicit(0f); val.GetState("Fade").GetAction<Wait>().time = FsmFloat.op_Implicit(0f); val.CreateState("Custom Boss Dead").InsertMethod(0, base.BossDead); state.Transitions = Array.Empty<FsmTransition>(); state.AddTransition("FINISHED", "Custom Boss Dead"); PopLogger.Log("patched fssm"); } } public static class EncounterCompletionRegistry { private static readonly Dictionary<string, Type> _customNotifications = new Dictionary<string, Type> { { "Widow", typeof(WidowDeadNotification) }, { "Phantom", typeof(PhantomDeadNotification) }, { "Last Judge", typeof(LastJudgeDeadNotification) }, { "Cogwork Dancers", typeof(CogworkDancersDeadNotification) }, { "Single Conchfly", typeof(SingleConchflyDeadNotification) }, { "Double Conchfly", typeof(ConchflyDeadNotification) }, { "Unravelled", typeof(UnravelledDeadNotification) }, { "First Sinner", typeof(FirstSinnerDeadNotification) }, { "Grandmother", typeof(GrandmotherDeadNotification) }, { "Father of Flame", typeof(FatherDeadNotification) }, { "Bell Eater", typeof(BelleaterDeadNotification) }, { "Clover Dancer", typeof(CloverDancersDeadNotification) }, { "Lost Lace", typeof(LostLaceDeadNotification) }, { "Bench", typeof(BenchTransitionNotification) }, { "Shakra", typeof(ShakraDeadNotification) } }; public static IEncounterCompletedNotification AddEncounterCompletion(GameObject root, Encounter encounter) { IEncounterCompletedNotification encounterCompletedNotification = null; if (_customNotifications.TryGetValue(encounter.Name, out var value)) { return root.AddComponent(value) as IEncounterCompletedNotification; } if (encounter.SpecialDeath) { BossZeroHPNotification bossZeroHPNotification = root.AddComponent<BossZeroHPNotification>(); bossZeroHPNotification.bossPaths = encounter.BossPathFromRoot; return bossZeroHPNotification; } BossDeathNotification bossDeathNotification = root.AddComponent<BossDeathNotification>(); bossDeathNotification.bossPaths = encounter.BossPathFromRoot; return bossDeathNotification; } } public interface IEncounterCompletedNotification { event Action OnEncounterCompleted; } public static class BossLists { private static readonly string BossListsDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "boss_list"); private static readonly string[] _defaultPharloomBosses = new string[13] { "Moss Mother", "Skull Tyrant", "Bell Beast", "Savage Beastfly", "Fourth Chorus", "Fire Lace", "Bench", "Double Conchfly", "Moorwing", "Sister Splinter", "Widow", "Phantom", "Last Judge" }; private static readonly string[] _defaultCitadelBosses = new string[20] { "Garmond and Zaza", "Cogwork Dancers", "Trobbio", "Chef Lugoli", "Broodmother", "Groal", "Bench", "Moss Mother 2", "Single Conchfly", "Zango", "Voltvyrm", "Savage Beastfly 2", "Shakra", "Bench", "Father of Flame", "Signis & Gron", "Second Sentinel", "Unravelled", "Flower Lace", "First Sinner" }; private static readonly string[] _defaultAbyssBosses = new string[17] { "Bell Eater", "Crawfather", "Gurr", "Lost Garmond", "Pinstress", "Watcher", "Tormented Trobbio", "Bench", "Palestag", "Clover Dancer", "Seth", "Nyleth", "Khann", "Kramelita", "Bench", "Grandmother", "Lost Lace" }; private static string[] PharloomBosses; private static string[] CitadelBosses; private static string[] AbyssBosses; private static string[] SilksongBosses; public static void Initialize() { PharloomBosses = LoadBossList("act1.txt", _defaultPharloomBosses); CitadelBosses = LoadBossList("act2.txt", _defaultCitadelBosses); AbyssBosses = LoadBossList("act3.txt", _defaultAbyssBosses); SilksongBosses = PharloomBosses.Concat(new string[1] { "Bench" }).Concat(CitadelBosses).Concat(new string[1] { "Bench" }) .Concat(AbyssBosses) .ToArray(); } private static Encounter GetEncounterByName(string name, bool is_void) { if (is_void && BossRegistry.EncounterByName.TryGetValue(name + " (Void)", out var value)) { return value; } return BossRegistry.EncounterByName[name]; } private static string[] LoadBossList(string fileName, string[] fallback) { string text = Path.Combine(BossListsDir, fileName); PopLogger.Log("trying to find file path: " + text); if (!File.Exists(text)) { return fallback; } try { string[] array = (from l in File.ReadAllLines(text) select l.Trim() into l where !string.IsNullOrEmpty(l) && !l.StartsWith("#") select l).ToArray(); return (array.Length != 0) ? array : fallback; } catch { return fallback; } } public static Encounter[] PharloomEncounters(bool is_void) { return PharloomBosses.Select((string x) => GetEncounterByName(x, is_void)).ToArray(); } public static Encounter[] CitadelEncounters(bool is_void) { return CitadelBosses.Select((string x) => GetEncounterByName(x, is_void)).ToArray(); } public static Encounter[] AbyssEncounters(bool is_void) { return AbyssBosses.Select((string x) => GetEncounterByName(x, is_void)).ToArray(); } public static Encounter[] SilksongEncounters(bool is_void) { return SilksongBosses.Select((string x) => GetEncounterByName(x, is_void)).ToArray(); } } public struct Encounter { public string Name { get; init; } public SceneLoadInfo SceneInfo { get; init; } public Action BeforeEnter { get; init; } public Action AfterEnter { get; init; } public Action OnExit { get; init; } public Action AfterExit { get; init; } public bool IsSafe { get; init; } public string[] BossPathFromRoot { get; init; } public bool SpecialDeath { get; init; } public float TransitionDelay { get; init; } public Vector2? CustomSpawnPoint { get; init; } public bool SkipTeleport { get; init; } public Action<HealthManager> ScaleHealth { get; init; } public Encounter() { Name = null; SceneInfo = null; BeforeEnter = null; AfterEnter = null; OnExit = null; AfterExit = null; IsSafe = false; CustomSpawnPoint = null; ScaleHealth = null; BossPathFromRoot = new string[0]; SpecialDeath = false; TransitionDelay = 5f; SkipTeleport = false; } } public struct BossInfo { public string Name { get; init; } public string JournalRecordName { get; init; } public List<Encounter> Encounters { get; init; } } public static class BossRegistry { private enum NailType { Old, Sharpened, Shining, Hivesteel, Palesteel } private static Action<HealthManager> ScaleToOldNail = delegate(HealthManager hm) { ScaleToNail(hm, NailType.Old); }; private static Action<HealthManager> ScaleToSharpenedNail = delegate(HealthManager hm) { ScaleToNail(hm, NailType.Sharpened); }; private static Action<HealthManager> ScaleToShiningNail = delegate(HealthManager hm) { ScaleToNail(hm, NailType.Shining); }; private static Action<HealthManager> ScaleToHivesteelNail = delegate(HealthManager hm) { ScaleToNail(hm, NailType.Hivesteel); }; public static List<BossInfo> BossInfos = new List<BossInfo> { new BossInfo { Name = "Mossbone Mother", JournalRecordName = "Mossbone Mother", Encounters = new List<Encounter> { new Encounter { Name = "Moss Mother", SceneInfo = new SceneLoadInfo { SceneName = "Tut_03", EntryGateName = "right1", Visualization = (SceneLoadVisualizations)2, PreventCameraFadeOut = false }, BeforeEnter = delegate { PlayerData.instance.defeatedMossMother = false; PlayerData.instance.encounteredMossMother = false; }, AfterEnter = delegate { GameObject obj7 = GameObjectUtil.FindGameObjectFromScene("Black Thread States/Normal World/Battle Scene/Wave 1"); if (obj7 != null) { GameObject gameObject = obj7.gameObject; if (gameObject != null) { gameObject.SetActive(true); } } }, OnExit = delegate { PlayerData.instance.encounteredMossMother = true; PlayerData.instance.defeatedMossMother = true; }, BossPathFromRoot = new string[1] { "Black Thread States/Normal World/Battle Scene/Wave 1/Mossbone Mother" }, ScaleHealth = ScaleToOldNail }, new Encounter { Name = "Moss Mother 2", SceneInfo = new SceneLoadInfo { SceneName = "Weave_03", EntryGateName = "right1" }, BeforeEnter = delegate { if (((PersistentItemDataCollection<bool, SerializableBoolData>)(object)SceneData.instance.persistentBools).scenes.TryGetValue("Weave_03", out var value9) && value9.TryGetValue("Boss Scene", out var value10)) { value10.Value = false; } }, AfterEnter = delegate { ScaleTransitionHP("Mossbone Mother A", "Control", NailType.Sharpened, "HP P2", "HP Call Buddy", "HP Half"); }, CustomSpawnPoint = new Vector2(15f, 21f), ScaleHealth = ScaleToSharpenedNail } } }, new BossInfo { Name = "Skull King", JournalRecordName = "Skull King", Encounters = new List<Encounter> { new Encounter { Name = "Skull Tyrant", SceneInfo = new SceneLoadInfo { SceneName = "Bone_15", EntryGateName = "left1" }, BeforeEnter = delegate { }, OnExit = delegate { }, BossPathFromRoot = new string[1] { "Boss Scene/Skull King" }, ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Bone Flyer Giant", JournalRecordName = "Bone Flyer Giant", Encounters = new List<Encounter> { new Encounter { Name = "Savage Beastfly", SceneInfo = new SceneLoadInfo { SceneName = "Ant_19", EntryGateName = "left1", Visualization = (SceneLoadVisualizations)0, PreventCameraFadeOut = false }, BeforeEnter = delegate { PlayerData.instance.defeatedBoneFlyerGiant = false; }, OnExit = delegate { PlayerData.instance.defeatedBoneFlyerGiant = true; }, BossPathFromRoot = new string[1] { "Boss Control/Boss Scene/Bone Flyer Giant" }, CustomSpawnPoint = new Vector2(63f, 34.6f), ScaleHealth = ScaleToHivesteelNail }, new Encounter { Name = "Savage Beastfly 2", SceneInfo = new SceneLoadInfo { SceneName = "Bone_East_08", EntryGateName = "right1" }, BeforeEnter = delegate { //IL_000f: Unknown result type (might be due to invalid IL or missing references) ((SerializableNamedList<Completion, NamedCompletion>)(object)PlayerData.instance.QuestCompletionData).SetData("Beastfly Hunt", QuestCompletionData.Accepted); CacheUtils.CachePlayerDataBools("defeatedBoneFlyerGiantGolemScene", "defeatedSongGolem"); PlayerData.instance.defeatedBoneFlyerGiantGolemScene = false; PlayerData.instance.defeatedSongGolem = true; }, OnExit = delegate { CacheUtils.Restore(); }, BossPathFromRoot = new string[1] { "Boss Scene Beastfly/Beastfly States/Active/Bone Flyer Giant" }, CustomSpawnPoint = new Vector2(82f, 8f), ScaleHealth = ScaleToHivesteelNail }, new Encounter { Name = "Savage Beastfly (Void)", SceneInfo = new SceneLoadInfo { SceneName = "Ant_19", EntryGateName = "left1", Visualization = (SceneLoadVisualizations)0, PreventCameraFadeOut = false }, BeforeEnter = delegate { CacheUtils.CachePlayerDataBools("blackThreadWorld"); PlayerData.instance.defeatedBoneFlyerGiant = false; PlayerData.instance.blackThreadWorld = true; }, OnExit = delegate { CacheUtils.Restore(); PlayerData.instance.defeatedBoneFlyerGiant = true; }, BossPathFromRoot = new string[1] { "Boss Control/Boss Scene/Bone Flyer Giant" }, CustomSpawnPoint = new Vector2(63f, 34.6f) }, new Encounter { Name = "Savage Beastfly 2 (Void)", SceneInfo = new SceneLoadInfo { SceneName = "Bone_East_08", EntryGateName = "right1" }, BeforeEnter = delegate { //IL_000f: Unknown result type (might be due to invalid IL or missing references) ((SerializableNamedList<Completion, NamedCompletion>)(object)PlayerData.instance.QuestCompletionData).SetData("Beastfly Hunt", QuestCompletionData.Accepted); CacheUtils.CachePlayerDataBools("defeatedBoneFlyerGiantGolemScene", "defeatedSongGolem", "blackThreadWorld"); PlayerData.instance.defeatedBoneFlyerGiantGolemScene = false; PlayerData.instance.defeatedSongGolem = true; PlayerData.instance.blackThreadWorld = true; }, OnExit = delegate { CacheUtils.Restore(); }, BossPathFromRoot = new string[1] { "Boss Scene Beastfly/Beastfly States/Active/Bone Flyer Giant" }, CustomSpawnPoint = new Vector2(82f, 8f), ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Bone Beast", JournalRecordName = "Bone Beast", Encounters = new List<Encounter> { new Encounter { Name = "Bell Beast", SceneInfo = new SceneLoadInfo { SceneName = "Bone_05", EntryGateName = "left1", Visualization = (SceneLoadVisualizations)2, PreventCameraFadeOut = false }, AfterEnter = delegate { ScaleTransitionHP("Boss Scene/Bone Beast", "Control", NailType.Sharpened, "P2 HP"); }, BeforeEnter = delegate { PlayerData.instance.defeatedBellBeast = false; PlayerData.instance.encounteredBellBeast = true; }, OnExit = delegate { PlayerData.instance.defeatedBellBeast = true; }, BossPathFromRoot = new string[1] { "Boss Scene/Bone Beast" }, CustomSpawnPoint = new Vector2(83f, 4f), ScaleHealth = ScaleToSharpenedNail } } }, new BossInfo { Name = "Dock Guard Thrower", JournalRecordName = "Dock Guard Thrower", Encounters = new List<Encounter> { new Encounter { Name = "Signis & Gron", SceneInfo = new SceneLoadInfo { SceneName = "Dock_09", EntryGateName = "right1" }, BeforeEnter = delegate { }, AfterEnter = delegate { if (GameObjectUtil.FindGameObjectFromScene("Boss Scene/Dock Guard Slasher") != null) { ScaleTransitionHP("Boss Scene/Dock Guard Slasher", "Control", NailType.Hivesteel, "P2 HP", "P3 HP", "P4 HP"); } }, OnExit = delegate { }, BossPathFromRoot = new string[2] { "Boss Scene/Dock Guard Slasher", "Boss Scene/Dock Guard Thrower" }, SpecialDeath = true, CustomSpawnPoint = new Vector2(40f, 8f), ScaleHealth = ScaleToHivesteelNail } } }, new BossInfo { Name = "Song Golem", JournalRecordName = "Song Golem", Encounters = new List<Encounter> { new Encounter { Name = "Fourth Chorus", SceneInfo = new SceneLoadInfo { SceneName = "Bone_East_08", EntryGateName = "right1" }, BeforeEnter = delegate { }, OnExit = delegate { }, BossPathFromRoot = new string[1] { "Boss Scene/song_golem/Song_Butt/SG_waist/Torso/SG_head" }, SpecialDeath = true } } }, new BossInfo { Name = "Bone Hunter Trapper", JournalRecordName = "Bone Hunter Trapper", Encounters = new List<Encounter> { new Encounter { Name = "Gurr", SceneInfo = new SceneLoadInfo { SceneName = "Bone_East_18b", EntryGateName = "top1" }, BeforeEnter = delegate { PlayerData.instance.encounteredAntTrapper = true; }, OnExit = delegate { } } } }, new BossInfo { Name = "Hunter Queen", JournalRecordName = "Hunter Queen", Encounters = new List<Encounter> { new Encounter { Name = "Kramelita", SceneInfo = new SceneLoadInfo { SceneName = "Memory_Ant_Queen", EntryGateName = "door_wakeInMemory", Visualization = (SceneLoadVisualizations)2, PreventCameraFadeOut = false }, BeforeEnter = delegate { PlayerData.instance.defeatedAntQueen = false; }, OnExit = delegate { PlayerData.instance.defeatedAntQueen = true; } } } }, new BossInfo { Name = "Vampire Gnat", JournalRecordName = "Vampire Gnat", Encounters = new List<Encounter> { new Encounter { Name = "Moorwing", SceneInfo = new SceneLoadInfo { SceneName = "Greymoor_08", EntryGateName = "top1", Visualization = (SceneLoadVisualizations)0, PreventCameraFadeOut = true }, BeforeEnter = delegate { PlayerData.instance.defeatedVampireGnatBoss = false; }, AfterEnter = delegate { ScaleTransitionHP("Vampire Gnat Scene/Vampire Gnat", "Control", NailType.Sharpened, "P2 HP"); }, OnExit = delegate { PlayerData.instance.defeatedVampireGnatBoss = true; }, BossPathFromRoot = new string[1] { "Vampire Gnat Scene/Vampire Gnat" }, ScaleHealth = ScaleToSharpenedNail } } }, new BossInfo { Name = "Wisp Pyre Effigy", JournalRecordName = "Wisp Pyre Effigy", Encounters = new List<Encounter> { new Encounter { Name = "Father of Flame", SceneInfo = new SceneLoadInfo { SceneName = "Belltown_08", EntryGateName = "right1" }, BeforeEnter = delegate { }, OnExit = delegate { }, TransitionDelay = 0f, CustomSpawnPoint = new Vector2(67f, 12f) } } }, new BossInfo { Name = "Crawfather", JournalRecordName = "Crawfather", Encounters = new List<Encounter> { new Encounter { Name = "Crawfather", SceneInfo = new SceneLoadInfo { SceneName = "Room_CrowCourt_02", EntryGateName = "top1" }, BeforeEnter = delegate { PlayerData.instance.encounteredCrowCourt = true; }, OnExit = delegate { } } } }, new BossInfo { Name = "Roachkeeper Chef", JournalRecordName = "Roachkeeper Chef", Encounters = new List<Encounter> { new Encounter { Name = "Chef Lugoli", SceneInfo = new SceneLoadInfo { SceneName = "Dust_Chef", EntryGateName = "left1" }, BeforeEnter = delegate { }, OnExit = delegate { }, ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Swamp Shaman", JournalRecordName = "Swamp Shaman", Encounters = new List<Encounter> { new Encounter { Name = "Groal", SceneInfo = new SceneLoadInfo { SceneName = "Shadow_18", EntryGateName = "right1" }, BeforeEnter = delegate { }, OnExit = delegate { }, SpecialDeath = true, CustomSpawnPoint = new Vector2(55f, 12f) } } }, new BossInfo { Name = "Splinter Queen", JournalRecordName = "Splinter Queen", Encounters = new List<Encounter> { new Encounter { Name = "Sister Splinter", SceneInfo = new SceneLoadInfo { SceneName = "Shellwood_18", EntryGateName = "right1" }, BeforeEnter = delegate { }, AfterEnter = delegate { ScaleTransitionHP("Boss Scene Parent/Boss Scene/Splinter Queen", "Control", NailType.Sharpened, "HP", "P2 HP", "P3 HP"); }, OnExit = delegate { }, BossPathFromRoot = new string[1] { "Boss Scene Parent/Boss Scene/Splinter Queen" }, ScaleHealth = ScaleToSharpenedNail } } }, new BossInfo { Name = "Seth", JournalRecordName = "Seth", Encounters = new List<Encounter> { new Encounter { Name = "Seth", SceneInfo = new SceneLoadInfo { SceneName = "Shellwood_22", EntryGateName = "right1" }, BeforeEnter = delegate { }, OnExit = delegate { }, CustomSpawnPoint = new Vector2(121f, 7f) } } }, new BossInfo { Name = "Flower Queen", JournalRecordName = "Flower Queen", Encounters = new List<Encounter> { new Encounter { Name = "Nyleth", SceneInfo = new SceneLoadInfo { SceneName = "Shellwood_11b_Memory", EntryGateName = "door_wakeInMemory" }, BeforeEnter = delegate { }, OnExit = delegate { }, CustomSpawnPoint = new Vector2(18f, 110f) } } }, new BossInfo { Name = "Last Judge", JournalRecordName = "Last Judge", Encounters = new List<Encounter> { new Encounter { Name = "Last Judge", SceneInfo = new SceneLoadInfo { SceneName = "Coral_Judge_Arena", EntryGateName = "left1" }, AfterEnter = delegate { ScaleTransitionHP("Boss Scene/Last Judge", "Control", NailType.Shining, "HP", "HP P2", "HP P3"); }, BeforeEnter = delegate { GameManager.instance.playerData.bellShrineBellhart = true; GameManager.instance.playerData.bellShrineEnclave = true; GameManager.instance.playerData.bellShrineGreymoor = true; GameManager.instance.playerData.bellShrineShellwood = true; GameManager.instance.playerData.bellShrineWilds = true; GameManager.instance.playerData.bellShrineBoneForest = true; GameManager.instance.playerData.encounteredLastJudge = true; GameManager.instance.playerData.defeatedLastJudge = false; if (((PersistentItemDataCollection<bool, SerializableBoolData>)(object)SceneData.instance.persistentBools).scenes.TryGetValue("Coral_Judge_Arena", out var value7) && value7.TryGetValue("Last Judge", out var value8)) { value8.Value = false; } }, OnExit = delegate { }, BossPathFromRoot = new string[1] { "Boss Scene/Last Judge" }, TransitionDelay = 0f, CustomSpawnPoint = new Vector2(25f, 25f), ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Coral Conch Driller Giant", JournalRecordName = "Coral Conch Driller Giant", Encounters = new List<Encounter> { new Encounter { Name = "Double Conchfly", SceneInfo = new SceneLoadInfo { SceneName = "Coral_11", EntryGateName = "left1" }, BeforeEnter = delegate { }, OnExit = delegate { }, TransitionDelay = 0f, CustomSpawnPoint = new Vector2(60f, 15f), ScaleHealth = ScaleToShiningNail }, new Encounter { Name = "Single Conchfly", SceneInfo = new SceneLoadInfo { SceneName = "Coral_27", EntryGateName = "left1" }, BeforeEnter = delegate { }, OnExit = delegate { }, CustomSpawnPoint = new Vector2(20f, 34f) } } }, new BossInfo { Name = "Coral Warrior Grey", JournalRecordName = "Coral Warrior Grey", Encounters = new List<Encounter> { new Encounter { Name = "Watcher", SceneInfo = new SceneLoadInfo { SceneName = "Coral_39", EntryGateName = "right1" }, BeforeEnter = delegate { }, OnExit = delegate { } } } }, new BossInfo { Name = "Coral King", JournalRecordName = "Coral King", Encounters = new List<Encounter> { new Encounter { Name = "Khann", SceneInfo = new SceneLoadInfo { SceneName = "Memory_Coral_Tower", EntryGateName = "door_wakeInMemory" }, BeforeEnter = delegate { }, OnExit = delegate { }, SpecialDeath = true, CustomSpawnPoint = new Vector2(35f, 360f) } } }, new BossInfo { Name = "Song Knight", JournalRecordName = "Song Knight", Encounters = new List<Encounter> { new Encounter { Name = "Second Sentinel", SceneInfo = new SceneLoadInfo { SceneName = "Hang_17b", EntryGateName = "left1" }, BeforeEnter = delegate { }, OnExit = delegate { }, CustomSpawnPoint = new Vector2(32f, 5f), ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Conductor Boss", JournalRecordName = "Conductor Boss", Encounters = new List<Encounter> { new Encounter { Name = "Unravelled", SceneInfo = new SceneLoadInfo { SceneName = "Ward_02", EntryGateName = "right1" }, BeforeEnter = delegate { GameManager.instance.playerData.collectedWardBossKey = true; }, OnExit = delegate { }, TransitionDelay = 0f } } }, new BossInfo { Name = "Clockwork Dancer", JournalRecordName = "Clockwork Dancer", Encounters = new List<Encounter> { new Encounter { Name = "Cogwork Dancers", SceneInfo = new SceneLoadInfo { SceneName = "Cog_Dancers", EntryGateName = "left1" }, BeforeEnter = delegate { }, AfterEnter = delegate { ScaleTransitionHP("Dancer Control", "Control", NailType.Sharpened, "Phase 1 HP", "Phase 2 HP", "Phase 3 HP", "Phase 4 HP"); }, OnExit = delegate { }, TransitionDelay = 0f, ScaleHealth = ScaleToSharpenedNail } } }, new BossInfo { Name = "Trobbio", JournalRecordName = "Trobbio", Encounters = new List<Encounter> { new Encounter { Name = "Trobbio", SceneInfo = new SceneLoadInfo { SceneName = "Library_13", EntryGateName = "left1" }, AfterEnter = delegate { ScaleTransitionHP("Grand Stage Scene/Boss Scene Trobbio/Trobbio", "Control", NailType.Shining, "P2 HP"); }, BeforeEnter = delegate { PlayerData.instance.encounteredTrobbio = true; }, OnExit = delegate { }, BossPathFromRoot = new string[1] { "Grand Stage Scene/Boss Scene Trobbio/Trobbio" }, SpecialDeath = true, ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Tormented Trobbio", JournalRecordName = "Tormented Trobbio", Encounters = new List<Encounter> { new Encounter { Name = "Tormented Trobbio", SceneInfo = new SceneLoadInfo { SceneName = "Library_13", EntryGateName = "left1" }, BeforeEnter = delegate { PlayerData.instance.blackThreadWorld = true; PlayerData.instance.encounteredTormentedTrobbio = true; }, AfterEnter = delegate { GameObject obj6 = GameObjectUtil.FindGameObjectFromScene("Grand Stage Scene/Boss Scene TormentedTrobbio"); if (obj6 != null) { obj6.SetActive(true); } }, OnExit = delegate { }, BossPathFromRoot = new string[1] { "Grand Stage Scene/Boss Scene TormentedTrobbio/Tormented Trobbio" }, CustomSpawnPoint = new Vector2(64f, 15f) } } }, new BossInfo { Name = "Slab Fly Broodmother", JournalRecordName = "Slab Fly Broodmother", Encounters = new List<Encounter> { new Encounter { Name = "Broodmother", SceneInfo = new SceneLoadInfo { SceneName = "Slab_16b", EntryGateName = "left1" }, BeforeEnter = delegate { }, OnExit = delegate { }, CustomSpawnPoint = new Vector2(60f, 6f) } } }, new BossInfo { Name = "Cloverstag White", JournalRecordName = "Cloverstag White", Encounters = new List<Encounter> { new Encounter { Name = "Palestag", SceneInfo = new SceneLoadInfo { SceneName = "Clover_19", EntryGateName = "left1" }, BeforeEnter = delegate { }, OnExit = delegate { } } } }, new BossInfo { Name = "Clover Dancer", JournalRecordName = "Clover Dancer", Encounters = new List<Encounter> { new Encounter { Name = "Clover Dancer", SceneInfo = new SceneLoadInfo { SceneName = "Clover_10", EntryGateName = "left1" }, BeforeEnter = delegate { if (((PersistentItemDataCollection<bool, SerializableBoolData>)(object)SceneData.instance.persistentBools).scenes.TryGetValue("Clover_10", out var value5) && value5.TryGetValue("Dancer A", out var value6)) { value6.Value = false; } }, OnExit = delegate { }, TransitionDelay = 0f, BossPathFromRoot = new string[1] { "Boss Scene/Dancer Control/Dancer A" }, CustomSpawnPoint = new Vector2(93f, 38f) } } }, new BossInfo { Name = "Giant Centipede", JournalRecordName = "Giant Centipede", Encounters = new List<Encounter> { new Encounter { Name = "Bell Eater", SceneInfo = new SceneLoadInfo { SceneName = "Bellway_Centipede_Arena", EntryGateName = "top1" }, BeforeEnter = delegate { }, OnExit = delegate { }, BossPathFromRoot = new string[2] { "Boss Scene/Giant Centipede Butt", "Boss Scene/Giant Centipede Head" }, TransitionDelay = 0f } } }, new BossInfo { Name = "Pinstress Boss", JournalRecordName = "Pinstress Boss", Encounters = new List<Encounter> { new Encounter { Name = "Pinstress", SceneInfo = new SceneLoadInfo { SceneName = "Peak_07", EntryGateName = "top2" }, BeforeEnter = delegate { //IL_000f: Unknown result type (might be due to invalid IL or missing references) ((SerializableNamedList<Completion, NamedCompletion>)(object)PlayerData.instance.QuestCompletionData).SetData("Pinstress Battle", QuestCompletionData.Accepted); CacheUtils.CachePlayerDataBools("blackThreadWorld"); PlayerData.instance.blackThreadWorld = true; }, AfterEnter = delegate { GameObject obj5 = GameObjectUtil.FindGameObjectFromScene("Pinstress Control/Pinstress Scene/Pinstress Boss/NPC"); FsmState state = ((obj5 != null) ? FSMUtility.LocateMyFSM(obj5, "NPC Control") : null).GetState("Wake 3"); state.Transitions = Array.Empty<FsmTransition>(); state.AddTransition("FINISHED", "Battle Start"); }, OnExit = delegate { CacheUtils.Restore(); }, SpecialDeath = true, TransitionDelay = 2f } } }, new BossInfo { Name = "Spinner Boss", JournalRecordName = "Spinner Boss", Encounters = new List<Encounter> { new Encounter { Name = "Widow", SceneInfo = new SceneLoadInfo { SceneName = "Belltown_Shrine", EntryGateName = "top1" }, BeforeEnter = delegate { PlayerData.instance.encounteredSpinner = true; PlayerData.instance.bellShrineBellhart = false; if (((PersistentItemDataCollection<int, SerializableIntData>)(object)SceneData.instance.persistentInts).scenes.TryGetValue("Belltown_Shrine", out var value3) && value3.TryGetValue("Bellshrine Sequence Bellhart", out var value4)) { value4.Value = 0; } }, AfterEnter = delegate { string boss_path = "Black Thread States Thread Only Variant/Normal World/Boss Scene/Spinner Boss"; ScaleTransitionHP(boss_path, "Control", NailType.Old, "P2 HP", "P3 HP"); ScaleTransitionHP(boss_path, "Fake Death", NailType.Old, "P3 HP"); }, AfterExit = delegate { ((MonoBehaviour)GameManager.instance).StartCoroutine(HeroController.instance.Respawn(HeroController.instance.transform)); }, OnExit = delegate { }, BossPathFromRoot = new string[1] { "Black Thread States Thread Only Variant/Normal World/Boss Scene/Spinner Boss" }, TransitionDelay = 0f, CustomSpawnPoint = new Vector2(49f, 28f), ScaleHealth = ScaleToOldNail } } }, new BossInfo { Name = "First Weaver", JournalRecordName = "First Weaver", Encounters = new List<Encounter> { new Encounter { Name = "First Sinner", SceneInfo = new SceneLoadInfo { SceneName = "Slab_10b", EntryGateName = "left1" }, BeforeEnter = delegate { }, AfterEnter = delegate { ScaleTransitionHP("Boss Scene/First Weaver", "Control", NailType.Shining, "HP", "Can Bind HP", "P2 HP", "Max HP"); }, OnExit = delegate { }, CustomSpawnPoint = new Vector2(38f, 10f), TransitionDelay = 0f, ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Phantom", JournalRecordName = "Phantom", Encounters = new List<Encounter> { new Encounter { Name = "Phantom", SceneInfo = new SceneLoadInfo { SceneName = "Organ_01", EntryGateName = "left1" }, BeforeEnter = delegate { PlayerData.instance.encounteredPhantom = true; }, OnExit = delegate { PopLogger.Log("repatched time scale"); }, AfterExit = delegate { ((MonoBehaviour)GameManager.instance).StartCoroutine(HeroController.instance.Respawn(HeroController.instance.transform)); }, BossPathFromRoot = new string[1] { "Boss Scene/Phantom" }, TransitionDelay = 0f, ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Lace", JournalRecordName = "Lace", Encounters = new List<Encounter> { new Encounter { Name = "Fire Lace", SceneInfo = new SceneLoadInfo { SceneName = "Bone_East_12", EntryGateName = "bot1", Visualization = (SceneLoadVisualizations)2, PreventCameraFadeOut = false }, BeforeEnter = delegate { PlayerData.instance.defeatedLace1 = false; PlayerData.instance.encounteredLace1 = true; PlayerData.instance.encounteredLace1Grotto = false; PlayerData.instance.encounteredLaceBlastedBridge = false; }, AfterEnter = delegate { ScaleTransitionHP("Boss Scene/Lace Boss1", "Control", NailType.Old, "Rage HP"); }, OnExit = delegate { PlayerData.instance.defeatedLace1 = true; }, BossPathFromRoot = new string[1] { "Boss Scene/Lace Boss1" }, CustomSpawnPoint = new Vector2(86f, 8f), ScaleHealth = ScaleToOldNail }, new Encounter { Name = "Flower Lace", SceneInfo = new SceneLoadInfo { SceneName = "Song_Tower_01", EntryGateName = "door_cutsceneEndLaceTower" }, AfterEnter = delegate { ScaleTransitionHP("Boss Scene/Lace Boss2 New", "Control", NailType.Shining, "P2 HP", "P3 HP"); GameObject obj4 = GameObject.Find("Boss Scene/Lace Boss2 New"); PlayMakerFSM val2 = ((obj4 != null) ? FSMUtility.LocateMyFSM(obj4, "Control") : null); if (val2 != null) { val2.SendEvent("BATTLE START REFIGHT"); } }, BeforeEnter = delegate { PlayerData.instance.encounteredLaceTower = true; }, OnExit = delegate { }, SkipTeleport = true, ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Silk Boss", JournalRecordName = "Silk Boss", Encounters = new List<Encounter> { new Encounter { Name = "Grandmother", SceneInfo = new SceneLoadInfo { SceneName = "Cradle_03", EntryGateName = "right2" }, BeforeEnter = delegate { }, AfterEnter = delegate { ScaleTransitionHP("Silk Boss", "Phase Control", NailType.Shining, "P1 HP", "P2 HP", "P3 HP", "P4 HP", "P5 HP", "P6 HP"); }, OnExit = delegate { //IL_000a: Unknown result type (might be due to invalid IL or missing references) HeroController.instance.rb2d.linearVelocity = Vector2.zero; }, AfterExit = delegate { FSMUtility.LocateMyFSM(((Component)((Component)GameCameras.instance.hudCamera).transform.Find("In-game/Anchor TL/Hud Canvas Offset/Hud Canvas")).gameObject, "Slide Out").SendEvent("IN"); PlayerData.instance.isInvincible = false; PopLogger.Log("did grandmother changes"); }, TransitionDelay = 0f, CustomSpawnPoint = new Vector2(38f, 134f), ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Lost Lace", JournalRecordName = "Lost Lace", Encounters = new List<Encounter> { new Encounter { Name = "Lost Lace", SceneInfo = new SceneLoadInfo { SceneName = "Abyss_Cocoon", EntryGateName = "door_entry", Visualization = (SceneLoadVisualizations)0, PreventCameraFadeOut = true }, BeforeEnter = delegate { PlayerData.instance.EncounteredLostLace = true; }, OnExit = delegate { GameObject obj3 = GameObject.Find("Hornet Dummy"); if (obj3 != null) { obj3.SetActive(false); } }, AfterExit = delegate { CoroutineUtil.WaitAndExecute(0.1f, delegate { GameObject obj2 = GameObject.Find("Hornet Dummy"); if (obj2 != null) { obj2.SetActive(false); } }); }, TransitionDelay = 0f } } }, new BossInfo { Name = "Shakra", JournalRecordName = "Shakra", Encounters = new List<Encounter> { new Encounter { Name = "Shakra", SceneInfo = new SceneLoadInfo { SceneName = "Greymoor_08", EntryGateName = "left2" }, BeforeEnter = delegate { //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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) PlayerData.instance.mapperMentorConvo = true; PlayerData.instance.mapperSparIntro = true; Completion data = ((SerializableNamedList<Completion, NamedCompletion>)(object)PlayerData.instance.QuestCompletionData).GetData("Shakra Final Quest"); CacheStack<Completion>.Cache(data); ((SerializableNamedList<Completion, NamedCompletion>)(object)PlayerData.instance.QuestCompletionData).SetData("Shakra Final Quest", QuestCompletionData.Completed); }, AfterEnter = delegate { EventRegister.SendEvent("MAPPER CALL", (GameObject)null); GameObject obj = GameObjectUtil.FindGameObjectFromScene("Mapper Spar NPC"); PlayMakerFSM fsm = ((obj != null) ? FSMUtility.LocateMyFSM(obj, "Dialogue") : null); if (fsm == null) { PopLogger.Log("start was null"); } else { ((MonoBehaviour)GameManager.instance).StartCoroutine(WaitForState()); } IEnumerator WaitForState() { PopLogger.Log("waiting for state...."); yield return (object)new WaitUntil((Func<bool>)(() => fsm.ActiveStateName == "Idle")); yield return (object)new WaitForEndOfFrame(); fsm.SetState("Start Battle"); } }, OnExit = delegate { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) Completion val = CacheStack<Completion>.Get(); ((SerializableNamedList<Completion, NamedCompletion>)(object)PlayerData.instance.QuestCompletionData).SetData("Shakra Final Quest", val); }, SpecialDeath = true, TransitionDelay = 0f, ScaleHealth = ScaleToShiningNail } } }, new BossInfo { Name = "Zap Core Enemy", JournalRecordName = "Zap Core Enemy", Encounters = new List<Encounter> { new Encounter { Name = "Voltvyrm", SceneInfo = new SceneLoadInfo { SceneName = "Coral_29", EntryGateName = "left1" }, SpecialDeath = true, ScaleHealth = ScaleToHivesteelNail } } }, new BossInfo { Name = "Garmond", JournalRecordName = "Garmond", Encounters = new List<Encounter> { new Encounter { Name = "Garmond and Zaza", SceneInfo = new SceneLoadInfo { SceneName = "Library_09", EntryGateName = "left1" }, AfterEnter = delegate { ((MonoBehaviour)GameManager.instance).StartCoroutine(Wait()); static IEnumerator Wait() { GameObject go = GameObjectUtil.FindGameObjectFromScene("Black Thread States/Normal World/Scene Control/Garmond Scene/Garmond Fighter/Citadel Library NPC"); if (go == null) { PopLogger.Log("garmond go was null"); } else { yield return (object)new WaitUntil((Func<bool>)(() => go.activeSelf)); FSMUtility.LocateMyFSM(go, "Dialogue").SetState("Battle Start"); PopLogger.Log("set fsm to start battle"); } } }, BeforeEnter = delegate { GameManager.instance.playerData.garmondInLibrary = true; }, SpecialDeath = true, TransitionDelay = 2f, ScaleHealth = ScaleToShiningNail }, new Encounter { Name = "Lost Garmond", SceneInfo = new SceneLoadInfo { SceneName = "Coral_33", EntryGateName = "right1" }, BeforeEnter = delegate { CacheUtils.CachePlayerDataBools("blackThreadWorld"); PlayerData.instance.blackThreadWorld = true; }, OnExit = delegate { CacheUtils.Restore(); }, CustomSpawnPoint = new Vector2(41f, 62f) } } }, new BossInfo { Name = "Bench", JournalRecordName = "Bench", Encounters = new List<Encounter> { new Encounter { Name = "Bench", SceneInfo = new SceneLoadInfo { SceneName = "Belltown_Room_Spare", EntryGateName = "left1" }, BeforeEnter = delegate { PlayerData.instance.BelltownFurnishingDesk = true; PlayerData.instance.BelltownFurnishingFairyLights = true; PlayerData.instance.BelltownFurnishingGramaphone = true; PlayerData.instance.BelltownFurnishingSpa = true; PlayerData.instance.BelltownFurnishingSpaAvailable = true; }, TransitionDelay = 0f } } }, new BossInfo { Name = "Blue Assistant", JournalRecordName = "Blue Assistant", Encounters = new List<Encounter> { new Encounter { Name = "Zango", SceneInfo = new SceneLoadInfo { SceneName = "Crawl_10", EntryGateName = "right1" }, BeforeEnter = delegate { CacheUtils.CachePlayerDataBools("BlueScientistDead", "blackThreadWorld"); PlayerData.instance.blackThreadWorld = true; if (((PersistentItemDataCollection<bool, SerializableBoolData>)(object)SceneData.instance.persistentBools).scenes.TryGetValue("Crawl_10", out var value) && value.TryGetValue("Blue Assistant", out var value2)) { value2.Value = false; } }, OnExit = delegate { CacheUtils.Restore(); }, TransitionDelay = 3f, CustomSpawnPoint = new Vector2(32f, 8f) } } } }; public static Dictionary<string, BossInfo> BossInfoByName = BossInfos.ToDictionary((BossInfo x) => x.Name, (BossInfo x) => x); public static Dictionary<string, Encounter> EncounterByName = AllEncounter.ToDictionary((Encounter x) => x.Name, (Encounter x) => x); public static HashSet<string> ValidRecords => BossInfos.Select((BossInfo x) => x.JournalRecordName).ToHashSet(); public static List<Encounter> AllEncounter => BossInfos.SelectMany((BossInfo x) => x.Encounters).ToList(); private static float GetNailDamage(NailType type) { return type switch { NailType.Old => 5f, NailType.Sharpened => 9f, NailType.Shining => 13f, NailType.Hivesteel => 17f, NailType.Palesteel => 21f, _ => throw new NotImplementedException(), }; } private static float GetMultiplier(NailType type) { float num = GetNailDamage(NailType.Palesteel) / GetNailDamage(type); return Mathf.Max(1f, num); } private static void ScaleToNail(HealthManager hm, NailType type) { hm.hp = Mathf.CeilToInt(GetMultiplier(type) * (float)hm.hp); hm.initHp = hm.hp; PopLogger.Log($"Scaled health for {((Component)hm).gameObject} to {hm.hp}"); } private static void ScaleTransitionHP(string boss_path, string fsm_name, NailType type, params string[] variables) { GameObject val = GameObject.Find(boss_path) ?? GameObjectUtil.FindGameObjectFromScene(boss_path); if (val == null) { PopLogger.Log("Could not find boss to scale HP"); return; } PlayMakerFSM val2 = FSMUtility.LocateMyFSM(val, fsm_name); if (val2 == null) { PopLogger.Log("could not find fsm for scaling"); return; } foreach (string text in variables) { FsmInt fsmInt = val2.FsmVariables.GetFsmInt(text); if (fsmInt == null) { PopLogger.Log("Could not find fsm int: " + text); continue; } fsmInt.Value = Mathf.CeilToInt((float)fsmInt.Value * GetMultiplier(type)); PopLogger.Log($"Setting {text} to {fsmInt.Value}"); } PopLogger.Log($"Patched transition hps for {val}"); } internal static bool HasSecondEncounter(string name) { return name switch { "Mossbone Mother" => true, "Lace" => true, "Coral Conch Driller Giant" => true, "Garmond" => true, "Bone Flyer Giant" => true, _ => false, }; } internal static (bool, bool) GetMultipleEncounterInfo(string name) { if (PlayerData.instance == null) { PopLogger.LogError("player data instance was null while looking up " + name); return (false, false); } return name switch { "Mossbone Mother" => (PlayerData.instance.defeatedMossMother, true), "Lace" => (PlayerData.instance.defeatedLace1, PlayerData.instance.defeatedLaceTower), "Coral Conch Driller Giant" => (PlayerData.instance.defeatedCoralDrillerSolo, PlayerData.instance.defeatedCoralDrillers), "Garmond" => (true, PlayerData.instance.garmondBlackThreadDefeated), "Bone Flyer Giant" => (PlayerData.instance.defeatedBoneFlyerGiant, PlayerData.instance.defeatedBoneFlyerGiantGolemScene), _ => (false, false), }; } } public class Fireworks : MonoBehaviour { public float Size = 1f; public Color Color = Color.white; private ParticleSystem ps; public static GameObject CreateFirework(Vector3 position, float size, Color color, Transform parent = null) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0027: 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_0045: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("CodeFirework"); if ((Object)(object)parent != (Object)null) { val.transform.SetParent(parent, true); } val.transform.position = position; Fireworks fireworks = val.AddComponent<Fireworks>(); fireworks.Size = Mathf.Max(0.01f, size); fireworks.Color = color; return val; } private void Awake() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Expected O, but got Unknown //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019a: 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_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Expected O, but got Unknown //IL_02f4: Unknown result type (might be due to invalid IL or missing references) ps = ((Component)this).gameObject.AddComponent<ParticleSystem>(); ParticleSystemRenderer component = ((Component)this).gameObject.GetComponent<ParticleSystemRenderer>(); component.renderMode = (ParticleSystemRenderMode)0; component.alignment = (ParticleSystemRenderSpace)1; component.sortMode = (ParticleSystemSortMode)2; MainModule main = ps.main; ((MainModule)(ref main)).loop = false; ((MainModule)(ref main)).duration = 1.2f; ((MainModule)(ref main)).startLifetime = new MinMaxCurve(0.8f, 1.6f); ((MainModule)(ref main)).startSpeed = new MinMaxCurve(4f * Size, 8f * Size); ((MainModule)(ref main)).startSize = new MinMaxCurve(0.06f * Size, 0.18f * Size); ((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(Color); ((MainModule)(ref main)).gravityModifier = MinMaxCurve.op_Implicit(0f); ((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)1; ((MainModule)(ref main)).maxParticles = 1000; EmissionModule emission = ps.emission; ((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(0f); ((EmissionModule)(ref emission)).SetBursts((Burst[])(object)new Burst[1] { new Burst(0f, (short)(60f * Size), (short)(120f * Size), 1, 0f) }); ShapeModule shape = ps.shape; ((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0; ((ShapeModule)(ref shape)).radius = 0.1f * Size; ColorOverLifetimeModule colorOverLifetime = ps.colorOverLifetime; ((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled = true; Gradient val = new Gradient(); val.SetKeys((GradientColorKey[])(object)new GradientColorKey[2] { new GradientColorKey(Color, 0f), new GradientColorKey(Color, 1f) }, (GradientAlphaKey[])(object)new GradientAlphaKey[2] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(0f, 1f) }); ((ColorOverLifetimeModule)(ref colorOverLifetime)).color = new MinMaxGradient(val); TrailModule trails = ps.trails; ((TrailModule)(ref trails)).enabled = true; ((TrailModule)(ref trails)).mode = (ParticleSystemTrailMode)1; ((TrailModule)(ref trails)).ratio = 1f; ((TrailModule)(ref trails)).lifetime = MinMaxCurve.op_Implicit(0.35f * Size); VelocityOverLifetimeModule velocityOverLifetime = ps.velocityOverLifetime; ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).enabled = true; ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).space = (ParticleSystemSimulationSpace)0; ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).x = new MinMaxCurve(-2f * Size, 2f * Size); ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).y = new MinMaxCurve(-2f * Size, 2f * Size); ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).z = new MinMaxCurve(-2f * Size, 2f * Size); try { Material val2 = new Material(Shader.Find("Sprites/Default")); val2.SetFloat("_Mode", 0f); val2.SetColor("_TintColor", Color); ((Renderer)component).material = val2; } catch { } } private IEnumerator Start() { yield return null; if (!((Object)(object)ps == (Object)null)) { MainModule main = ps.main; ((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(Color); ps.Play(); while ((Object)(object)ps != (Object)null && ps.IsAlive(true)) { yield return null; } Object.Destroy((Object)(object)((Component)this).gameObject); } } public static Color ParseColor(string htmlColor, Color fallback) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(htmlColor)) { return fallback; } Color result = default(Color); if (ColorUtility.TryParseHtmlString(htmlColor, ref result)) { return result; } return fallback; } } public interface IBindings { void ApplyBindings(); void RestoreBindings(); } public class HealthBindings : IBindings { [CompilerGenerated] private int <boundMax>P; private List<GameObject> _overlays; private List<GameObject> _modifiedHealthDisplays; private int _prevMaxHealth; public bool Active { get; private set; } public HealthBindings(int boundMax) { <boundMax>P = boundMax; _overlays = new List<GameObject>(); _modifiedHealthDisplays = new List<GameObject>(); base..ctor(); } public void ApplyUI() { _overlays.Clear(); Transform hudCanvas = Bindings.HudCanvas; GameObject health = ((Component)hudCanvas.Find("Health")).gameObject; PopLogger.Log($"Found health {health}"); foreach (PlayMakerFSM item in from i in Enumerable.Range(0, health.transform.childCount) select FSMUtility.LocateMyFSM(((Component)health.transform.GetChild(i)).gameObject, "health_display")) { if (item != null) { FsmInt fsmInt = item.FsmVariables.GetFsmInt("Health Number"); int? num = ((fsmInt != null) ? new int?(fsmInt.Value) : null); if (num.HasValue && num.Value > <boundMax>P && num.Value <= 10 && num.Value <= _prevMaxHealth) { ((Behaviour)item).enabled = false; GameObject gameObject = ((Component)item).gameObject; PopLogger.Log($"Found health display for health number {num.Value} at {gameObject}"); GameObject gameObject2 = ((Component)((Component)gameObject.transform.Find("Idle")).gameObject.transform.parent).gameObject; gameObject2.SetActive(false); _modifiedHealthDisplays.Add(gameObject2); } } } EventRegister.SendEvent(EventRegisterEvents.HealthUpdate, (GameObject)null); EventRegister.SendEvent(EventRegisterEvents.HeroHealed, (GameObject)null); EventRegister.SendEvent(EventRegisterEvents.HeroHealedToMax, (GameObject)null); } public void RestoreUI() { foreach (GameObject overlay in _overlays) { if (overlay != null) { overlay.SetActive(false); } } _overlays.Clear(); foreach (GameObject modifiedHealthDisplay in _modifiedHealthDisplays) { if (modifiedHealthDisplay != null) { modifiedHealthDisplay.SetActive(true); } } EventRegister.SendEvent(EventRegisterEvents.HealthUpdate, (GameObject)null); EventRegister.SendEvent(EventRegisterEvents.HeroHealed, (GameObject)null); } public void ApplyBindings() { Active = true; _prevMaxHealth = PlayerData.instance.maxHealth; PlayerData.instance.maxHealth = <boundMax>P; PlayerData.instance.maxHealthBase = <boundMax>P; PlayerData.instance.health = Mathf.Min(<boundMax>P, PlayerData.instance.health); ApplyUI(); } public void RestoreBindings() { Active = false; PlayerData.instance.maxHealth = _prevMaxHealth; PlayerData.instance.health = _prevMaxHealth; PlayerData.instance.maxHealthBase = _prevMaxHealth; RestoreUI(); } } public class SilkBindings : IBindings { [CompilerGenerated] private int <maxSilk>P; private int _prevSilk; public bool Active { get; private set; } public SilkBindings(int maxSilk) { <maxSilk>P = maxSilk; base..ctor(); } private void ApplyUI() { EventRegister.SendEvent(EventRegisterEvents.RegeneratedSilkChunk, (GameObject)null); } private void RestoreUI() { EventRegister.SendEvent(EventRegisterEvents.RegeneratedSilkChunk, (GameObject)null); } public void ApplyBindings() { _prevSilk = PlayerData.instance.silkMax; PlayerData.instance.silkMax = <maxSilk>P; PlayerData.instance.silk = Mathf.Min(<maxSilk>P, PlayerData.instance.silk); ApplyUI(); Active = true; } public void RestoreBindings() { PlayerData.instance.silkMax = _prevSilk; Active = false; RestoreUI(); } } public class NailBindings : IBindings { [CompilerGenerated] private float <multiplier>P; public float Multiplier => <multiplier>P; public bool Active { get; private set; } public NailBindings(float multiplier) { <multiplier>P = multiplier; base..ctor(); } public void ApplyBindings() { Hooks.EnableBoundNail(); Active = true; } public void RestoreBindings() { Hooks.DisableBoundNail(); Active = false; } } public class SkillBindings : IBindings { private Dictionary<string, List<string>> _removedTools = new Dictionary<string, List<string>>(); public bool Active { get; private set; } public void ApplyBindings() { Active = true; _removedTools = CrestUtil.RemoveAllTools((ToolItemType x) => (int)x != 3); Hooks.EnablePreventToolUse(); } public void RestoreBindings() { Active = false; CrestUtil.RestoreAllTools(_removedTools, (ToolItemType x) => (int)x != 3); Hooks.DisablePreventToolUse(); } } public class ToolBindings : IBindings { private Dictionary<string, List<string>> _removedTools = new Dictionary<string, List<string>>(); public bool Active { get; private set; } public void ApplyBindings() { Active = true; _removedTools = CrestUtil.RemoveAllTools((ToolItemType x) => (int)x == 3); Hooks.EnablePreventToolUse(); } public void RestoreBindings() { Active = false; CrestUtil.RestoreAllTools(_removedTools, (ToolItemType x) => (int)x == 3); Hooks.DisablePreventToolUse(); } } public static class Bindings { public static HealthBindings HealthBindings = new HealthBindings(4); public static SilkBindings SilkBindings = new SilkBindings(9); public static NailBindings NailBindings = new NailBindings(0.6f); public static SkillBindings SkillBindings = new SkillBindings(); public static ToolBindings ToolBindings = new ToolBindings(); public static Transform HudCanvas => ((Component)GameCameras.instance.hudCamera).transform.Find("In-game/Anchor TL/Hud Canvas Offset/Hud Canvas"); private static void Reset() { HealthBindings = new HealthBindings(4); SilkBindings = new SilkBindings(9); NailBindings = new NailBindings(0.6f); SkillBindings = new SkillBindings(); ToolBindings = new ToolBindings(); } } public class Hooks { public enum HookBehavior { DoubleDamage, InfiniteDamage, BoundNail, PreventToolUse } private static readonly Dictionary<HookBehavior, ToggleableHook> _hooks = new Dictionary<HookBehavior, ToggleableHook>(); private static Action<Action<PlayerData, int, bool, bool>, PlayerData, int, bool, bool> CreateDamageHook(int multiplier, bool absolute) { return delegate(Action<PlayerData, int, bool, bool> orig, PlayerData self, int amount, bool blue, bool mask) { PopLogger.Log($"calling orig with damage: {multiplier * amount}"); orig(self, multiplier * amount, blue, !absolute && mask); }; } private static void BindNailDamageHook(Action<HealthManager, HitInstance> orig, HealthManager self, HitInstance hit) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 if ((int)hit.AttackType == 0 || (int)hit.AttackType == 7 || (int)hit.AttackType == 16) { hit.DamageDealt = Math.Min(hit.DamageDealt, Mathf.CeilToInt((float)PlayerData.instance.nailDamage * Bindings.NailBindings.Multiplier)); } orig(self, hit); } private static bool InventoryToolSlotHook(Func<InventoryItemTool, bool> orig, InventoryItemTool self) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)self.ToolType == 3) { if (Bindings.SkillBindings.Active) { PopLogger.Log("Preventing tool use for " + ((Object)self).name); return false; } } else if (Bindings.ToolBindings.Active) { PopLogger.Log("Preventing tool use for " + ((Object)self).name); return false; } return orig(self); } public static void Initialize() { _hooks[HookBehavior.DoubleDamage] = new ToggleableHook(AccessTools.DeclaredMethod(typeof(PlayerData), "TakeHealth", (Type[])null, (Type[])null), CreateDamageHook(2, absolute: false)); _hooks[HookBehavior.InfiniteDamage] = new ToggleableHook(AccessTools.DeclaredMethod(typeof(PlayerData), "TakeHealth", (Type[])null, (Type[])null), CreateDamageHook(56, absolute: true)); _hooks[HookBehavior.BoundNail] = new ToggleableHook(AccessTools.DeclaredMethod(typeof(HealthManager), "TakeDamage", (Type[])null, (Type[])null), new Action<Action<HealthManager, HitInstance>, HealthManager, HitInstance>(BindNailDamageHook)); _hooks[HookBehavior.PreventToolUse] = new ToggleableHook(AccessTools.DeclaredMethod(typeof(InventoryItemTool), "Submit", (Type[])null, (Type[])null), new Func<Func<InventoryItemTool, bool>, InventoryItemTool, bool>(InventoryToolSlotHook)); } public static void EnableDoubleDamage() { PopLogger.Log("enabling double damage"); _hooks[HookBehavior.DoubleDamage].Enable(); } public static void DisableDoubleDamage() { PopLogger.Log("disabling double damage"); _hooks[HookBehavior.DoubleDamage].Disable(); } public static void EnableInfiniteDamage() { _hooks[HookBehavior.InfiniteDamage].Enable(); } public static void DisableInfiniteDamage() { _hooks[HookBehavior.InfiniteDamage].Disable(); } public static void EnableBoundNail() { _hooks[HookBehavior.BoundNail].Enable(); } public static void DisableBoundNail() { _hooks[HookBehavior.BoundNail].Disable(); } public static void EnablePreventToolUse() { _hooks[HookBehavior.PreventToolUse].Enable(); } public static void DisablePreventToolUse() { if (!Bindings.SkillBindings.Active && !Bindings.ToolBindings.Active) { _hooks[HookBehavior.PreventToolUse].Disable(); } } } public static class PopLogger { public static void Log(object message) { } public static void LogWarning(object message) { } public static void LogError(object message) { } } [BepInPlugin("momochi003.pop", "Pantheon of Pharloom", "1.3.2")] public sealed class PantheonOfPharloom : BaseUnityPlugin { [HarmonyPatch(typeof(JournalItemManager), "GetItems")] internal static class Patch_GetItems { [HarmonyPrefix] private static bool Prefix(JournalItemManager __instance, ref List<EnemyJournalRecord> __result) { if (((Object)((Component)__instance).gameObject).name == "Bosses") { List<EnemyJournalRecord> list = new List<EnemyJournalRecord>(); foreach (EnemyJournalRecord item in from x in EnemyJournalManager.GetAllEnemies() where BossRegistry.ValidRecords.Contains(((Object)x).name) select x) { if ((((Object)item).name == "Shakra" || ((Object)item).name == "Blue Assistant") && PlayerData.instance.blackThreadWorld) { item.isAlwaysUnlocked = true; } list.Add(item); } __result = list; return false; } return true; } } [HarmonyPatch(typeof(InventoryItemCollectableManager), "GetItems")] internal static class PatchInventory_GetItems { [HarmonyPrefix] private static bool Prefix(InventoryItemCollectableManager __instance, ref List<CollectableItem> __result) { if (((Object)((Component)__instance).gameObject).name == "BossRush") { __result = new List<CollectableItem>(); return false; } return true; } } [HarmonyPatch(typeof(JournalItemManager), "SetDisplay", new Type[] { typeof(InventoryItemSelectable) })] internal static class Patch_SetDisplay { [HarmonyPrefix] private static bool Prefix(JournalItemManager __instance, InventoryItemSelectable selectable) { if ((Object)(object)__instance == (Object)null || (Object)(object)selectable == (Object)null) { return true; } if ((Object)(object)((Component)__instance).gameObject == (Object)null) { return true; } if (((Object)((Component)__instance).gameObject).name == "Bosses") { PopLogger.Log(selectable); ((Component)__instance).gameObject.GetComponent<BossesUIController>().BossItemSelected?.Invoke(selectable); } return true; } } [HarmonyPatch(typeof(GameManager), "ReadyForRespawn", new Type[] { typeof(bool) })] internal static class Patch_ReadyForRespawn { [HarmonyPostfix] private static void Postfix(bool isFirstLevelForPlayer) { if (isFirstLevelForPlayer) { PopLogger.Log("called game loaded...."); Instance.GameLoaded(); } } } [HarmonyPatch(typeof(GameManager), "SaveGame", new Type[] { typeof(int), typeof(Action<bool>), typeof(bool), typeof(AutoSaveName) })] internal static class Patch_SaveGame { private static bool Prefix(int saveSlot, Action<bool> ogCallback, bool withAutoSave, AutoSaveName autoSaveName) { ogCallback?.Invoke(obj: true); return false; } } private static bool _initializingMenu = false; private static bool _initialized = false; public bool CusorEnabled { get; set; } public static PantheonOfPharloom Instance { get; private set; } public static Dictionary<string, GameObject> PreloadedObjects { get; } = new Dictionary<string, GameObject>(); public event Action GameLoaded; private GameObject CreatePrefab(GameObject original) { GameObject val = Object.Instantiate<GameObject>(original); Object.DontDestroyOnLoad((Object)(object)val); val.SetActive(false); PopLogger.Log($"Created new game object: {val}"); return val; } private void Awake() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) new Harmony("patcher").PatchAll(); Hooks.Initialize(); Instance = this; SceneManager.activeSceneChanged += delegate(Scene from, Scene to) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if (((Scene)(ref to)).name == "Menu_Title" && !_initialized) {