예제 #1
0
        private static void CheckFireball(On.PlayMakerFSM.orig_OnEnable orig, PlayMakerFSM self)
        {
            // Wall Impact state doesn't exist in shade soul control
            // Fireballs get recycled so we've gotta check for the state this method adds as well
            if (self.FsmName != "Fireball Control" || self.GetState("Wall Impact") == null || self.GetState("Idle (No Collision)") != null)
            {
                orig(self);
                return;
            }

            // Store the terrain checker reference, prevent it from being enabled
            GameObject terrainChecker = self.GetAction <ActivateGameObject>("Pause", 3).gameObject.GameObject.Value;

            self.GetState("Pause").RemoveAction(3);

            // Create a new state before the regular idle
            FsmState idleNoCol = self.CopyState("R", "Idle (No Collision)");

            idleNoCol.RemoveAction(0);

            self.GetState("L").ChangeTransition("FINISHED", "Idle (No Collision)");
            self.GetState("R").ChangeTransition("FINISHED", "Idle (No Collision)");

            idleNoCol.AddTransition("FINISHED", "Idle");

            // New state needs to start the fireball moving
            idleNoCol.AddAction(new SetVelocity2d
            {
                gameObject = new FsmOwnerDefault(),
                vector     = Vector2.zero,
                x          = self.FsmVariables.FindFsmFloat("Velocity"),
                y          = 0,
                everyFrame = false
            });

            // Small waiting period before proceeding to the old idle state
            idleNoCol.AddAction(new Wait
            {
                time        = NO_COLLISION_TIME,
                finishEvent = FsmEvent.FindEvent("FINISHED"),
                realTime    = false
            });

            var idle = self.GetState("Idle");

            // Idle state needs to activate the collision now
            idle.InsertAction(new ActivateGameObject
            {
                gameObject = new FsmOwnerDefault
                {
                    GameObject  = terrainChecker,
                    OwnerOption = OwnerDefaultOption.SpecifyGameObject
                },
                activate    = true,
                recursive   = false,
                resetOnExit = false,
                everyFrame  = false
            }, 0);

            // Account for the additional waiting time before Idle
            idle.GetAction <Wait>(8).time.Value -= NO_COLLISION_TIME;

            orig(self);
        }
        public override void Process(string scene, Object changeObj)
        {
            if (scene != _sceneName || !(changeObj is PlayMakerFSM fsm) || fsm.FsmName != _fsmName ||
                fsm.gameObject.name != _objectName)
            {
                return;
            }

            FsmState pdBool      = fsm.GetState("PD Bool?");
            FsmState charm       = fsm.GetState("Charm?");
            FsmState bigItem     = fsm.GetState("Big Item?");
            FsmState bigGetFlash = fsm.GetState("Big Get Flash");
            FsmState trinkFlash  = fsm.GetState("Trink Flash");
            FsmState giveTrinket = fsm.GetState("Store Key");

            // Remove actions that stop shiny from spawning
            pdBool.RemoveActionsOfType <StringCompare>();

            // Change pd bool test to our new bool
            pdBool.RemoveActionsOfType <PlayerDataBoolTest>();
            pdBool.AddAction(
                new RandomizerExecuteLambda(() => fsm.SendEvent(
                                                RandomizerMod.Instance.Settings.CheckLocationFound(_location) ? "COLLECTED" : null
                                                )));


            // Charm must be preserved as the entry point for AddYNDialogueToShiny
            charm.ClearTransitions();
            charm.AddTransition("FINISHED", "Big Item?");

            // Check if each additive item has already been obtained. Give 100 geo instead of popup if so.
            bigItem.ClearTransitions();
            bigItem.AddFirstAction(new RandomizerExecuteLambda(() => bigItem.AddTransition("FINISHED", BigItemPopup.AdditiveMaxedOut(_itemDefs) ? "Trink Flash" : "Big Get Flash"))); // if we have duplicates, the last item is not a big popup

            // give 300 geo for last duplicate
            trinkFlash.ClearTransitions();
            trinkFlash.AddTransition("FINISHED", "Store Key");
            fsm.GetState("Trinket Type").ClearTransitions();
            trinkFlash.AddTransition("FINISHED", "Store Key");
            giveTrinket.RemoveActionsOfType <SetPlayerDataBool>();
            giveTrinket.AddAction(new RandomizerExecuteLambda(() => GiveItem(GiveAction.AddGeo, _item, _location, 300)));
            giveTrinket.GetActionsOfType <GetLanguageString>().First().convName     = _itemDefs.Last().NameKey;
            giveTrinket.GetActionsOfType <SetSpriteRendererSprite>().First().sprite = RandomizerMod.GetSprite(LogicManager.GetItemDef(_itemDefs.Last().Name).shopSpriteKey);

            // Normal path for big items. Set bool and show the popup after the flash
            bigGetFlash.AddAction(new RandomizerCallStaticMethod(
                                      typeof(BigItemPopup),
                                      nameof(BigItemPopup.ShowAdditive),
                                      _itemDefs,
                                      fsm.gameObject,
                                      "GET ITEM MSG END"));

            // Don't actually need to set the skill here, that happens in BigItemPopup
            // Maybe change that at some point, it's not where it should happen
            bigGetFlash.AddAction(new RandomizerExecuteLambda(() => GiveItem(_action, _item, _location)));

            // Exit the fsm after the popup
            bigGetFlash.ClearTransitions();
            bigGetFlash.AddTransition("GET ITEM MSG END", "Hero Up");
            bigGetFlash.AddTransition("HERO DAMAGED", "Finish");
        }
