diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp index 45d7e372b..05253e5b2 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp @@ -140,18 +140,25 @@ void RegionTable_Init_ForestTemple() { //Events EventAccess(LOGIC_STICK_ACCESS, []{return logic->CanGetDekuBabaSticks();}), EventAccess(LOGIC_NUT_ACCESS, []{return logic->CanGetDekuBabaNuts();}), - EventAccess(LOGIC_FOREST_SUMMON_NE_SCARECROW, []{return logic->CanUse(RG_HOVER_BOOTS) && ((ctx->GetTrickOption(RT_FOREST_DOORFRAME) && logic->CanJumpslash()) || (ctx->GetTrickOption(RT_HOVER_BOOST_SIMPLE) && logic->CanUse(RG_MEGATON_HAMMER))) && logic->ReachScarecrow();}), EventAccess(LOGIC_FOREST_DRAINED_WELL, []{return true;}), }, {}, { //Exits - Entrance(RR_FOREST_TEMPLE_NE_COURTYARD_LOWER, []{return true;}), - Entrance(RR_FOREST_TEMPLE_NE_COURTYARD_ISLAND, []{return logic->IsAdult && ctx->GetTrickOption(RT_FOREST_COURTYARD_LEDGE) && logic->CanUse(RG_HOVER_BOOTS);}), - Entrance(RR_FOREST_TEMPLE_MAP_ROOM, []{return true;}), + Entrance(RR_FOREST_TEMPLE_NE_COURTYARD_LOWER, []{return true;}), + Entrance(RR_FOREST_TEMPLE_NE_COURTYARD_ISLAND, []{return logic->IsAdult && ctx->GetTrickOption(RT_FOREST_COURTYARD_LEDGE) && logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_FOREST_TEMPLE_NE_COURTYARD_DOORFRAME, []{return logic->CanHammerRecoilHover() || ((ctx->GetTrickOption(RT_FOREST_DOORFRAME) && logic->CanUse(RG_HOVER_BOOTS) && logic->CanJumpslash()));}), + Entrance(RR_FOREST_TEMPLE_MAP_ROOM, []{return true;}), }); - areaTable[RR_FOREST_TEMPLE_NE_COURTYARD_ISLAND] = Region("Forest Temple NE Courtyard Island", SCENE_FOREST_TEMPLE, { + areaTable[RR_FOREST_TEMPLE_NE_COURTYARD_DOORFRAME] = Region("Forest Temple NE Courtyard Doorframe", SCENE_FOREST_TEMPLE, { + //Events EventAccess(LOGIC_FOREST_SUMMON_NE_SCARECROW, []{return logic->ScarecrowsSong();}), - }, { + }, {}, { + //Exits + Entrance(RR_FOREST_TEMPLE_NE_COURTYARD_LOWER, []{return true;}), + Entrance(RR_FOREST_TEMPLE_NE_COURTYARD_ISLAND, []{return logic->CanHammerRecoilHover();}), + }); + + areaTable[RR_FOREST_TEMPLE_NE_COURTYARD_ISLAND] = Region("Forest Temple NE Courtyard Island", SCENE_FOREST_TEMPLE, {}, { //Locations LOCATION(RC_FOREST_TEMPLE_RAISED_ISLAND_COURTYARD_CHEST, true), }, { @@ -160,9 +167,12 @@ void RegionTable_Init_ForestTemple() { Entrance(RR_FOREST_TEMPLE_NE_COURTYARD_SCARECROW_LEDGE, []{return logic->Get(LOGIC_FOREST_SUMMON_NE_SCARECROW) && logic->CanUse(RG_HOOKSHOT);}), }); - areaTable[RR_FOREST_TEMPLE_NE_COURTYARD_SCARECROW_LEDGE] = Region("Forest Temple NE Courtyard Scarecrow Ledge", SCENE_FOREST_TEMPLE, {}, { + areaTable[RR_FOREST_TEMPLE_NE_COURTYARD_SCARECROW_LEDGE] = Region("Forest Temple NE Courtyard Scarecrow Ledge", SCENE_FOREST_TEMPLE, { + //Events + EventAccess(LOGIC_FOREST_SUMMON_NE_SCARECROW, []{return logic->ScarecrowsSong();}), + }, { //Locations - LOCATION(RC_FOREST_TEMPLE_GS_RAISED_ISLAND_COURTYARD, logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_DINS_FIRE) || logic->HasExplosives()), + LOCATION(RC_FOREST_TEMPLE_GS_RAISED_ISLAND_COURTYARD, logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_DINS_FIRE) || logic->HasExplosives()), }, { //Exits Entrance(RR_FOREST_TEMPLE_NE_COURTYARD_ISLAND, []{return true;}), @@ -367,10 +377,10 @@ void RegionTable_Init_ForestTemple() { areaTable[RR_FOREST_TEMPLE_BLUE_DOORMAT_HALLWAY] = Region("Forest Temple Blue Doormat Hallway", SCENE_FOREST_TEMPLE, {}, {}, { //Exits Entrance(RR_FOREST_TEMPLE_BLUE_DOORMAT_HALLWAY_DOORMAT, []{return logic->CanPassEnemy(RE_BIG_SKULLTULA);}), - Entrance(RR_FOREST_TEMPLE_LOBBY, []{return true;}), + Entrance(RR_FOREST_TEMPLE_LOBBY, []{return true;}), }); - areaTable[RR_FOREST_TEMPLE_BASEMENT] = Region("Forest Temple Boss Region", SCENE_FOREST_TEMPLE, { + areaTable[RR_FOREST_TEMPLE_BASEMENT] = Region("Forest Temple Basement", SCENE_FOREST_TEMPLE, { //Events EventAccess(LOGIC_FOREST_OPEN_BOSS_HALLWAY, []{return true;}), }, { @@ -604,17 +614,29 @@ void RegionTable_Init_ForestTemple() { Entrance(RR_FOREST_TEMPLE_MQ_NE_COURTYARD_ISLAND, []{return logic->CanUse(RG_LONGSHOT);}), }); + areaTable[RR_FOREST_TEMPLE_MQ_NE_COURTYARD_DOORFRAME] = Region("Forest Temple MQ NE Courtyard Doorframe", SCENE_FOREST_TEMPLE, {}, { + //Locations + //Actually killing the skull from the doorframe with melee is annoying. Hammer swing hits low enough unaided, other swords need to crouch stab but the spot is precise based on range. kokiri sword doesn't reach at all for adult. + LOCATION(RC_FOREST_TEMPLE_MQ_GS_RAISED_ISLAND_COURTYARD, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW) || logic->CanUse(RG_MEGATON_HAMMER) || + (logic->CanStandingShield() && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_MASTER_SWORD) || (logic->IsChild && logic->CanUse(RG_KOKIRI_SWORD))))), + }, { + //Exits + Entrance(RR_FOREST_TEMPLE_MQ_NE_COURTYARD, []{return true;}), + Entrance(RR_FOREST_TEMPLE_MQ_NE_COURTYARD_ISLAND, []{return logic->CanHammerRecoilHover();}), + }); + areaTable[RR_FOREST_TEMPLE_MQ_COURTYARD_TOP_LEDGES] = Region("Forest Temple MQ Courtyard Top Ledges", SCENE_FOREST_TEMPLE, {}, { //Locations LOCATION(RC_FOREST_TEMPLE_MQ_RAISED_ISLAND_COURTYARD_UPPER_CHEST, true), - //Actually killing the skull from the doorframe with melee is annoying. Hammer swing hits low enough unaided, other swords need to crouch stab but the spot is precise based on range. kokiri sword doesn't reach at all for adult. - LOCATION(RC_FOREST_TEMPLE_MQ_GS_RAISED_ISLAND_COURTYARD, ((logic->IsAdult && logic->CanUse(RG_SONG_OF_TIME)) || (logic->CanUse(RG_HOVER_BOOTS) && ctx->GetTrickOption(RT_FOREST_DOORFRAME) || (ctx->GetTrickOption(RT_HOVER_BOOST_SIMPLE) && logic->CanUse(RG_MEGATON_HAMMER)))) && logic->CanJumpslash() && (logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->BlastOrSmash() || logic->CanUse(RG_DINS_FIRE) || logic->CanUse(RG_FAIRY_BOW) || logic->HookshotOrBoomerang() || (logic->CanStandingShield() && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_MASTER_SWORD) || (logic->IsChild && logic->CanUse(RG_KOKIRI_SWORD)))))), }, { //Exits - Entrance(RR_FOREST_TEMPLE_MQ_NORTH_PASSAGE, []{return true;}), - Entrance(RR_FOREST_TEMPLE_MQ_NE_COURTYARD, []{return true;}), + Entrance(RR_FOREST_TEMPLE_MQ_NORTH_PASSAGE, []{return true;}), + Entrance(RR_FOREST_TEMPLE_MQ_NE_COURTYARD, []{return true;}), + Entrance(RR_FOREST_TEMPLE_MQ_NE_COURTYARD_DOORFRAME, []{return logic->CanHammerRecoilHover() || + ((ctx->GetTrickOption(RT_FOREST_DOORFRAME) && logic->CanUse(RG_HOVER_BOOTS) && logic->CanJumpslash())) || + (logic->IsChild && (ctx->GetTrickOption(RT_FOREST_MQ_CHILD_DOORFRAME) || logic->CanMiddairGroundJump()));}), //N64 logic doesn't check damage but I always take some so I'm adding it - Entrance(RR_FOREST_TEMPLE_MQ_NE_COURTYARD_ISLAND, []{return ctx->GetTrickOption(RT_FOREST_COURTYARD_LEDGE) && logic->CanUse(RG_HOVER_BOOTS) && logic->CanJumpslash() && logic->TakeDamage();}), + Entrance(RR_FOREST_TEMPLE_MQ_NE_COURTYARD_ISLAND, []{return ctx->GetTrickOption(RT_FOREST_COURTYARD_LEDGE) && logic->CanUse(RG_HOVER_BOOTS) && logic->CanJumpslash() && logic->TakeDamage();}), }); areaTable[RR_FOREST_TEMPLE_MQ_NE_COURTYARD_ISLAND] = Region("Forest Temple MQ NE Courtyard Island", SCENE_FOREST_TEMPLE, {}, { diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index a79d45444..0d3045c98 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -452,6 +452,11 @@ bool Logic::CanGroundJumpJumpSlash(bool hasBombflower) { (CanUse(RG_BOMB_BAG) || (hasBombflower && HasItem(RG_GORONS_BRACELET))); } +bool Logic::CanMiddairGroundJump(bool hasBombflower) { + return ctx->GetTrickOption(RT_GROUND_JUMP_HARD) && CanStandingShield() && CanUse(RG_HOVER_BOOTS) && + (CanUse(RG_BOMB_BAG) || (hasBombflower && HasItem(RG_GORONS_BRACELET))); +} + bool Logic::CanOpenUnderwaterChest() { return ctx->GetTrickOption(RT_OPEN_UNDERWATER_CHEST) && CanUse(RG_IRON_BOOTS) && CanUse(RG_HOOKSHOT); } @@ -1041,6 +1046,11 @@ bool Logic::CanDetonateUprightBombFlower() { (EffectiveHealth() != 1 || CanUse(RG_NAYRUS_LOVE))); } +bool Logic::CanHammerRecoilHover(bool needShield) { + return CanUse(RG_HOVER_BOOTS) && ctx->GetTrickOption(RT_HOVER_BOOST_SIMPLE) && CanUse(RG_MEGATON_HAMMER) && + (!needShield || CanStandingShield()); +} + bool Logic::Water3FCentralToHighEmblem() { return (IsAdult && (CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && CanUse(RG_BOMB_BAG) && TakeDamage()))) || diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index a65fde0bb..d627bd851 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -46,6 +46,7 @@ class Logic { bool SmallKeys(s16 scene, uint8_t requiredAmount); bool CanGroundJump(bool hasBombflower = false); bool CanGroundJumpJumpSlash(bool hasBombflower = false); + bool CanMiddairGroundJump(bool hasBombflower = false); bool CanOpenUnderwaterChest(); bool CanDoGlitch(GlitchType glitch); bool CanEquipSwap(RandomizerGet itemName); @@ -60,6 +61,7 @@ class Logic { bool CanHitEyeTargets(); bool CanDetonateBombFlowers(); bool CanDetonateUprightBombFlower(); + bool CanHammerRecoilHover(bool needShield = false); bool Water3FCentralToHighEmblem(); bool WaterRisingTargetTo3FCentral(); bool WaterLevel(RandoWaterLevel level); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 5427b5e01..693419946 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -891,6 +891,7 @@ typedef enum { RR_FOREST_TEMPLE_NW_COURTYARD_UPPER, RR_FOREST_TEMPLE_NE_COURTYARD_LOWER, RR_FOREST_TEMPLE_NE_COURTYARD_UPPER, + RR_FOREST_TEMPLE_NE_COURTYARD_DOORFRAME, RR_FOREST_TEMPLE_NE_COURTYARD_ISLAND, RR_FOREST_TEMPLE_NE_COURTYARD_SCARECROW_LEDGE, RR_FOREST_TEMPLE_MAP_ROOM, @@ -938,6 +939,7 @@ typedef enum { RR_FOREST_TEMPLE_MQ_NORTH_PASSAGE, RR_FOREST_TEMPLE_MQ_NE_COURTYARD, RR_FOREST_TEMPLE_MQ_COURTYARD_TOP_LEDGES, + RR_FOREST_TEMPLE_MQ_NE_COURTYARD_DOORFRAME, RR_FOREST_TEMPLE_MQ_NE_COURTYARD_ISLAND, RR_FOREST_TEMPLE_MQ_NE_COURTYARD_LEDGE_ABOVE_ISLAND, RR_FOREST_TEMPLE_MQ_JOELLE_ROOM, @@ -4210,6 +4212,7 @@ typedef enum { RT_FOREST_MQ_JS_HALLWAY_SWITCH, RT_FOREST_MQ_HOOKSHOT_HALLWAY_SWITCH, RT_FOREST_MQ_RANG_HALLWAY_SWITCH, + RT_FOREST_MQ_CHILD_DOORFRAME, RT_FIRE_SOT, RT_FIRE_STRENGTH, RT_FIRE_SCARECROW, diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 204c63d0e..e78d0b743 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1798,6 +1798,13 @@ void Settings::CreateOptions() { "The Boomerang can return to Link through walls, allowing child to hit the hallway switch. This can be " "used to allow adult to pass through later, or in conjunction with \"Forest Temple Outside Backdoor with " "Jump Slash\"."); + OPT_TRICK(RT_FOREST_MQ_CHILD_DOORFRAME, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, + "Forest Temple MQ Doorframe GS as Child without Boomerang", + "If Adult burns the courtyard webbing with Fire Arrows (which is a permanent flag in Ship Rando) " + "then Child can climb up to the balconies and jump to the SoT block from the railing, " + "and from there either roll jump or jump against the wall to reach the doorframe.\n" + "From there, The GS can be killed with a crouchstab, explosives or other ranged weapon " + "and collected by climbing down."); // Is also used in MQ logic, but has no practical effect there as of now OPT_TRICK(RT_FIRE_SOT, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "Fire Temple Song of Time Room GS without Song of Time",