From 01800f74c6304977136d187bca0e04a2ac112ec8 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 22 Apr 2025 02:34:59 +0100 Subject: [PATCH] WIP --- .../randomizer/location_access.cpp | 138 ++++- .../Enhancements/randomizer/location_access.h | 110 +--- .../dungeons/spirit_temple.cpp | 538 +++++++++++------- soh/soh/Enhancements/randomizer/logic.cpp | 87 ++- soh/soh/Enhancements/randomizer/logic.h | 19 +- .../Enhancements/randomizer/randomizerTypes.h | 79 +-- soh/soh/Enhancements/randomizer/settings.cpp | 10 +- 7 files changed, 617 insertions(+), 364 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index a12e2f5b6..63147c2da 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -210,33 +210,129 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { return areaTable[region].Here(condition); } -bool SpiritExplosiveLogic() { - return logic->HasExplosives() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; +uint8_t SpiritExplosiveLogic() { + return logic->SpiritBrokenWallToStatue() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; } // RANDOTODO basically every condition here will need climb or longshot. -bool SpiritSharedStatueRoom(ConditionFn condition, bool anyAge) { - return areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM].SpiritShared( - condition, [] { return logic->HasExplosives(); }, [] { return true; }, 5, 3, SpiritExplosiveLogic(), anyAge); -} -bool SpiritSharedSunBlockRoom(ConditionFn condition, bool anyAge) { - return areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE].SpiritShared( - condition, [] { return logic->HasExplosives(); }, [] { return true; }, 5, 3, SpiritExplosiveLogic(), anyAge); -} +// clang-format off +std::map Region::spiritLogicData = { + {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, SpiritLogicData(5, 5, 3, []{return true;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_BROKEN_WALL, SpiritLogicData(5, 5, 3, []{return true /*logic->CanClimbHigh()*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_2F_MIRROR, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();} , []{return true/*logic->CanClimbHigh()*/;} , []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_GS_LEDGE, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue() && logic->SpiritWestToSkull();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic()) && logic->SpiritWestToSkull() + /* && logic->CanClimbHigh() && str0*/;} , []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true;})}, + {RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && str0*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, + {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && str0*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();} , []{return true/*logic->CanClimbHigh() && str0*/;} , []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;} , []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));})}, + {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, SpiritLogicData(7, 6, 7, []{return true;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, SpiritLogicData(7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return true/*logic->Climb*/;} , []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return true;} , []{return true;})}, + {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) + /* && logic->CanClimbHigh()*/;} , []{return false;} , []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});})}, +}; +// clang-format on -bool SpiritSharedBrokenWallRoom(ConditionFn condition, bool anyAge) { - return areaTable[RR_SPIRIT_TEMPLE_CHILD_CLIMB].SpiritShared( - condition, [] { return true; }, [] { return true; }, 5, 3, 1, anyAge); -} -bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].SpiritShared( - condition, [] { return true; }, [] { return true; }, 7, 0, 0, anyAge); -} +/* + * This logic covers checks that exist in the shared areas of Spirit from a glitchless standpoint. + * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on + specific ages -bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].SpiritShared( - condition, [] { return true; }, [] { return true; }, 7, 7, 6, anyAge); + * There are 4 possibilities for passing a check, but first I have to talk about parallel universes. + + * In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out + * In the second, they enter as adult and spend as many keys as they can to lock child out. + + * When an Age can no longer be kept out by the other age, that age is said to have Certain Access to a region + * If both ages have access to a region with a certain number of keys, but there is no Certain Access, + * then a check is only in logic if both ages can collect the check independently + + * If an age has Certain Access then that age can collect checks alone, + * and there is no reason to check the other age untile the universes converge. + + * The universes converge when the player has all the keys, giving both ages Certain Access. + + * We must check for these universes manually as we allow technical access with minimum keys for + * technical reasons as otherwise the code will never run + */ + + +bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion, ConditionFn otherCondition){ + SpiritLogicData curRegionData = Region::spiritLogicData[region]; + uint8_t childKeys = logic->ReverseSpiritChild ? curRegionData.childReverseKeys : curRegionData.childKeys; + // If we have enough keys that an age cannot be kept out, we have Certain Access + // otherwise if we have entered in reverse and can reach from the face, we have Certain Access + bool ChildCertainAccess = (logic->ReverseSpiritChild && curRegionData.reverseAccess) || logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys); + bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess) || logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys); + // If both ages have certain access, we can test with Either age + if (ChildCertainAccess && AdultCertainAccess) { + if (anyAge) { + return areaTable[region].Here(condition); + } + return condition(); + // otherwise, we have to check the current age and... + } else if (areaTable[region].Child() && logic->IsChild) { + bool result = condition(); + // If we have Certain Access, we just run the condition. + if (ChildCertainAccess) { + return result; + // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Adult too + } else if (result) { + SpiritLogicData otherRegionData = Region::spiritLogicData[otherRegion]; + // store current age variables + bool pastAdult = logic->IsAdult; + bool pastChild = logic->IsChild; + + logic->IsChild = false; + logic->IsAdult = true; + + // If Adult can get there and get the check, we can get the check in logic + // If reverse spirit is also possible, we need to make sure Adult can get it via reverse entry too + result = (curRegionData.adultAccess && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || + (otherRegion != RR_NONE && + (otherRegionData.adultAccess && (!logic->IsReverseAccessPossible() || otherRegionData.reverseAccess) && otherCondition())); + + logic->IsChild = pastChild; + logic->IsAdult = pastAdult; + + return result; + } + } else if (areaTable[region].Adult() && logic->IsAdult) { + bool result = condition(); + // if we have enough keys to have Certain Access, we just run the condition + // Alternatively, if we have entered in reverse and can reach from the face, we have Certain Access + if (AdultCertainAccess) { + return result; + // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Child too + } else if (result){ + SpiritLogicData otherRegionData = Region::spiritLogicData[otherRegion]; + // store current age variables + bool pastAdult = logic->IsAdult; + bool pastChild = logic->IsChild; + + logic->IsChild = true; + logic->IsAdult = false; + + // If Child can get there and get the check, we can get the check in logic + // If reverse spirit is also possible, we need to make sure Child can get it via reverse entry too + result = (curRegionData.childAccess && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || + (otherRegion != RR_NONE && + (otherRegionData.childAccess && (!logic->IsReverseAccessPossible() || otherRegionData.reverseAccess) && otherCondition())); + + logic->IsChild = pastChild; + logic->IsAdult = pastAdult; + + return result; + } + } + return false; } bool BeanPlanted(const RandomizerRegion region) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 23d454010..f1e943454 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -111,6 +111,25 @@ class Entrance; enum class EntranceType; } // namespace Rando +struct SpiritLogicData { + uint8_t childKeys; //the number of keys that guarantees Child can reach this region + //The number of keys that guarantees Child can reach this region if they have reverse access + //This changes for MQ broken wall room as the first child lock can only be opened by Child + //guaranteeing access with 6 keys + uint8_t childReverseKeys; + uint8_t adultKeys; //the number of keys that guarantees Adult can reach this region + //The area access condition to reach this region as Child, from the first lock, + //including the minimum number of keys for ambiguous access + // 1 key is always assumed to be required + ConditionFn childAccess; + //The area access condition to reach this region as Adult, from the first lock + //including the minimum number of keys for ambiguous access + //1 key is always assumed to be required on vanilla + ConditionFn adultAccess; + //The area access condition to reach this region from the boss door, + ConditionFn reverseAccess; +}; + class Region { public: Region(); @@ -231,85 +250,11 @@ class Region { "Adult Night: " + std::to_string(adultNight); } - - /* - * This logic covers checks that exist in the shared areas of Spirit from a glitchless standpoint. - * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on - specific ages - - * There are 4 possibilities for passing a check, but first I have to talk about parallel universes. - - * In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out - * In the second, they enter as adult and spend as many keys as they can to lock child out. - - * When an Age can no longer be kept out by the other age, that age is said to have Certain Access to a region - * If both ages have access to a region with a certain number of keys, but there is no Certain Access, - * then a check is only in logic if both ages can collect the check independently - - * If an age has Certain Access then that age can collect checks alone, - * and there is no reason to check the other age untile the universes converge. - - * The universes converge when the player has all the keys, giving both ages Certain Access. - - * We must check for these universes manually as we allow technical access with minimum keys for - * technical reasons as otherwise the code will never run - */ - - bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, uint8_t childKeys, - uint8_t adultKeys, uint8_t eitherKeys, bool anyAge = false) { - // If we have all of the keys, we know that access is Certain Access - if (ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->IsMQ() ? logic->SmallKeys(RR_SPIRIT_TEMPLE, 7) - : logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)) { - if (anyAge) { - return Here(condition); - } - return condition(); - // otherwise, we have to check the current age and... - } else if (Child() && logic->IsChild) { - bool result = condition(); - // if we have enough keys to have Certain Access, we just run the condition - if (logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys)) { - return result; - // otherwise we need to check both ages if we have enough keys that either can get there - } else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && adultAccess) { - // store current age variables - bool pastAdult = logic->IsAdult; - bool pastChild = logic->IsChild; - - logic->IsChild = false; - logic->IsAdult = true; - - result = condition(); - - logic->IsChild = pastChild; - logic->IsAdult = pastAdult; - - return result; - } - } else if (Adult() && logic->IsAdult) { - bool result = condition(); - // if we have enough keys to have Certain Access, we just run the condition - if (logic->SmallKeys(RR_SPIRIT_TEMPLE, adultKeys)) { - return result; - // otherwise we need to check both ages - } else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && childAccess) { - // store current age variables - bool pastAdult = logic->IsAdult; - bool pastChild = logic->IsChild; - - logic->IsChild = true; - logic->IsAdult = false; - - result = condition(); - - logic->IsChild = pastChild; - logic->IsAdult = pastAdult; - - return result; - } - } - return false; - } + + static std::map spiritLogicData; + + bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, ConditionFn ReverseAccess, + uint8_t childKeys, uint8_t adultKeys, uint8_t eitherKeys, bool anyAge); }; extern std::array areaTable; @@ -318,11 +263,8 @@ extern std::vector grottoEvents; bool Here(const RandomizerRegion region, ConditionFn condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this -bool SpiritSharedBrokenWallRoom(ConditionFn condition, bool anyAge = false); -bool SpiritSharedStatueRoom(ConditionFn condition, bool anyAge = false); -bool SpiritSharedSunBlockRoom(ConditionFn condition, bool anyAge = false); -bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false); -bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false); +bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge = false, + RandomizerRegion otherRegion = RR_NONE, ConditionFn otherCondition = []{return false;}); bool CanPlantBean(const RandomizerRegion region); bool BothAges(const RandomizerRegion region); bool ChildCanAccess(const RandomizerRegion region); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index dd7de996a..8c2f49d58 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -22,44 +22,45 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_2, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), + Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_LOBBY] = Region("Child Spirit Temple", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_1F_WEST] = Region("Spirit Temple 1F West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->NutCrate, []{return true;}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);}), + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_STALFOS] = Region("Child Spirit Temple Stalfos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH] = Region("Spirit Temple Switch Bridge South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SpiritChildStalfosBridge, []{return logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU));}), + //RANDOTODO a version od CanHitSwitch that takes WallOrFloor + EventAccess(&logic->SpiritChildSwitchBridge, []{return logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU));}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, []{return (logic->SpiritChildStalfosBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return (logic->SpiritChildSwitchBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE] = Region("Child Spirit Temple Stalfos Across Bridge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH] = Region("Spirit Temple Switch Bridge North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SpiritChildStalfosBridge, []{return logic->CanHitSwitch();}), + EventAccess(&logic->SpiritChildSwitchBridge, []{return logic->CanHitSwitch();}), }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS, []{return logic->CanUse(RG_HOVER_BOOTS) || (logic->SpiritChildStalfosBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false));}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_ANUBIS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return logic->CanUse(RG_HOVER_BOOTS) || (logic->SpiritChildSwitchBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false));}), + Entrance(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_ANUBIS] = Region("Child Spirit Temple Anubis", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_1F_ANUBIS] = Region("Spirit Temple 1F Anubis", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_1, true), LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_2, true), @@ -67,214 +68,297 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_4, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return Here(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);});}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, []{return Here(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);});}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH] = Region("Spirit Temple Rupee Bridge North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SpiritChildTorchesBridge, []{return true;}), + EventAccess(&logic->SpiritRupeeBridge, []{return true;}), }, { //Locations - // these assume SpiritChildTorchesBridge, silver rupee shuffle & shuffle climb will want to adjust + // these assume SpiritRupeeBridge, silver rupee shuffle & shuffle climb will want to adjust LOCATION(RC_SPIRIT_TEMPLE_CHILD_EARLY_TORCHES_CHEST, logic->HasFireSourceWithTorch()), // possible to collect without lowering fence, should be a trick LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES, []{return logic->SpiritChildTorchesBridge;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_ANUBIS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return logic->SpiritRupeeBridge;}), + Entrance(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH] = Region("Spirit Temple Rupee Bridge South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOOMERANG)), + LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->SpiritChildTorchesBridge;}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, []{return logic->SpiritRupeeBridge;}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB] = Region("Child Spirit Temple Before Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_1F_BOXES] = Region("Child Spirit Temple Before Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_1, logic->CanBreakSmallCrates()), LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_2, logic->CanBreakSmallCrates()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_CLIMB] = Region("Child Spirit Temple Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE] = Region("Spirit Temple Child Climb Base", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritSharedBrokenWallRoom([]{return logic->CanHitSwitch(ED_BOMB_THROW);})), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritSharedBrokenWallRoom([]{return logic->CanHitSwitch(ED_BOMB_THROW);})), - LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritSharedBrokenWallRoom([]{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);})), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_LOBBY] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { - //Events - EventAccess(&logic->SpiritAdultLobbySwitch, []{return logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH));}), - }, {}, { + areaTable[RR_SPIRIT_TEMPLE_BROKEN_WALL] = Region("Spirit Temple Broken Wall", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})), + }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_SAND_PIT, []{return logic->SpiritAdultLobbySwitch;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return logic->SpiritAdultLobbySwitch;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), + Entrance(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return true;}), + ///*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS))) + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->SpiritBrokenWallToStatue();}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_SAND_PIT] = Region("Adult Spirit Temple Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_1F_EAST] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SAND_PIT, []{return Here(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanHitSwitch(logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH) ? ED_BOMB_THROW : ED_BOOMERANG);});}), + Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return Here(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanHitSwitch(logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH) ? ED_BOMB_THROW : ED_BOOMERANG);});}), + Entrance(RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SAND_PIT] = Region("Spirit Temple Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_COMPASS_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_ZELDAS_LULLABY)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_BOULDERS] = Region("Adult Spirit Temple Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_ABOVE_BOULDERS] = Region("Spirit Temple Above Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //Jump slash is possible as child, but pretty tight. Jumpslash as late as you can + //A damage boost off the boulder is also possible, but you need + EventAccess(&logic->SpiritBouldersSilvers, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT);}), + }, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BOULDERS] = Region("Spirit Temple Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_GS_BOULDER_ROOM, logic->CanUse(RG_SONG_OF_TIME) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, []{return true;}), + //Jump slash is possible as child, but pretty tight. Jumpslash as late as you can + //A damage boost off the boulder is also possible, but you need + Entrance(RR_SPIRIT_TEMPLE_PAST_BOULDERS, []{return logic->SpiritBouldersSilvers;}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS] = Region("Adult Spirit Temple Past Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_PAST_BOULDERS] = Region("Spirit Temple Past Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_EARLY_ADULT_RIGHT_CHEST, true), LOCATION(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_CLIMB] = Region("Adult Spirit Temple Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE] = Region("Spirit Temple East Climb Base", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_2F_MIRROR] = Region("Spirit Temple 2F Mirror", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, true), - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, true), + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;})), + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), + Entrance(RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), }); + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST] = Region("Spirit Temple Statue Rooom West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM access + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), + //Current and N64 logic doesn't need scarecrow, you can hit the skull with normal hookshot from a specific spot on the forearm + //Child can get this with hook by backflipping onto the upper arm and standing in a precise place + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) ? ED_BOOMERANG : ED_HOOKSHOT);}, false, + RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}), + //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW) + Entrance(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->SpiritWestToSkull();}), + // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), + Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true;}), + //!QUANTUM LOGIC! + //When child enters spirit in reverse, has 4 keys, and dungeon entrance shuffle is off, + //Child cannot lock themselves out of desert colossus access as if they save the west hand lock for last + //they will be able to exit the dungeon through the intended entrance and vice versa + //for needing to open the west hand lock to block the intended child route + Entrance(RR_DESERT_COLOSSUS, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && logic->ReverseSpiritChild && + logic->IsChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_GS_LEDGE] = Region("Spirit Temple GS ledge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}) + }); + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM] = Region("Spirit Temple Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritSharedStatueRoom([]{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), - LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritSharedStatueRoom([]{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, - (ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS)) ? ED_CLOSE : ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) ? ED_BOOMERANG : ED_HOOKSHOT);})), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_4, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_5, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource();}, false, + RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_4, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_5, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}), // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled - Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_SHORTCUT, []{return logic->SpiritStatueRoomSouthDoor;}), }); - areaTable[RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE] = Region("Spirit Temple Stairs to Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_EMPTY_STAIRS] = Region("Spirit Temple Empty Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE] = Region("Spirit Temple Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { - //Events - EventAccess(&logic->SpiritSunBlockTorch, []{return SpiritSharedSunBlockRoom([]{return logic->IsAdult || logic->CanKillEnemy(RE_BEAMOS);});}), - }, { + areaTable[RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM] = Region("Spirit Temple Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - //Child can push blocks to get to the chest without killing the beamos, but it's likely a trick for similar reasons to armos push, and is not relevant without doorsanity - LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritSharedSunBlockRoom([]{return (logic->HasFireSource() || - (logic->SpiritSunBlockTorch && (logic->CanUse(RG_STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))) && - (logic->IsAdult || logic->CanKillEnemy(RE_BEAMOS) || logic->CanUse(RG_HOOKSHOT));})), + //the blocks can be used to get all the silver rupees and the chest itemless + LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, + []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND] = Region("Spirit Temple West Stairs to Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS] = Region("Spirit Temple Skulltula Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_GS_HALL_AFTER_SUN_BLOCK_ROOM, (logic->HasExplosives() && logic->CanUse(RG_BOOMERANG) && logic->CanUse(RG_HOOKSHOT)) || - (logic->CanUse(RG_BOOMERANG) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives()) || - (logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && - logic->CanUse(RG_BOOMERANG) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))))), - LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_1, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), - LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_2, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), + LOCATION(RC_SPIRIT_TEMPLE_GS_HALL_AFTER_SUN_BLOCK_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);})), + LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), }); - areaTable[RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_WEST_THRONE] = Region("Spirit Temple West Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return Here(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_WEST_HAND_EXIT] = Region("Spirit Temple West Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_WEST_HAND] = Region("Spirit Temple West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST] = Region("Spirit Temple Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)), - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->CanUse(RG_ZELDAS_LULLABY) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))), + //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST access. WARNING: ZL spawning chests here is a temp flag + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_CLIMB, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), - Entrance(RR_SPIRIT_TEMPLE_SHORTCUT, []{return logic->CanUse(RG_MEGATON_HAMMER) && (ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS) || (logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanUse(RG_HOOKSHOT)));}), + Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); + Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->SpiritEastToSwitch();}), + Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), + //!QUANTUM LOGIC! + //With 3 keys, you cannot lock adult out of longshotting to the west hand as you would have to + //open the west hand door and then adult could climb through sun block room + //This requires that adult can complete both routes + //Implies CanKillEnemy(RE_IRON_KNUCKLE) + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_LONGSHOT) && logic->CanKillEnemy(RE_BEAMOS);}), + //Similarly, we can gurantee jumping into desert colossus the same way + //except this does not need the longshot as we don't care which hand we jump down from + Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS);}), }); - areaTable[RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS] = Region("Spirit Temple Stairs to Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH] = Region("Spirit Temple Shortcut Switch", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->CanUse(RG_MEGATON_HAMMER);});}), + }, { + //Locations + //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST access + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //If child can ever use silver gauntlets and adult items, there needs to be an event here to account for child entering in reverse + //opening the way for adult entering via the front. + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_MEGATON_HAMMER);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_POT_STAIRS] = Region("Spirit Temple Stairs to Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { LOCATION(RC_SPIRIT_TEMPLE_BEAMOS_HALL_POT_1, logic->CanBreakPots()), }, { Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { - //Locations - LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->HasExplosives()), - LOCATION(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, logic->HasExplosives() && logic->CanUse(RG_SUNS_SONG)), - }, { + areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->CanKillEnemy(RE_BEAMOS);}), - Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + //Implies killing the anubis with the fire ring, doing so itemless requires voiding out, which can lock hardcore + OHKO seeds + Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), }); - areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->HasExplosives()), + LOCATION(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, logic->HasExplosives() && logic->CanUse(RG_SUNS_SONG)), + }, { Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM, []{return logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), - Entrance(RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM] = Region("Spirit Temple Four Armos Side Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -285,46 +369,47 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_CHEST_STAIRS] = Region("Spirit Temple Chest Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_LEFT_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)), LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_RIGHT_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_EAST_THRONE] = Region("Spirit Temple East Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return Here(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_EAST_HAND_EXIT] = Region("Spirit Temple East hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND] = Region("Spirit Temple Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_EAST_HAND] = Region("Spirit Temple East Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, []{return logic->CanUse(RG_LONGSHOT);}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_LOWER] = Region("Spirit Temple Big Wall Lower", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER)));}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && logic->CanPassEnemy(RE_WALLTULA));}), }); areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_UPPER] = Region("Spirit Temple Big Wall Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations + //Grabbing these with rang is possible, but requires a blind shot aimed high LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_LEFT_HEART, logic->CanUse(RG_HOOKSHOT)), LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_RIGHT_HEART, logic->CanUse(RG_HOOKSHOT)), }, { @@ -336,41 +421,69 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_4F_CENTRAL] = Region("Spirit Temple 4F Central", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), + Entrance(RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM] = Region("Spirit Temple Boss Key Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM] = Region("Spirit Temple Fake Doors Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, (logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) || (logic->CanHitEyeTargets() && logic->CanUse(RG_HOOKSHOT))), + LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, (logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) || + (Here(RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, []{return logic->CanHitEyeTargets() && logic->CanAvoidEnemy(RE_TORCH_SLUG, true, 4);}) + && logic->CanUse(RG_HOOKSHOT))), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM] = Region("Spirit Temple Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM] = Region("Spirit Temple Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->Spirit4FSwitch, []{return logic->CanJumpslash() || logic->HasExplosives();}), + //Needs the mirror in the cave to be a perm flag and event for doorsanity + EventAccess(&logic->SpiritPlatformLowered, []{return (Here(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return logic->CanJumpslash() || logic->HasExplosives();}) || logic->CanUse(RG_MIRROR_SHIELD)) || + (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + + }, {}, { Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, []{return logic->CanJumpslash() || logic->HasExplosives();}), + //Rang can hit the switch on the way back but that's a trick + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, []{return logic->Spirit4FSwitch;}), + //Assumes RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE access + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return (logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->Spirit4FSwitch) || + (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), }); - areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE] = Region("Spirit Temple Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE] = Region("Spirit Temple Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->Spirit4FSwitch, []{return logic->HasExplosives();}), + }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))), + LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, (logic->IsAdult && logic->CanUse(RG_MIRROR_SHIELD)) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_MEGATON_HAMMER);}), + //Assumes a SpiritPlatformLowered check on entry + areaTable[RR_SPIRIT_TEMPLE_PLATFORM] = Region("Spirit Temple Lowered Platform", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->Spirit4FSwitch;}) + && logic->CanUse(RG_HOOKSHOT);}), }); - areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_STATUE_HEAD] = Region("Spirit Temple Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //WARNING these events are not glitchproofed and assume you need all keys to reach from the front + EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}), + EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}), + }, {}, { // Exits - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT));}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); #pragma endregion @@ -432,7 +545,6 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - //For non-fairy pot items, you can also get them with rang without killing the stalfos EventAccess(&logic->FairyPot, []{return Here(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}), }, { //Locations @@ -470,7 +582,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits //The bridge is a temp flag, so not a way to cross south to north in logic - Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, []{return logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}), }); @@ -487,7 +599,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE] = Region("Spirit Temple MQ Under Like Like", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_LIKE_LIKE_POT, MQSpiritSharedBrokenWallRoom(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_LIKE_LIKE_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanBreakPots();})), }, { //Exits //This covers adult access only, as child arrives here from the other side of this door @@ -498,10 +610,9 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM] = Region("Spirit Temple MQ Broken Wall Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations //Implies CanKillEnemy(RE_LIKE_LIKE) - LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, MQSpiritSharedBrokenWallRoom(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanKillEnemy(RE_BEAMOS);})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanKillEnemy(RE_BEAMOS);})), //Sunlights only temp spawn this chest, which is unintuitive/a bug. - //chest is only reachable as adult glitchlessly, so we can skip the shared in favour of IsAdult as adult access is always Certain - LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, logic->IsAdult && (logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return (logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->CanUse(RG_HOOKSHOT);})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanHitSwitch();}), @@ -509,28 +620,48 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 2);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM] = Region("Spirit Temple MQ Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST] = Region("Spirit Temple MQ Statue Room West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME);});}), + }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MQ_COMPASS_CHEST, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanHitEyeTargets();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG)) || ((logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) && logic->CanBreakPots());})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) && logic->CanBreakPots());})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_CENTER_EAST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_WEST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_EASTMOST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_1, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_2, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakSmallCrates();})), - + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG)) || + ((logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) && logic->CanBreakPots());})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG) && ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE)) || + (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) && logic->CanBreakPots());})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return logic->CanBreakSmallCrates();})), }, { //Exits - //we check possible adult access directly in MQSpiritSharedBrokenWallRoom, so this exit only covers Certain Access - Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), - //We can use Here instead of Shared here because adult will never need to rely on child access to reach this room, and adult access is Certain - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return Here(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME));});}), - Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || logic->CanUse(RG_SONG_OF_TIME);}), + //Minimal entry, real key logic handled in Shared functions + Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), + //IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME) + Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->MQSpiritStatueToSunBlock();}), //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM] = Region("Spirit Temple MQ Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->HasFireSource();});}), + }, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MQ_COMPASS_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanHitEyeTargets();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_CENTER_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_EASTMOST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_1, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_2, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), + //the drop sometimes flies off the block when the crate is blown up, but not always, so I added a rang requirement + //a trick to reload for it is plausible + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return (logic->CanUse(RG_SONG_OF_TIME) && logic->CanBreakSmallCrates()) || + (logic->CanUse(RG_BOOMERANG) && logic->HasExplosives());})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return true/*logic->CanClimb()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return logic->SpiritStatueRoomSouthDoor;}), + //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_SCARECROW);}), + }); areaTable[RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS] = Region("Spirit Temple MQ Flamethrower Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits @@ -541,43 +672,43 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM] = Region("Spirit Temple MQ Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations //We don't need Shared here because If we are checking as child, universe 2 adult access needs nothing so it always passes, and if we are checking as adult, it is Certain Access - LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, true), - LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, SpiritMQSharedSunBlockRoom([]{return true/*str0*/;})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritMQSharedSunBlockRoom([]{return logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, SpiritMQSharedSunBlockRoom([]{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, SpiritMQSharedSunBlockRoom([]{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true/*str0*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, []{return true/*str0*/;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS] = Region("Spirit Temple MQ Skulltula Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}), //This door causes the Universes to merge as it requires 7 keys for both ages - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND] = Region("Spirit Temple MQ West Stairs to Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_THRONE] = Region("Spirit Temple MQ West Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT] = Region("Spirit Temple MQ West Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return Here(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Exit to Silver Gauntlets Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { - //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return true;}), - }); - - areaTable[RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Silver Gauntlets Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_HAND] = Region("Spirit Temple MQ West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), }, { //Exits //If it is ever relevent for 1 age to spawn the mirror shield chest for the other can longshot across, it needs an eventAccess - Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH] = Region("Spirit Temple MQ Block Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -590,8 +721,8 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH] = Region("Spirit Temple MQ Block Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - //Does not need to be shared as it's hard child locked, because adult pushing the block is a permanent flag that blocks the eye target and cannot be undone - LOCATION(RC_SPIRIT_TEMPLE_MQ_SILVER_BLOCK_HALLWAY_CHEST, logic->IsChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 7) && logic->CanHitEyeTargets()), + //Dhard child locked because adult pushing the block is a permanent flag that blocks the eye target and cannot be undone + LOCATION(RC_SPIRIT_TEMPLE_MQ_SILVER_BLOCK_HALLWAY_CHEST, SpiritMQSharedBigBlock([]{return logic->IsChild && logic->CanHitEyeTargets();})), }, { //Exits //if going to RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH from here is ever relevant, there needs to be an event to handle the block @@ -609,7 +740,7 @@ void RegionTable_Init_SpiritTemple() { //We only need 4 keys, access to Shield hand and longshot to reach Gauntlets hand, as if we waste the 5th key we have given ourselves Gauntlets hand access through child climb //This exit handles that possibility as cleanly as possible without quantum logic, but will not survive glitch logic //logic->CanKillEnemy(RE_FLOORMASTER) is implied - Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanJumpslash() && (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && @@ -647,11 +778,11 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return logic->CanUse(RG_MEGATON_HAMMER);}), Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SAND_PIT, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM] = Region("Spirit Temple MQ Leever Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SAND_PIT] = Region("Spirit Temple MQ Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_LEEVER_ROOM_CHEST, logic->CanKillEnemy(RE_PURPLE_LEEVER) && logic->CanUse(RG_HOOKSHOT)), LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_LEEVER_ROOM, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), @@ -732,7 +863,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}), Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return true;}), Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); @@ -810,11 +941,16 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->MQSpiritOpenedBigMirrorCave;}), //If it's ever relevant to longshot into head from lobby, this needs to be an event access - Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}), Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD] = Region("Spirit Temple MQ Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //WARNING these events are not glitchproofed and assume you need all keys to reach from the front + EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}), + EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}), + }, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), @@ -825,9 +961,9 @@ void RegionTable_Init_SpiritTemple() { // Boss Room areaTable[RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY] = Region("Spirit Temple Boss Entryway", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits - Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", "Spirit Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index acae5c2d4..d6e06bf79 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -539,14 +539,11 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS); [[fallthrough]]; case ED_BOMB_THROW: - killed = killed || CanUse(RG_BOMB_BAG); + killed = killed || CanUse(RG_BOMB_BAG) || CanUse(RG_DINS_FIRE); [[fallthrough]]; case ED_BOOMERANG: - // RANDOTODO test dins and chu range in a practical example - killed = killed || CanUse(RG_DINS_FIRE); - [[fallthrough]]; case ED_HOOKSHOT: - // RANDOTODO test dins and chu range in a practical example + // RANDOTODO test chu range in a practical example killed = killed || CanUse(RG_HOOKSHOT) || (wallOrFloor && CanUse(RG_BOMBCHU_5)); [[fallthrough]]; case ED_LONGSHOT: @@ -576,15 +573,15 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS); [[fallthrough]]; case ED_BOMB_THROW: - // RANDOTODO test dins and chu range in a practical example + // RANDOTODO test chu range in a practical example killed = killed || (!inWater && CanUse(RG_BOMB_BAG)); [[fallthrough]]; case ED_BOOMERANG: - // RANDOTODO test dins and chu range in a practical example + // RANDOTODO test chu range in a practical example killed = killed || CanUse(RG_BOOMERANG); [[fallthrough]]; case ED_HOOKSHOT: - // RANDOTODO test dins, bomb and chu range in a practical example + // RANDOTODO test chu range in a practical example killed = killed || CanUse(RG_HOOKSHOT) || (wallOrFloor && CanUse(RG_BOMBCHU_5)); [[fallthrough]]; case ED_LONGSHOT: @@ -629,7 +626,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal [[fallthrough]]; case ED_BOOMERANG: case ED_HOOKSHOT: - // RANDOTODO test dins and chu range in a practical example + // RANDOTODO test chu range in a practical example killed = killed || (wallOrFloor && CanUse(RG_BOMBCHU_5)); [[fallthrough]]; case ED_LONGSHOT: @@ -710,12 +707,11 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS); [[fallthrough]]; case ED_BOMB_THROW: - // RANDOTODO test dins and chu range in a practical example killed = killed || (!inWater && CanUse(RG_BOMB_BAG)); [[fallthrough]]; case ED_BOOMERANG: case ED_HOOKSHOT: - // RANDOTODO test dins, bomb and chu range in a practical example + // RANDOTODO test chu range in a practical example killed = killed || CanUse(RG_HOOKSHOT) || (wallOrFloor && CanUse(RG_BOMBCHU_5)); [[fallthrough]]; case ED_LONGSHOT: @@ -789,6 +785,33 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal case RE_OCTOROK: return CanReflectNuts() || HookshotOrBoomerang() || CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_BOMB_BAG) || (wallOrFloor && CanUse(RG_BOMBCHU_5)); + case RE_WALLTULA: + switch (distance) { + case ED_CLOSE: + case ED_SHORT_JUMPSLASH: + killed = CanUse(RG_KOKIRI_SWORD); + [[fallthrough]]; + case ED_MASTER_SWORD_JUMPSLASH: + killed = killed || CanUse(RG_MASTER_SWORD); + [[fallthrough]]; + case ED_LONG_JUMPSLASH: + killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS); + [[fallthrough]]; + case ED_BOMB_THROW: + killed = killed || (!inWater && CanUse(RG_BOMB_BAG)) || CanUse(RG_DINS_FIRE); + [[fallthrough]]; + case ED_BOOMERANG: + case ED_HOOKSHOT: + killed = killed || CanUse(RG_HOOKSHOT) || CanUse(RG_BOMBCHU_5) || CanUse(RG_MEGATON_HAMMER); + [[fallthrough]]; + case ED_LONGSHOT: + killed = killed || CanUse(RG_LONGSHOT); + [[fallthrough]]; + case ED_FAR: + killed = killed || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); + break; + } + return killed; default: SPDLOG_ERROR("CanKillEnemy reached `default`."); assert(false); @@ -841,6 +864,7 @@ bool Logic::CanPassEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal return CanUse(RG_HOOKSHOT) || CanUse(RG_SUNS_SONG); case RE_IRON_KNUCKLE: case RE_BIG_OCTO: + case RE_WALLTULA: //consistent with RT_SPIRIT_WALL return false; case RE_GREEN_BUBBLE: return TakeDamage() || CanUse(RG_NUTS) || CanUse(RG_BOOMERANG) || CanUse(RG_HOOKSHOT); @@ -887,6 +911,7 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity case RE_WALLMASTER: case RE_ANUBIS: case RE_PURPLE_LEEVER: + case RE_WALLTULA: return true; case RE_BEAMOS: return !grounded || CanUse(RG_NUTS) || CanUse(RG_DINS_FIRE) || @@ -899,6 +924,8 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity case RE_BLUE_BUBBLE: // RANDOTODO Trick to use shield hylian shield as child to stun these guys return !grounded || CanUse(RG_NUTS) || HookshotOrBoomerang() || CanStandingShield(); + case RE_TORCH_SLUG: + return !grounded || CanUse(RG_NUTS) || CanUse(RG_HOOKSHOT) || CanUse(RG_DINS_FIRE); default: SPDLOG_ERROR("CanPassEnemy reached `default`."); assert(false); @@ -2331,6 +2358,32 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { inLogic[logicVal] = value; } +bool Logic::IsReverseAccessPossible(){ + return ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES) && + (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || + ctx->GetOption(RSK_MIX_BOSS_ENTRANCES)); +} + +bool Logic::SpiritBrokenWallToStatue() { + return /*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS))); +} + +bool Logic::SpiritEastToSwitch() { + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); +} + +bool Logic::SpiritWestToSkull() { + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW); +} + +bool Logic::MQSpiritStatueToSunBlock() { + return IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME); +} + +bool Logic::MQSpiritStatueSouthDoor() { + return HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && CanUse(RG_FAIRY_BOW) && CanUse(RG_SONG_OF_TIME)/* && CanClimb()*/); +} + void Logic::Reset() { NewSaveContext(); StartPerformanceTimer(PT_LOGIC_RESET); @@ -2520,11 +2573,15 @@ void Logic::Reset() { MQSpirit3SunsEnemies = false; MQSpiritOpenedBigMirrorCave = false; Spirit1FSilverRupees = false; - SpiritChildStalfosBridge = false; - SpiritChildTorchesBridge = false; - SpiritSunBlockTorch = false; - SpiritAdultLobbySwitch = false; + SpiritChildSwitchBridge = false; + SpiritRupeeBridge = false; + SpiritBouldersSilvers = false; + SpiritPlatformLowered = false; + Spirit4FSwitch = false; + ReverseSpiritChild = false; + ReverseSpiritAdult = false; StopPerformanceTimer(PT_LOGIC_RESET); } + } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 207109f4c..2a03262ee 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -26,6 +26,7 @@ enum class GlitchDifficulty { HERO, }; + class Logic { public: bool noVariable = false; @@ -182,10 +183,14 @@ class Logic { bool MQSpirit3SunsEnemies = false; bool MQSpiritOpenedBigMirrorCave = false; bool Spirit1FSilverRupees = false; - bool SpiritChildStalfosBridge = false; - bool SpiritChildTorchesBridge = false; - bool SpiritSunBlockTorch = false; - bool SpiritAdultLobbySwitch = false; + bool SpiritChildSwitchBridge = false; + bool SpiritRupeeBridge = false; + bool SpiritBouldersSilvers = false; + bool SpiritStatueRoomSouthDoor = false; + bool SpiritPlatformLowered = false; + bool Spirit4FSwitch = false; + bool ReverseSpiritChild = false; + bool ReverseSpiritAdult = true; /* --- END OF HELPERS AND LOCATION ACCESS --- */ @@ -294,6 +299,12 @@ class Logic { static std::map RandoGetToDungeonScene; static std::map RandoGetToEquipFlag; static std::map RandoGetToRandInf; + bool Logic::IsReverseAccessPossible(); + bool Logic::SpiritBrokenWallToStatue(); + bool Logic::SpiritEastToSwitch(); + bool Logic::SpiritWestToSkull(); + bool Logic::MQSpiritStatueToSunBlock(); + bool Logic::MQSpiritStatueSouthDoor(); private: std::shared_ptr ctx; diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 6353c12f2..7d669730d 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -8,6 +8,8 @@ #define TWO_ACTOR_PARAMS(a, b) ((((a)&0xFFFF) << 16) | ((b)&0xFFFF)) +typedef bool (*ConditionFn)(); + // This should probably go in a less rando-specific location // but the best location will probably be in the modding engine // which doesn't exist yet. @@ -844,43 +846,50 @@ typedef enum { RR_WATER_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_LOBBY, - RR_SPIRIT_TEMPLE_CHILD_LOBBY, - RR_SPIRIT_TEMPLE_CHILD_STALFOS, - RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, - RR_SPIRIT_TEMPLE_CHILD_ANUBIS, - RR_SPIRIT_TEMPLE_CHILD_TORCHES, - RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, - RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, - RR_SPIRIT_TEMPLE_CHILD_CLIMB, - RR_SPIRIT_TEMPLE_ADULT_LOBBY, - RR_SPIRIT_TEMPLE_ADULT_SAND_PIT, - RR_SPIRIT_TEMPLE_ADULT_BOULDERS, - RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS, - RR_SPIRIT_TEMPLE_ADULT_CLIMB, + RR_SPIRIT_TEMPLE_1F_WEST, + RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, + RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, + RR_SPIRIT_TEMPLE_1F_ANUBIS, + RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, + RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, + RR_SPIRIT_TEMPLE_1F_BOXES, + RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, + RR_SPIRIT_TEMPLE_BROKEN_WALL, + RR_SPIRIT_TEMPLE_1F_EAST, + RR_SPIRIT_TEMPLE_SAND_PIT, + RR_SPIRIT_TEMPLE_BOULDERS, + RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, + RR_SPIRIT_TEMPLE_PAST_BOULDERS, + RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE, + RR_SPIRIT_TEMPLE_2F_MIRROR, RR_SPIRIT_TEMPLE_STATUE_ROOM, + RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, + RR_SPIRIT_TEMPLE_GS_LEDGE, RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, - RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, - RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, - RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, - RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, - RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, - RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, - RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, + RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, + RR_SPIRIT_TEMPLE_SHORTCUT, + RR_SPIRIT_TEMPLE_EMPTY_STAIRS, + RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, + RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, + RR_SPIRIT_TEMPLE_WEST_THRONE, + RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, + RR_SPIRIT_TEMPLE_WEST_HAND, + RR_SPIRIT_TEMPLE_POT_STAIRS, RR_SPIRIT_TEMPLE_BEAMOS_PITS, RR_SPIRIT_TEMPLE_FOUR_ARMOS, RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM, - RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, - RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, - RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, - RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND, + RR_SPIRIT_TEMPLE_CHEST_STAIRS, + RR_SPIRIT_TEMPLE_EAST_THRONE, + RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, + RR_SPIRIT_TEMPLE_EAST_HAND, RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, RR_SPIRIT_TEMPLE_4F_CENTRAL, - RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM, + RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, - RR_SPIRIT_TEMPLE_SHORTCUT, - RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, + RR_SPIRIT_TEMPLE_PLATFORM, + RR_SPIRIT_TEMPLE_STATUE_HEAD, RR_SPIRIT_TEMPLE_MQ_LOBBY, RR_SPIRIT_TEMPLE_MQ_1F_WEST, @@ -893,19 +902,20 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, + RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, - RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, - RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, - RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, - RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, + RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, + RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, + RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, + RR_SPIRIT_TEMPLE_MQ_WEST_HAND, RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH, RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F, RR_SPIRIT_TEMPLE_MQ_1F_EAST, - RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM, + RR_SPIRIT_TEMPLE_MQ_SAND_PIT, RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM, RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, @@ -923,7 +933,7 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM, RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, - RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, + RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, @@ -3665,7 +3675,7 @@ typedef enum { RT_SHADOW_MQ_WINDY_WALKWAY, RT_LENS_SPIRIT, RT_SPIRIT_CHILD_CHU, - RT_SPIRIT_LOBBY_GS, + RT_SPIRIT_WEST_LEDGE, RT_SPIRIT_LOWER_ADULT_SWITCH, RT_SPIRIT_LOBBY_JUMP, RT_SPIRIT_PLATFORM_HOOKSHOT, @@ -6481,6 +6491,7 @@ typedef enum { RE_BARI, RE_SHABOM, RE_OCTOROK, + RE_WALLTULA, } RandomizerEnemy; // RANDOTODO compare child long jumpslash range with adult short diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index c5d0c23d8..f36ffec9a 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1006,11 +1006,11 @@ void Settings::CreateOptions() { "Removes the requirements for the Lens of Truth in Spirit Temple."); OPT_TRICK(RT_SPIRIT_CHILD_CHU, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "Spirit Temple Child Side Bridge with Bombchu", "A carefully-timed Bombchu can hit the switch."); - OPT_TRICK(RT_SPIRIT_LOBBY_GS, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, - "Spirit Temple Main Room GS with Boomerang", - "Standing on the highest part of the arm of the statue, a precise Boomerang throw can kill and obtain " - "this Gold Skulltula. You must throw the Boomerang slightly off to the side so that it curves into the " - "Skulltula, as aiming directly at it will clank off of the wall in front."); + OPT_TRICK(RT_SPIRIT_WEST_LEDGE, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, + "Spirit Temple Statue Room West Ledge Checks with Boomerang", + "By carefully walking onto the upper arm of the statue, it's possible to get a good angle on the " + "Gold Skulltula (In Vanilla) and the farthest pot (In MQ) to collect the checks with Boomerang. " + "The nearest pot in MQ can be reached from the forearm and is always in logic."); OPT_TRICK(RT_SPIRIT_LOWER_ADULT_SWITCH, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED }, "Spirit Temple Lower Adult Switch with Bombs", "A bomb can be used to hit the switch on the ceiling, but it must be thrown from a particular distance "