예제 #3
0
        /*
         * Better organization someday...
         */
        public static void MiscQoLChanges(Scene newScene)
        {
            string sceneName = newScene.name;

            // Make baldurs always able to spit rollers and reduce hp
            if (sceneName == SceneNames.Crossroads_11_alt || sceneName == SceneNames.Crossroads_ShamanTemple ||
                sceneName == SceneNames.Fungus1_28)
            {
                foreach (GameObject obj in Object.FindObjectsOfType <GameObject>())
                {
                    if (obj.name.Contains("Blocker"))
                    {
                        HealthManager hm = obj.GetComponent <HealthManager>();
                        if (hm != null)
                        {
                            hm.hp = 5;
                        }
                        PlayMakerFSM fsm = FSMUtility.LocateFSM(obj, "Blocker Control");
                        if (fsm != null)
                        {
                            fsm.GetState("Can Roller?").RemoveActionsOfType <IntCompare>();
                        }
                    }
                }
            }

            switch (sceneName)
            {
            // Lemm sell all

            /*
             * case SceneNames.Ruins1_05b when RandomizerMod.Instance.Settings.Lemm:
             *  PlayMakerFSM lemm = FSMUtility.LocateFSM(GameObject.Find("Relic Dealer"), "npc_control");
             *  lemm.GetState("Convo End").AddAction(new RandomizerSellRelics());
             *  break;
             */

            // Grubfather rewards are given out all at once
            case SceneNames.Crossroads_38 when RandomizerMod.Instance.Settings.Grubfather:
                PlayMakerFSM grubDaddy = FSMUtility.LocateFSM(GameObject.Find("Grub King"), "King Control");
                grubDaddy.GetState("Final Reward?").RemoveTransitionsTo("Recover");
                grubDaddy.GetState("Final Reward?").AddTransition("FINISHED", "Recheck");
                grubDaddy.GetState("Recheck").RemoveTransitionsTo("Gift Anim");
                grubDaddy.GetState("Recheck").AddTransition("FINISHED", "Activate Reward");

                int geoTotal = 0;
                grubDaddy.GetState("All Given").AddAction(new RandomizerAddGeo(grubDaddy.gameObject, 0, true));
                grubDaddy.GetState("Recheck").AddFirstAction(new RandomizerExecuteLambda(() =>
                                                                                         grubDaddy.GetState("All Given").GetActionsOfType <RandomizerAddGeo>()[0].SetGeo(geoTotal)));

                foreach (PlayMakerFSM grubFsm in grubDaddy.gameObject.GetComponentsInChildren <PlayMakerFSM>(true))
                {
                    if (grubFsm.FsmName == "grub_reward_geo")
                    {
                        FsmState grubGeoState = grubFsm.GetState("Remaining?");
                        int      geo          = grubGeoState.GetActionsOfType <IntCompare>()[0].integer1.Value;

                        grubGeoState.RemoveActionsOfType <FsmStateAction>();
                        grubGeoState.AddAction(new RandomizerExecuteLambda(() => geoTotal += geo));
                        grubGeoState.AddTransition("FINISHED", "End");
                    }
                }
                break;

            // Great Hopper Easter Egg, I guess
            case SceneNames.Deepnest_East_16:
                GameObject hopper1 = newScene.FindGameObject("Giant Hopper");
                GameObject hopper2 = newScene.FindGameObject("Giant Hopper (1)");

                for (int i = 0; i < 10; i++)
                {
                    GameObject newHopper1 = Object.Instantiate(hopper1, hopper1.transform.parent);
                    GameObject newHopper2 = Object.Instantiate(hopper2, hopper2.transform.parent);

                    HealthManager hopper1HM = newHopper1.GetComponent <HealthManager>();
                    hopper1HM.SetGeoSmall(0);
                    hopper1HM.SetGeoMedium(0);
                    hopper1HM.SetGeoLarge(0);

                    HealthManager hopper2HM = newHopper2.GetComponent <HealthManager>();
                    hopper2HM.SetGeoSmall(0);
                    hopper2HM.SetGeoMedium(0);
                    hopper2HM.SetGeoLarge(0);

                    Vector3 hopper1Pos = newHopper1.transform.localPosition;
                    hopper1Pos = new Vector3(
                        hopper1Pos.x + i,
                        hopper1Pos.y,
                        hopper1Pos.z);
                    newHopper1.transform.localPosition = hopper1Pos;

                    Vector3 hopper2Pos = newHopper2.transform.localPosition;
                    hopper2Pos = new Vector3(
                        hopper2Pos.x + i - 4,
                        hopper2Pos.y,
                        hopper2Pos.z);
                    newHopper2.transform.localPosition = hopper2Pos;
                }
                break;

            // Skip dreamer text before Dream Nail
            case SceneNames.RestingGrounds_04:
                FsmState dreamerPlaqueInspect = FSMUtility
                                                .LocateFSM(GameObject.Find("Dreamer Plaque Inspect"), "Conversation Control")
                                                .GetState("Hero Anim");
                dreamerPlaqueInspect.RemoveActionsOfType <ActivateGameObject>();
                dreamerPlaqueInspect.RemoveTransitionsTo("Fade Up");
                dreamerPlaqueInspect.AddTransition("FINISHED", "Map Msg?");

                PlayMakerFSM dreamerScene2 = FSMUtility.LocateFSM(GameObject.Find("Dreamer Scene 2"), "Control");
                dreamerScene2.GetState("Take Control").RemoveTransitionsTo("Blast");
                dreamerScene2.GetState("Take Control").AddTransition("FINISHED", "Fade Out");
                dreamerScene2.GetState("Fade Out").RemoveTransitionsTo("Dial Wait");
                dreamerScene2.GetState("Fade Out").AddTransition("FINISHED", "Set Compass Point");
                break;
            }
        }
예제 #4
0
        public void Update()
        {
            if (done)
            {
                return;
            }

            // skip it if they aren't even at tier 4 grimmchild yet. or if they're especially dumb and
            // banished grimm
            if (!PlayerData.instance.GetBoolInternal("killedNightmareGrimm"))
            {
                done = true;
                return;
            }


            grimmchild = GameObject.FindGameObjectWithTag("Grimmchild");
            if (grimmchild == null)
            {
                return;
            }

            gcFSM = FSMUtility.LocateFSM(grimmchild, "Control");
            if (baseFireInterval < 0.0)
            {
                Log("Set default values for speeds");
                setDefaultGCValues();
            }

            FsmState followState = gcFSM.GetState("Follow");

            FloatCompare[] followCompare = followState.GetActionsOfType <FloatCompare>();
            //followCompare[0].float2 = 1.0f;

            followCompare[1].float2 = .5f;

            followState.RemoveActionsOfType <Wait>();

            FsmState anticAttack = gcFSM.GetState("Antic");

            RandomFloat[] anticRand = anticAttack.GetActionsOfType <RandomFloat>();

            anticRand[0].max = (float)(baseFireInterval / speedModifier);
            anticRand[0].min = (float)(baseFireInterval / speedModifier);

            FsmState noTarget = gcFSM.GetState("No Target");

            SetFloatValue[] noTargetWait = noTarget.GetActionsOfType <SetFloatValue>();
            noTargetWait[0].floatValue = (float)(baseFireInterval / (speedModifier * 2));

            gcFSM.FsmVariables.GetFsmFloat("Flameball Speed").Value = (float)(baseFBSpeed * FBSpeedModifier);
            grimmchild.FindGameObjectInChildren("Enemy Range").GetComponent <CircleCollider2D>().radius = (float)(baseRange * rangeModifier);



            tk2dSprite grimmSprite = grimmchild.GetComponent <tk2dSprite>();
            Color      grimmColor  = grimmSprite.color;

            grimmColor.a      = filterAlpha;
            grimmColor.b      = filterBlue;
            grimmColor.g      = filterGreen;
            grimmColor.r      = filterRed;
            grimmSprite.color = grimmColor;



            FsmState shootYouFool = gcFSM.GetState("Shoot");

            GrimmballFireReal.grimmchild = grimmchild;
            GrimmballFireReal.shootState = shootYouFool;
            GrimmballFireReal.ballSize   = ballSize;
            GrimmballFireReal.damage     = maxDamage;
            GrimmballFireReal.ghostBalls = ghostBall;


            CallMethod[] currentMethods = shootYouFool.GetActionsOfType <CallMethod>();

            if (currentMethods.Length == 0)
            {
                SpawnObjectFromGlobalPool[] spawnObjs = shootYouFool.GetActionsOfType <SpawnObjectFromGlobalPool>();
                try
                {
                    GrimmballFireReal.deadShootSpawner = spawnObjs[0];
                    shootYouFool.RemoveActionsOfType <SpawnObjectFromGlobalPool>();
                }
                catch
                {
                    Log("Not removing shoot spawner, probs because it's length is: " + spawnObjs.Length);
                }


                CallMethod newDankShootMethod = new CallMethod {
                };
                try
                {
                    newDankShootMethod.behaviour  = GameManager.instance.gameObject.GetComponent <GrimmballFireReal>();
                    newDankShootMethod.methodName = "GrimmballUpdater";
                    newDankShootMethod.parameters = new FsmVar[0];
                    newDankShootMethod.everyFrame = false;
                } catch (Exception e)
                {
                    Log("Unable to add method: error " + e);
                }
                Log("Made custom call method");
                shootYouFool.AddAction(newDankShootMethod);

                FireAtTarget currentFAT = shootYouFool.GetActionsOfType <FireAtTarget>()[0];
                shootYouFool.RemoveActionsOfType <FireAtTarget>();
                GrimmballFireReal.oldAttack = currentFAT;

                currentFAT.spread = 0.0f;


                GetChild currentGetChild = shootYouFool.GetActionsOfType <GetChild>()[0];
                shootYouFool.RemoveActionsOfType <GetChild>();
                currentGetChild.childName = "Grimmball(Clone)";

                SetFsmInt currentSetFSMInt = shootYouFool.GetActionsOfType <SetFsmInt>()[0];
                shootYouFool.RemoveActionsOfType <SetFsmInt>();

                // Reorder these things to make sense
                shootYouFool.AddAction(currentGetChild);
                shootYouFool.AddAction(currentSetFSMInt);
                shootYouFool.AddAction(currentFAT);

                GameObject gcRangeObj = grimmchild.FindGameObjectInChildren("Enemy Range");
                //GrimmEnemyRange rangeDelete = gcRangeObj.GetComponent<GrimmEnemyRange>();
                //Destroy(rangeDelete);
                gcRangeObj.AddComponent <GrimmballFireReal>();

                FsmState         targetScan  = gcFSM.GetState("Check For Target");
                CallMethodProper rangeDetect = targetScan.GetActionsOfType <CallMethodProper>()[0];
                rangeDetect.methodName.Value = "GetTarget";
                rangeDetect.behaviour.Value  = "GrimmballFireReal";

                setVolumeLevels();
            }

            gcFSM.SetState("Init");

            //grimmchild.PrintSceneHierarchyTree("modgrimmchild.txt");

            done = true;
        }
        /*
         * Room changes required for the randomizer to function on any mode
         * For example, removing certain vanilla item locations which can't be handled by RandomizerAction
         */

        public static void ApplyRandomizerChanges(Scene newScene)
        {
            switch (newScene.name)
            {
            // Prevent Grimm encounter which gives Grimmchild
            case SceneNames.Grimm_Main_Tent:
                PlayerData.instance.metGrimm = true;
                break;

            // Prevent reading focus tablet when focus is randomized
            case SceneNames.Tutorial_01 when RandomizerMod.Instance.Settings.Cursed:
                GameObject.Find("Tut_tablet_top").LocateMyFSM("Inspection").GetState("Init").ClearTransitions();
                break;

            // Prevent reading tablet which gives completion percentage
            case SceneNames.Room_Final_Boss_Atrium:
                GameObject.Find("Tut_tablet_top").LocateMyFSM("Inspection").GetState("Init").ClearTransitions();
                break;

            // Removes the prompt to donate to the 3000 geo fountain in Basin
            case SceneNames.Abyss_04:
                Object.Destroy(GameObject.Find("Fountain Donation"));
                break;

            // Opens lifeblood door in Abyss with any amount of blue health
            case SceneNames.Abyss_06_Core:
                if (PlayerData.instance.healthBlue > 0 || PlayerData.instance.joniHealthBlue > 0 || GameManager.instance.entryGateName == "left1")
                {
                    PlayerData.instance.SetBoolInternal("blueVineDoor", true);
                    PlayMakerFSM BlueDoorFSM = GameObject.Find("Blue Door").LocateMyFSM("Control");
                    BlueDoorFSM.GetState("Init").RemoveTransitionsTo("Got Charm");
                }
                break;

            // Removes trigger for Void Heart sequence
            case SceneNames.Abyss_15:
                GameObject.Find("Dream Enter Abyss").LocateMyFSM("Control").GetState("Init").RemoveTransitionsTo("Idle");
                GameObject.Find("Dream Enter Abyss").LocateMyFSM("Control").GetState("Init").AddTransition("FINISHED", "Inactive");
                break;

            // Automatically unlock Godseeker and add an action to the Godtuner spot to remove simple key on purchase
            case SceneNames.GG_Waterways:
                PlayerData.instance.SetBool("godseekerUnlocked", true);
                if (GameObject.Find("Randomizer Shiny") != null)
                {
                    PlayMakerFSM godtuner = GameObject.Find("Randomizer Shiny").LocateMyFSM("Shiny Control");
                    godtuner.GetState(godtuner.GetState("Charm?").Transitions.First(t => t.EventName == "YES").ToState).AddFirstAction(new RandomizerExecuteLambda(() => PlayerData.instance.DecrementInt("simpleKeys")));
                }
                break;

            // Spawns mawlek shard out of bounds and moves it inbounds when mawlek is killed
            case SceneNames.Crossroads_09:
                if (GameObject.Find("Randomizer Shiny") is GameObject mawlekShard)
                {
                    mawlekShard.transform.SetPositionY(100f);
                    IEnumerator mawlekDead()
                    {
                        yield return(new WaitUntil(() => PlayerData.instance.killedMawlek));

                        mawlekShard.transform.SetPositionY(10f);
                        mawlekShard.transform.SetPositionX(61.5f);
                    }

                    GameManager.instance.StartCoroutine(mawlekDead());
                }
                break;

            // Removes Grubfather rewards corresponding to randomizer items
            case SceneNames.Crossroads_38:
                Object.Destroy(GameObject.Find("Reward 5"));      //Mask
                Object.Destroy(GameObject.Find("Reward 10"));     //Charm
                Object.Destroy(GameObject.Find("Reward 16"));     //Rancid Egg
                Object.Destroy(GameObject.Find("Reward 23"));     //Relic
                Object.Destroy(GameObject.Find("Reward 31"));     //Pale Ore
                Object.Destroy(GameObject.Find("Reward 38"));     //Relic
                Object.Destroy(GameObject.Find("Reward 46"));     //Charm
                break;

            // Break the Goam journal entry dive floor if the player has dive and rooms are randomized to prevent soul-based locks
            case SceneNames.Crossroads_52:
                if (RandomizerMod.Instance.Settings.RandomizeRooms && Ref.PD.quakeLevel > 0)
                {
                    GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                    {
                        sceneName      = "Crossroads_52",
                        id             = "Quake Floor",
                        activated      = true,
                        semiPersistent = false
                    });
                }
                break;

            // Remove gate from Ancestral Mound
            case SceneNames.Crossroads_ShamanTemple:
                Object.Destroy(GameObject.Find("Bone Gate"));
                break;

            // Remove Beast's Den hardsave, allow rear access from entrance, destroy Herrah
            case SceneNames.Deepnest_Spider_Town:
                GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                {
                    sceneName      = "Deepnest_Spider_Town",
                    id             = "Collapser Small (12)",
                    activated      = true,
                    semiPersistent = false
                });

                FsmState denHardSave = GameObject.Find("RestBench Spider").LocateMyFSM("Fade").GetState("Land");
                denHardSave.RemoveActionsOfType <CallMethodProper>();
                denHardSave.RemoveActionsOfType <SendMessage>();
                denHardSave.RemoveActionsOfType <SetPlayerDataBool>();

                Object.Destroy(GameObject.Find("Dreamer Hegemol"));
                Object.Destroy(GameObject.Find("Dream Enter"));
                Object.Destroy(GameObject.Find("Dream Impact"));
                Object.Destroy(GameObject.Find("Shield"));
                break;

            // Break the second Oro dive floor if the player has dive and both transitions AND soul totems are randomized to prevent soul-based locks
            case SceneNames.Deepnest_East_14:
                if (RandomizerMod.Instance.Settings.RandomizeSoulTotems && RandomizerMod.Instance.Settings.RandomizeTransitions &&
                    Ref.PD.quakeLevel > 0 && GameManager.instance.entryGateName == "top2")
                {
                    GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                    {
                        sceneName      = "Deepnest_East_14",
                        id             = "Quake Floor (1)",
                        activated      = true,
                        semiPersistent = false
                    });
                }
                break;

            // Break the first two dive floors on the way to the 420 geo rock if the player has dive and rooms are randomized to prevent soul-based locks
            case SceneNames.Deepnest_East_17:
                if (RandomizerMod.Instance.Settings.RandomizeRooms && Ref.PD.quakeLevel > 0)
                {
                    GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                    {
                        sceneName      = "Deepnest_East_17",
                        id             = "Quake Floor",
                        activated      = true,
                        semiPersistent = false
                    });
                    GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                    {
                        sceneName      = "Deepnest_East_17",
                        id             = "Quake Floor (1)",
                        activated      = true,
                        semiPersistent = false
                    });
                }
                break;

            // Edits Dream Nail location to change scene to seer
            case SceneNames.Dream_Nailcollection:
                FSMUtility.LocateFSM(GameObject.Find("Randomizer Shiny"), "Shiny Control").GetState("Finish")
                .AddAction(new RandomizerChangeScene("RestingGrounds_07", "right1"));
                break;

            // Edit Hornet room to open gates after boss fight, and removes dreamer cutscene
            case SceneNames.Fungus1_04:
                foreach (PlayMakerFSM childFSM in GameObject.Find("Cloak Corpse")
                         .GetComponentsInChildren <PlayMakerFSM>(true))
                {
                    if (childFSM.FsmName == "Shiny Control")
                    {
                        SendEvent openGate = new SendEvent
                        {
                            eventTarget = new FsmEventTarget
                            {
                                target      = FsmEventTarget.EventTarget.BroadcastAll,
                                excludeSelf = true
                            },
                            sendEvent  = FsmEvent.FindEvent("BG OPEN"),
                            delay      = 0,
                            everyFrame = false
                        };
                        childFSM.GetState("Destroy").AddFirstAction(openGate);
                        childFSM.GetState("Finish").AddFirstAction(openGate);

                        break;
                    }
                }

                ObjectDestroyer.Destroy("Dreamer Scene 1");
                ObjectDestroyer.Destroy("Hornet Saver");
                ObjectDestroyer.Destroy("Cutscene Dreamer");
                ObjectDestroyer.Destroy("Dream Scene Activate");

                if (!Ref.PD.hornet1Defeated)
                {
                    Object.Destroy(FSMUtility.LocateFSM(GameObject.Find("Camera Locks Boss"), "FSM"));
                }
                break;

            // Make city crest gate openable infinite times and not hard save
            // Break the dive floor if transitions are randomized and the player has dive to prevent soul-based locks
            case SceneNames.Fungus2_21:
                FSMUtility.LocateFSM(GameObject.Find("City Gate Control"), "Conversation Control")
                .GetState("Activate").RemoveActionsOfType <SetPlayerDataBool>();

                FsmState gateSlam = FSMUtility.LocateFSM(GameObject.Find("Ruins_gate_main"), "Open")
                                    .GetState("Slam");
                gateSlam.RemoveActionsOfType <SetPlayerDataBool>();
                gateSlam.RemoveActionsOfType <CallMethodProper>();
                gateSlam.RemoveActionsOfType <SendMessage>();

                if (RandomizerMod.Instance.Settings.RandomizeTransitions && Ref.PD.quakeLevel > 0 && GameManager.instance.entryGateName == "right1")
                {
                    GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                    {
                        sceneName      = "Fungus2_21",
                        id             = "Quake Floor",
                        activated      = true,
                        semiPersistent = false
                    });
                }
                break;

            // Removes Leg Eater dialogue tree, preventing him from dying
            case SceneNames.Fungus2_26:
                PlayMakerFSM legEater       = FSMUtility.LocateFSM(GameObject.Find("Leg Eater"), "Conversation Control");
                FsmState     legEaterChoice = legEater.GetState("Convo Choice");
                legEaterChoice.RemoveTransitionsTo("Convo 1");
                legEaterChoice.RemoveTransitionsTo("Convo 2");
                legEaterChoice.RemoveTransitionsTo("Convo 3");
                legEaterChoice.RemoveTransitionsTo("Infected Crossroad");
                legEaterChoice.RemoveTransitionsTo("Bought Charm");
                legEaterChoice.RemoveTransitionsTo("Gold Convo");
                legEaterChoice.RemoveTransitionsTo("All Gold");
                legEaterChoice.RemoveTransitionsTo("Ready To Leave");
                legEater.GetState("All Gold?").RemoveTransitionsTo("No Shop");
                Ref.PD.legEaterLeft = false;
                break;

            // Destroy Monomon and remove Quirrel encounter
            case SceneNames.Fungus3_archive_02:
                PlayerData.instance.SetBool("summonedMonomon", true);
                Object.Destroy(GameObject.Find("Quirrel Wounded"));
                Object.Destroy(GameObject.Find("Quirrel"));
                Object.Destroy(GameObject.Find("Monomon"));
                Object.Destroy(GameObject.Find("Dream Enter"));
                Object.Destroy(GameObject.Find("Dream Impact"));
                Object.Destroy(GameObject.Find("Shield"));
                break;

            // Destroys original lurker key. Moves new shiny out of bounds if lurker is alive and moves it inbounds when lurker is killed
            case "GG_Lurker":
                if (PlayerData.instance.killedPaleLurker)
                {
                    Object.Destroy(GameObject.Find("Shiny Item Key"));
                }
                else
                {
                    GameObject.Find("New Shiny").transform.SetPositionY(200f);
                    IEnumerator LurkerKilled()
                    {
                        yield return(new WaitUntil(() => PlayerData.instance.killedPaleLurker || GameManager.instance.sceneName != "GG_Lurker"));

                        yield return(new WaitUntil(() => GameObject.Find("Shiny Item Key") is GameObject lurkerKey || GameManager.instance.sceneName != "GG_Lurker"));

                        if (GameManager.instance.sceneName == "GG_Lurker")
                        {
                            Object.Destroy(GameObject.Find("Shiny Item Key"));
                            GameObject lurkerCorpse = Object.FindObjectsOfType <GameObject>().First(obj => obj.name.StartsWith("Corpse Pale Lurker"));    // Corpse Pale Lurker(Clone)
                            GameObject.Find("New Shiny").transform.SetPosition2D(lurkerCorpse.transform.position);
                        }
                    }

                    GameManager.instance.StartCoroutine(LurkerKilled());
                }
                break;

            case SceneNames.Hive_03 when RandomizerMod.Instance.Settings.StartName == "Hive":
                GameObject hivePlatform = ObjectCache.SmallPlatform;
                hivePlatform.transform.SetPosition2D(58.5f, 134f);
                hivePlatform.SetActive(true);
                break;

            // Platforms for open mode
            case SceneNames.Fungus1_13 when RandomizerMod.Instance.Settings.StartName == "Far Greenpath":
            {
                GameObject leftGPQGplat = ObjectCache.SmallPlatform;
                leftGPQGplat.transform.SetPosition2D(45f, 16.5f);
                leftGPQGplat.SetActive(true);
                GameObject rightGPQGplat = ObjectCache.SmallPlatform;
                rightGPQGplat.transform.SetPosition2D(64f, 16.5f);
                rightGPQGplat.SetActive(true);
            }
                GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                {
                    sceneName      = "Fungus1_13",
                    id             = "Vine Platform (1)",
                    activated      = true,
                    semiPersistent = false
                });
                GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                {
                    sceneName      = "Fungus1_13",
                    id             = "Vine Platform (2)",
                    activated      = true,
                    semiPersistent = false
                });
                break;

            // Bounce shrooms to prevent softlock for Fungal Core start in open mode without claw
            case SceneNames.Fungus2_30:
            {
                GameObject bounceShroom = GameObject.Find("Bounce Shroom C");

                GameObject s0 = Object.Instantiate(bounceShroom);
                s0.transform.SetPosition3D(12.5f, 26f, 0f);
                s0.SetActive(true);

                GameObject s1 = Object.Instantiate(bounceShroom);
                s1.transform.SetPosition3D(12.5f, 54f, 0f);
                s1.SetActive(true);

                GameObject s2 = Object.Instantiate(bounceShroom);
                s2.transform.SetPosition3D(21.7f, 133f, 0f);
                s2.SetActive(true);
            }
            break;

            // Break the Peak entrance dive floor if the player has dive and transitions are randomized to prevent soul-based locks
            case SceneNames.Mines_01:
                if (RandomizerMod.Instance.Settings.RandomizeTransitions && Ref.PD.quakeLevel > 0 && GameManager.instance.entryGateName == "left1")
                {
                    GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                    {
                        sceneName      = "Mines_01",
                        id             = "mine_1_quake_floor",
                        activated      = true,
                        semiPersistent = false
                    });
                }
                break;

            // Make tolls always interactable, in the rare case that lantern is not randomized but RG access through the dark room is expected
            case SceneNames.Mines_33:
                if (RandomizerMod.Instance.Settings.DarkRooms && !RandomizerMod.Instance.Settings.RandomizeKeys)
                {
                    GameObject[] tolls = new GameObject[] { GameObject.Find("Toll Gate Machine"), GameObject.Find("Toll Gate Machine (1)") };
                    foreach (GameObject toll in tolls)
                    {
                        Object.Destroy(FSMUtility.LocateFSM(toll, "Disable if No Lantern"));
                    }
                }
                break;

            // Break the Crystallized Mound dive floor if the player has dive and transitions or soul totems are randomized to prevent soul-based locks
            case SceneNames.Mines_35:
                if ((RandomizerMod.Instance.Settings.RandomizeTransitions || RandomizerMod.Instance.Settings.RandomizeSoulTotems) && Ref.PD.quakeLevel > 0)
                {
                    GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                    {
                        sceneName      = "Mines_35",
                        id             = "mine_1_quake_floor",
                        activated      = true,
                        semiPersistent = false
                    });
                }
                break;

            // Break the Crypts dive floor if the player has dive and soul totems are randomized to prevent soul-based locks
            case SceneNames.RestingGrounds_05:
                if (RandomizerMod.Instance.Settings.RandomizeSoulTotems && Ref.PD.quakeLevel > 0)
                {
                    GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                    {
                        sceneName      = "RestingGrounds_05",
                        id             = "Quake Floor",
                        activated      = true,
                        semiPersistent = false
                    });
                }
                break;

            // Move Seer back to make room for items, and remove essence rewards
            case SceneNames.RestingGrounds_07:
                GameObject.Find("Dream Moth").transform.Translate(new Vector3(-5f, 0f));

                PlayMakerFSM moth = FSMUtility.LocateFSM(GameObject.Find("Dream Moth"), "Conversation Control");

                PlayerData.instance.dreamReward1 = true;
                moth.FsmVariables.GetFsmBool("Got Reward 1").Value = true;      //Relic
                PlayerData.instance.dreamReward3 = true;
                moth.FsmVariables.GetFsmBool("Got Reward 3").Value = true;      //Pale Ore
                PlayerData.instance.dreamReward4 = true;
                moth.FsmVariables.GetFsmBool("Got Reward 4").Value = true;      //Charm
                PlayerData.instance.dreamReward5 = true;
                moth.FsmVariables.GetFsmBool("Got Reward 5").Value = true;      //Vessel Fragment
                PlayerData.instance.dreamReward5b = true;
                moth.FsmVariables.GetFsmBool("Got Reward 5b").Value = true;     //Skill
                PlayerData.instance.dreamReward6 = true;
                moth.FsmVariables.GetFsmBool("Got Reward 6").Value = true;      //Relic
                PlayerData.instance.dreamReward7 = true;
                moth.FsmVariables.GetFsmBool("Got Reward 7").Value = true;      //Mask Shard
                PlayerData.instance.dreamReward8 = true;
                moth.FsmVariables.GetFsmBool("Got Reward 8").Value = true;      //Skill
                break;

            // Make Sly pickup send Sly back upstairs -- warps player out to prevent resulting softlock from trying to enter the shop from a missing transition
            case SceneNames.Room_Sly_Storeroom:
                FsmState slyFinish = FSMUtility.LocateFSM(GameObject.Find("Randomizer Shiny"), "Shiny Control").GetState("Finish");
                slyFinish.AddAction(new RandomizerSetBool("SlyCharm", true));
                slyFinish.AddAction(new RandomizerChangeScene("Town", "door_sly"));
                break;

            case SceneNames.Ruins1_05 + "c":
                GameObject platform = ObjectCache.SmallPlatform;
                platform.transform.SetPosition2D(26.6f, 73.2f);
                platform.SetActive(true);
                break;

            // Many changes to make the desolate dive pickup work properly
            case SceneNames.Ruins1_24:
                // Stop spell container from destroying itself
                PlayMakerFSM quakePickup = FSMUtility.LocateFSM(GameObject.Find("Quake Pickup"), "Pickup");
                quakePickup.GetState("Idle").RemoveActionsOfType <IntCompare>();
                foreach (PlayMakerFSM childFSM in quakePickup.gameObject.GetComponentsInChildren <PlayMakerFSM>(true))
                {
                    if (childFSM.FsmName == "Shiny Control")
                    {
                        // Make spell container spawn shiny instead
                        quakePickup.GetState("Appear").GetActionsOfType <ActivateGameObject>()[1].gameObject
                        .GameObject.Value = childFSM.gameObject;

                        // Make shiny open gates on pickup/destroy
                        SendEvent openGate = new SendEvent
                        {
                            eventTarget = new FsmEventTarget
                            {
                                target      = FsmEventTarget.EventTarget.BroadcastAll,
                                excludeSelf = true
                            },
                            sendEvent  = FsmEvent.FindEvent("BG OPEN"),
                            delay      = 0,
                            everyFrame = false
                        };
                        childFSM.GetState("Destroy").AddFirstAction(openGate);
                        childFSM.GetState("Finish").AddFirstAction(openGate);
                        break;
                    }
                }

                // Stop the weird invisible floor from appearing if dive has been obtained
                if (Ref.PD.quakeLevel > 0)
                {
                    Object.Destroy(GameObject.Find("Roof Collider Battle"));
                }

                // Change battle gate to be destroyed if Soul Master is dead instead of it the player has quake
                FsmState checkQuake = FSMUtility.LocateFSM(GameObject.Find("Battle Gate (1)"), "Destroy if Quake").GetState("Check");
                checkQuake.RemoveActionsOfType <FsmStateAction>();
                checkQuake.AddAction(new RandomizerBoolTest(nameof(PlayerData.killedMageLord), null, "DESTROY", true));
                break;

            // Prevent simple key softlocks
            case SceneNames.Ruins2_04:
                FsmState hotSpringsKey = GameObject.Find("Inspect").LocateMyFSM("Conversation Control").GetState("Got Key?");
                hotSpringsKey.RemoveActionsOfType <IntCompare>();
                hotSpringsKey.AddAction(new RandomizerExecuteLambda(() =>
                {
                    if (GameManager.instance.GetPlayerDataInt("simpleKeys") > 1 || (PlayerData.instance.openedWaterwaysManhole && GameManager.instance.GetPlayerDataInt("simpleKeys") > 0))
                    {
                        PlayMakerFSM.BroadcastEvent("YES");
                    }
                    else
                    {
                        PlayMakerFSM.BroadcastEvent("NO");
                    }
                }));
                break;

            // Destroy Lurien
            case SceneNames.Ruins2_Watcher_Room:
                Object.Destroy(GameObject.Find("Dreamer Lurien"));
                Object.Destroy(GameObject.Find("Dream Enter"));
                Object.Destroy(GameObject.Find("Dream Impact"));
                Object.Destroy(GameObject.Find("Shield"));
                break;

            // Open all colosseum trials
            case SceneNames.Room_Colosseum_01:
                PlayerData.instance.colosseumBronzeOpened = true;
                PlayerData.instance.colosseumSilverOpened = true;
                PlayerData.instance.colosseumGoldOpened   = true;
                GameObject.Find("Silver Trial Board").LocateMyFSM("Conversation Control").GetState("Hero Anim").ClearTransitions();
                GameObject.Find("Silver Trial Board").LocateMyFSM("Conversation Control").GetState("Hero Anim").AddTransition("FINISHED", "Box Up YN");
                GameObject.Find("Gold Trial Board").LocateMyFSM("Conversation Control").GetState("Hero Anim").ClearTransitions();
                GameObject.Find("Gold Trial Board").LocateMyFSM("Conversation Control").GetState("Hero Anim").AddTransition("FINISHED", "Box Up YN");
                break;

            // Destroy Grey Mourner after the flower has been delivered
            case SceneNames.Room_Mansion:
                if (PlayerData.instance.xunFlowerGiven)
                {
                    PlayerData.instance.xunRewardGiven = true;
                }
                break;

            // Removes King's Brand cutscene trigger
            case SceneNames.Room_Wyrm:
                Object.Destroy(GameObject.Find("Avalanche End"));
                break;

            // Open Colosseum gates after picking up resp. items
            case SceneNames.Room_Colosseum_Bronze:
                GameObject.Find("Colosseum Manager").LocateMyFSM("Geo Pool").GetState("Open Gates").AddFirstAction(new RandomizerSetBool("colosseumBronzeCompleted", true, true));
                break;

            case SceneNames.Room_Colosseum_Silver:
                GameObject.Find("Colosseum Manager").LocateMyFSM("Geo Pool").GetState("Open Gates").AddFirstAction(new RandomizerSetBool("colosseumSilverCompleted", true, true));
                break;

            // Prevent simple key softlocks
            case SceneNames.Town:
                FsmState jijiKey = GameObject.Find("Jiji Door").LocateMyFSM("Conversation Control").GetState("Key?");
                jijiKey.RemoveActionsOfType <GetPlayerDataInt>();
                jijiKey.RemoveActionsOfType <IntCompare>();
                jijiKey.AddAction(new RandomizerExecuteLambda(() =>
                {
                    if (GameManager.instance.GetPlayerDataInt("simpleKeys") > 1 || (PlayerData.instance.openedWaterwaysManhole && GameManager.instance.GetPlayerDataInt("simpleKeys") > 0))
                    {
                        PlayMakerFSM.BroadcastEvent("KEY");
                    }
                    else
                    {
                        PlayMakerFSM.BroadcastEvent("NOKEY");
                    }
                }));
                break;

            // Break the Dung Defender dive floor if the player has dive and transitions are randomized to prevent soul-based locks
            case SceneNames.Waterways_05:
                if (RandomizerMod.Instance.Settings.RandomizeTransitions && Ref.PD.quakeLevel > 0)
                {
                    GameManager.instance.sceneData.SaveMyState(new PersistentBoolData
                    {
                        sceneName      = "Waterways_05",
                        id             = "Quake Floor",
                        activated      = true,
                        semiPersistent = false
                    });
                }
                break;
            }
        }
        public override void Process(string scene, Object changeObj)
        {
            if (scene != _sceneName || !(changeObj is PlayMakerFSM fsm) || fsm.FsmName != _fsmName ||
                fsm.gameObject.name != _objectName)
            {
                return;
            }

            FsmState pdBool      = fsm.GetState("PD Bool?");
            FsmState charm       = fsm.GetState("Charm?");
            FsmState bigGetFlash = fsm.GetState("Big Get Flash");

            // Remove actions that stop shiny from spawning
            pdBool.RemoveActionsOfType <StringCompare>();

            // Change pd bool test to our new bool
            PlayerDataBoolTest boolTest = pdBool.GetActionsOfType <PlayerDataBoolTest>()[0];

            if (_playerdata)
            {
                boolTest.boolName = _boolName;
            }
            else
            {
                RandomizerBoolTest randBoolTest = new RandomizerBoolTest(_boolName, boolTest.isFalse, boolTest.isTrue);
                pdBool.RemoveActionsOfType <PlayerDataBoolTest>();
                pdBool.AddFirstAction(randBoolTest);
            }

            string logBoolName = string.Empty;

            for (int i = 0; i < _itemDefs.Length; i++)
            {
                if (!Ref.PD.GetBool(_itemDefs[i].BoolName))
                {
                    logBoolName = _itemDefs[i].BoolName;
                    break;
                }
            }

            // Force the FSM to show the big item flash
            charm.ClearTransitions();
            charm.AddTransition("FINISHED", "Big Get Flash");
            bigGetFlash.AddAction(new RandomizerExecuteLambda(() => RandoLogger.LogItemToTrackerByBoolName(logBoolName, _location)));
            bigGetFlash.AddAction(new RandomizerExecuteLambda(() => RandoLogger.UpdateHelperLog()));
            bigGetFlash.AddFirstAction(new RandomizerExecuteLambda(() => AreaRando.Instance.Settings.UpdateObtainedProgressionByBoolName(logBoolName)));

            // Set bool and show the popup after the flash
            bigGetFlash.AddAction(new RandomizerCallStaticMethod(
                                      typeof(BigItemPopup),
                                      nameof(BigItemPopup.ShowAdditive),
                                      _itemDefs,
                                      fsm.gameObject,
                                      "GET ITEM MSG END"));

            // Don't actually need to set the skill here, that happens in BigItemPopup
            // Maybe change that at some point, it's not where it should happen
            if (!_playerdata)
            {
                bigGetFlash.AddAction(new RandomizerSetBool(_boolName, true));
            }

            // Exit the fsm after the popup
            bigGetFlash.ClearTransitions();
            bigGetFlash.AddTransition("GET ITEM MSG END", "Hero Up");
            bigGetFlash.AddTransition("HERO DAMAGED", "Finish");
        }