diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 14addde6e..2a6e571c4 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -561,96 +561,6 @@ void Region::ResetVariables() { } } -/* - * This logic covers checks that exist in the shared areas of MQ spirit from a glitchless standpoint. - * This room has Quantum logic that I am currently handling with this function, however this is NOT suitable for - glitch logic as it relies on specific ages - * In this chunk there are 3 possibilities for passing a check, but first I have to talk about parallel universes. - - * In MQ Spirit key logic, we mostly care about 2 possibilities for how the player can spend keys, creating 2 - Parralel universes - * In the first universe, the player did not enter spirit as adult until after climbing as child, thus child spends - keys linearly, only needing 2 to reach statue room. - * In the second universe, the player went in as adult, possibly out of logic, and started wasting the keys to lock - child out. - * These Universes converge when the player has 7 keys (meaning adult can no longer lock child out) and adult is - known to be able to reach Statue room. This creates "Certain Access", which is tracked separatly for each age. - * Child Certain Access is simple, if we have 7 keys and child access, it's Certain Access. - * Adult Certain Access is also simple, adult is not key locked, so if they make it to a location, it's Certain - Access. - * Things get complicated when we handle the overlap of the 2 universes, - * though an important detail is that if we have Certain Access as either age, we don't need to checked the overlap - because overlap logic is strictly stricter than either Certain Access. - - * In order to track the first universe, the logic allows technical child access with the minimum number of keys, - and then checks in this function for if we have 7 keys to determine if that is Certain or not. - * This is for technical reasons, as areas with no access at all will simply not be checked. - * Normally we would need to do similar shenanigans to track the second universe, however adult must have go through - statue room to waste keys, - * so can go back there and get new keys for Child to use if they do, and the navigation logic for shared MQ spirit - from Statue Room is very simple for Adult. - * Additionally, we don't need to know if adult can actually reach spirit temple or climb to statue room, because if - the player can't do that, then universe 2 can't happen anyway, - * and if the player does so out of logic, they can do it again, as the only consumable used sets a permanent flag. - - * The Adult Navigation logic is as such: - * - Broken Wall room is 6 key locked, because if the player tries to spend 6 keys in a way that would block adults - access, they would have to give child access instead. - * - The child side hammer switch for the time travelling chest is 7 key locked for adult - * - Reaching gauntlets hand is 7 key locked - * - Going back into big block room is complex, but the only check there is child only so not a concern - * - Everything else is possible with basic adult movement, or is impossible for child to reach glitchlessly - * Anything 7 key locked does not need to be checked as shared, as all child access is Certain and because of this - workaround we don't need to fake Adult access, meaning that is also Certain. - * All of this combined means that when checking if adult can reach a location in universe 2, we only have to ask if - it is a 6 key locked location or not. - - * Knowing all of this this, we can confirm things are logical in 3 different ways: - * - If we have Adult Access, we know it is Certain Access, so they can get checks alone. - * - If we have 7 keys, child has Certain Access as we know they cannot be locked out, so can get checks alone, - otherwise we check the logical overlap - * - If Child and Adult can get the check (ignoring actual adult access to the location), and the location is either - not 6 key locked or we have 6 keys, we can get the check with the overlap - */ -bool Region::MQSpiritShared(ConditionFn condition, bool IsBrokenWall, bool anyAge) { - // if we have Certain Access as child, we can check anyAge and if true, resolve a condition with Here as if - // adult is here it's also Certain Access - if (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7)) { - if (anyAge) { - return AnyAgeTime(condition); - } - return condition(); - // else, if we are here as adult, we have Certain Access from that and don't need special handling for - // checking adult - } else if (Adult() && logic->IsAdult) { - return condition(); - // if we do not have Certain Access, we need to check the overlap by seeing if we are both here as child and - // meet the adult universe's access condition. We only need to do it as child, as only child access matters - // for this check, as adult access is assumed based on keys - } else if (Child() && logic->IsChild && (!IsBrokenWall || logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 6))) { - // store current age variables - bool pastAdult = logic->IsAdult; - bool pastChild = logic->IsChild; - - // First check if the check is possible as child - logic->IsChild = true; - logic->IsAdult = false; - bool result = condition(); - // If so, check again as adult. both have to be true for result to be true - if (result) { - logic->IsChild = false; - logic->IsAdult = true; - result = condition(); - } - - // set back age variables - logic->IsChild = pastChild; - logic->IsAdult = pastAdult; - return result; - } - return false; -} - void Region::printAgeTimeAccess() { auto message = "Child Day: " + std::to_string(childDay) + "\t" @@ -666,19 +576,211 @@ void Region::printAgeTimeAccess() { std::array areaTable; +/* + * This logic covers checks that exist in the shared areas of Spirit + * 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 + + * In order to pass a check, we must either determine that Access is certain, + or that it is always possible to get a check somehow. + + * 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. + + * Additionally, if it is possible to enter spirit in reverse, there are 2 more universes: + * In the third universe, adult enters in reverse, and wastes all the keys so noone can enter through the front + * In the forth, child manages to do the same, and lock people out of the front + * However all access from the boss door in Statue Room and adjacent areas is Certain, so this is not usually + relevant + + * While other universes exist, such as both ages entering in reverse or + child using their key, getting stuck, then coming back to do the dungeon as adult, these + are all sub-possibilities of these 4 universes + + * As we do not know which universe we are in until the player chooses one in-game, + we must be able to collect the check in all universes + + * When an Age can no longer be kept out in any universe, that age is said to have Certain Access to a + region + * If both ages have potential access to a region with a certain number of keys, but there is no Certain Access, + * then a check is only in logic if all possible universes can collect the check independently + + * The universes converge when the player has all the keys, giving both ages Certain Access everywhere. + + * We must check for these universes manually as we set access vairables to true with minimum keys for + * technical reasons as otherwise the logic code will never run + + * The 1st and 3rd column list how many keys are needed for each age to have Certain Access from the front + * the 2nd and 4th column list how many keys are needed for each age to have Certain Access from the boss door + * Sometimes, we may check for a higher number of keys in the condition, this happens in cases where the number of + keys + * for Certain Access depends on a certain condition, the listed number is the lowest possible to make sure the + condition is checked. + + * The 1st condition is the combined conditions needed to move from the 1F child lock to the area being checks + * the 2nd condition is the same for adult 1F lock, and the 3rd is the access from the boss door. +*/ + +// clang-format off +std::map Region::spiritLogicData = { + //Vanilla Child uses ExplosiveKeyLogic here because they need to exist for shared adult checks + {RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, {5, 0, 3, 0, []{return true;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, {5, 0, 3, 0, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_2F_MIRROR_ROOM, {5, 0, 3, 0, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritSunOnFloorToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_GS_LEDGE, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic();}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true;}}}, + //Assumes SpiritSunBlockSouthLedge() for all access + {RR_SPIRIT_TEMPLE_SUN_BLOCK_CHEST_LEDGE, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_OUTER_RIGHT_HAND, {5, 5, 3, 3, []{return logic->OuterWestHandLogic();}, []{return logic->OuterWestHandLogic();}, []{return logic->OuterWestHandLogic();}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_ADULT, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_INNER_LEFT_HAND, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT) && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));}}}, + //MQ /*&& logic->CanClimbHigh()*/ + {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, {7, 6, 7, 7, []{return logic->StatueRoomMQKeyLogic();}, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->StatueRoomMQKeyLogic() && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, {7, 6, 7, 7, []{return logic->StatueRoomMQKeyLogic() && logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->StatueRoomMQKeyLogic()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_CHILD, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;}}}, + {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, {7, 0, 0, 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_OUTER_RIGHT_HAND, {7, 7, 4, 4, []{return logic->CanHitSwitch() && logic->OuterWestHandMQLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->OuterWestHandMQLogic();}, []{return logic->OuterWestHandMQLogic();}}}, + {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_DOOR, {7, 0, 0, 0, []{return logic->CanHitSwitch() && /* logic->CanClimbHigh() &&*/ + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_DOOR].AnyAgeTime([]{return logic->MQSpiritStatueSouthDoor();});}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_DOOR].AnyAgeTime([]{return logic->MQSpiritStatueSouthDoor();});}}}, +}; +// clang-format on + +bool SpiritCertainAccess(RandomizerRegion region) { + SpiritLogicData& curRegionData = Region::spiritLogicData[region]; + if (logic->IsChild) { + uint8_t keys = curRegionData.childKeys; + uint8_t revKeys = curRegionData.childRevKeys; + bool knownFrontAccess = logic->Get(LOGIC_FORWARDS_SPIRIT_CHILD) || !logic->IsReverseAccessPossible(); + // 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 + return ((knownFrontAccess && curRegionData.childAccess()) && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, keys)) || + ((logic->Get(LOGIC_REVERSE_SPIRIT_CHILD) && curRegionData.reverseAccess()) && + logic->SmallKeys(SCENE_SPIRIT_TEMPLE, revKeys)) || + (curRegionData.childAccess() && curRegionData.reverseAccess() && + logic->SmallKeys(SCENE_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys)); + } else { + uint8_t keys = curRegionData.adultKeys; + uint8_t revKeys = curRegionData.adultRevKeys; + bool knownFrontAccess = logic->Get(LOGIC_FORWARDS_SPIRIT_ADULT) || !logic->IsReverseAccessPossible(); + // 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 + return ((knownFrontAccess && curRegionData.adultAccess()) && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, keys)) || + ((logic->Get(LOGIC_FORWARDS_SPIRIT_ADULT) && curRegionData.reverseAccess()) && + logic->SmallKeys(SCENE_SPIRIT_TEMPLE, revKeys)) || + (curRegionData.adultAccess() && curRegionData.reverseAccess() && + logic->SmallKeys(SCENE_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys)); + } +} + +/* + Spirit Shared can take up to 3 regions, this is because checks can exist in many regions at the same time + and the logic needs to be able to check the access logic from those regions to check the other universes properly. + + anyAge is equivalent to a self referencing Here, used for events and any check where that is relevent. +*/ + +bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion, + ConditionFn otherCondition, RandomizerRegion thirdRegion, ConditionFn thirdCondition) { + SpiritLogicData& curRegionData = Region::spiritLogicData[region]; + bool result = false; + + // store current age variables + bool pastAdult = logic->IsAdult; + bool pastChild = logic->IsChild; + + logic->IsChild = true; + logic->IsAdult = false; + + bool ChildCertainAccess = SpiritCertainAccess(region); + + // Switch back to adult to check adult access + logic->IsChild = false; + logic->IsAdult = true; + + bool AdultCertainAccess = SpiritCertainAccess(region); + // If we are AnyAge and have any CertainAccess, then we can check those ages + // we don't need to check ambiguity here as if this fails, then 1 of the ages has failed + if (anyAge && (ChildCertainAccess || AdultCertainAccess)) { + // set age access to the Certain Access + logic->IsChild = ChildCertainAccess; + logic->IsAdult = AdultCertainAccess; + + // check condition as well as having at least child or adult access + result = condition(); + + // otherwise, we have to check the current age and... + } else if (areaTable[region].Child() && pastChild) { + // Switch to Child + logic->IsChild = true; + logic->IsAdult = false; + + result = condition(); + // If we have Certain Access, we just run the condition. + // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Adult + // and if needed, in reverse + if (!ChildCertainAccess && result) { + // Switch to Adult + 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 && + (Region::spiritLogicData[otherRegion].adultAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && + otherCondition())) || + (thirdRegion != RR_NONE && + (Region::spiritLogicData[thirdRegion].adultAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && + thirdCondition())); + } + } else if (areaTable[region].Adult() && pastAdult) { + 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 + // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Child + // and if needed, in reverse + if (!AdultCertainAccess && result) { + // Switch to Child + 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 && + (Region::spiritLogicData[otherRegion].childAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && + otherCondition())) || + (thirdRegion != RR_NONE && + (Region::spiritLogicData[thirdRegion].childAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && + thirdCondition())); + } + } + // set back age variables + logic->IsChild = pastChild; + logic->IsAdult = pastAdult; + return result; +} + bool AnyAgeTime(ConditionFn condition) { assert(logic->CurrentRegionKey != RR_NONE); return areaTable[logic->CurrentRegionKey].AnyAgeTime(condition); } -bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].MQSpiritShared(condition, false, anyAge); -} - -bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].MQSpiritShared(condition, true, anyAge); -} - bool BeanPlanted(const RandomizerGet bean) { auto logic = Rando::Context::GetInstance()->GetLogic(); // flag irrelevant if plant won't spawn diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 4bb1db613..82c954319 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -8,6 +8,7 @@ #include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/randomizer/SeedContext.h" #include "soh/Enhancements/randomizer/logic.h" +#include "soh/Enhancements/randomizer/dungeon.h" #define TIME_PASSES true #define TIME_DOESNT_PASS false @@ -101,6 +102,27 @@ class Entrance; enum class EntranceType; } // namespace Rando +struct SpiritLogicData { + // the minimum number of keys that guarantees Child can reach this region + uint8_t childKeys; + // The minimum number of keys that guarantees Child can reach this region if they have reverse access + uint8_t childRevKeys; + // the minimum number of keys that guarantees Adult can reach this region + uint8_t adultKeys; + // the minimum number of keys that guarantees Adult can reach this region with reverse entry + uint8_t adultRevKeys; + // 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(); @@ -211,19 +233,22 @@ class Region { bool CanPlantBeanCheck(RandomizerGet bean) const; bool AllAccountedFor() const; - bool MQSpiritShared(ConditionFn condition, bool IsBrokenWall, bool anyAge = false); void ResetVariables(); void printAgeTimeAccess(); + static std::map spiritLogicData; }; extern std::array areaTable; extern std::vector grottoEvents; bool AnyAgeTime(ConditionFn condition); -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; }, RandomizerRegion thirdRegion = RR_NONE, + ConditionFn thirdCondition = [] { return false; }); +bool SpiritCertainAccess(RandomizerRegion region); bool CanPlantBean(const RandomizerRegion region, RandomizerGet bean); 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 933d12b1a..363b52af1 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -9,150 +9,548 @@ void RegionTable_Init_SpiritTemple() { // Vanilla/MQ Decider areaTable[RR_SPIRIT_TEMPLE_ENTRYWAY] = Region("Spirit Temple Entryway", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla();}), - Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ();}), + Entrance(RR_SPIRIT_TEMPLE_FOYER, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla();}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FOYER, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ();}), Entrance(RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, []{return true;}), }); #pragma region Vanilla - areaTable[RR_SPIRIT_TEMPLE_LOBBY] = Region("Spirit Temple Lobby", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_FOYER] = Region("Spirit Temple Foyer", SCENE_SPIRIT_TEMPLE, { + // Events + //WARNING these events assume you need less or equal keys for forwards entry and reverse + EventAccess(LOGIC_FORWARDS_SPIRIT_CHILD, []{return logic->IsChild;}), + EventAccess(LOGIC_FORWARDS_SPIRIT_ADULT, []{return logic->IsAdult;}), + }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_2, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_EARLY_ADULT, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), + Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_SIDE_HUB, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_SIDE_HUB, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD] = Region("Child Spirit Temple", SCENE_SPIRIT_TEMPLE, { + areaTable[RR_SPIRIT_TEMPLE_CHILD_SIDE_HUB] = Region("Spirit Temple Child Side Hub", SCENE_SPIRIT_TEMPLE, { //Events EventAccess(LOGIC_NUT_ACCESS, []{return logic->CanBreakSmallCrates();}), + }, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_FOYER, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_BOXES, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), + //Implies logic->CanKillEnemy(RE_KEESE) + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_ARMOS);});}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_ARMOS);});}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH] = Region("Spirit Temple Switch Bridge South", SCENE_SPIRIT_TEMPLE, { + //Events + //RANDOTODO a version of CanHitSwitch that takes WallOrFloor + EventAccess(LOGIC_SPIRIT_CHILD_SWITCH_BRIDGE, []{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_SIDE_HUB, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return (logic->Get(LOGIC_SPIRIT_CHILD_SWITCH_BRIDGE) && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || + logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT)/* || (logic->IsAdult && CanBunnyJump())*/;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH] = Region("Spirit Temple Switch Bridge North", SCENE_SPIRIT_TEMPLE, { + //Events + EventAccess(LOGIC_SPIRIT_CHILD_SWITCH_BRIDGE, []{return logic->CanHitSwitch();}), }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_EARLY_TORCHES_CHEST, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT)))) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_DINS_FIRE))), - LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_1, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_2, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_3, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_4, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST, true), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return logic->CanUse(RG_HOVER_BOOTS) || (logic->Get(LOGIC_SPIRIT_CHILD_SWITCH_BRIDGE) && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false));}), + Entrance(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_1F_ANUBIS] = Region("Spirit Temple 1F Anubis", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_1, true), + LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_2, true), + LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_3, true), + LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_4, true), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return AnyAgeTime([]{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);});}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, []{return AnyAgeTime([]{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);});}), + }); + + areaTable[RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH] = Region("Spirit Temple Rupee Bridge North", SCENE_SPIRIT_TEMPLE, { + //Events + EventAccess(LOGIC_SPIRIT_SILVER_RUPEE_BRIDGE, []{return true/*CanClimb()*/;}), + }, { + //Locations + // 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->Get(LOGIC_SPIRIT_SILVER_RUPEE_BRIDGE) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return logic->Get(LOGIC_SPIRIT_SILVER_RUPEE_BRIDGE);}), + Entrance(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH] = Region("Spirit Temple Rupee Bridge South", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_CHILD_SIDE_HUB, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, []{return logic->Get(LOGIC_SPIRIT_SILVER_RUPEE_BRIDGE);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_CHILD_BOXES] = Region("Child Spirit Temple Before Climb", SCENE_SPIRIT_TEMPLE, {}, { + //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_CLIMB, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 1);}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_SIDE_HUB, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 1);}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_CLIMB] = Region("Child Spirit Temple Climb", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F] = Region("Spirit Temple Sun On Floor 1F", SCENE_SPIRIT_TEMPLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, logic->HasProjectile(HasProjectileAge::Both) || (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->HasProjectile(HasProjectileAge::Adult)) || (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5) && logic->IsChild && logic->HasProjectile(HasProjectileAge::Child))), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, logic->HasProjectile(HasProjectileAge::Both) || (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->HasProjectile(HasProjectileAge::Adult)) || (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5) && logic->IsChild && logic->HasProjectile(HasProjectileAge::Child))), - LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, logic->HasProjectile(HasProjectileAge::Both) || logic->CanUse(RG_DINS_FIRE) || - (logic->TakeDamage() && (logic->CanJumpslashExceptHammer() || logic->HasProjectile(HasProjectileAge::Child))) || - (logic->IsChild && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5) && logic->HasProjectile(HasProjectileAge::Child)) || - (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->HasProjectile(HasProjectileAge::Adult) || (logic->TakeDamage() && logic->CanJumpslashExceptHammer())))), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);}, false, + RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_BOXES, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return true/*CanClimbHigh()*/;}), }); - areaTable[RR_SPIRIT_TEMPLE_EARLY_ADULT] = Region("Early Adult Spirit Temple", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F] = Region("Spirit Temple Sun On Floor 2F", SCENE_SPIRIT_TEMPLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_COMPASS_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_ZELDAS_LULLABY)), - LOCATION(RC_SPIRIT_TEMPLE_EARLY_ADULT_RIGHT_CHEST, (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))) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslashExceptHammer())), - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3)), - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3)), - LOCATION(RC_SPIRIT_TEMPLE_GS_BOULDER_ROOM, logic->CanUse(RG_SONG_OF_TIME) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH)))), - LOCATION(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG) && (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))) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash())), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);}, false, + RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 1);}), + Entrance(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, []{return true;}), + ///*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS))) + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->SpiritSunOnFloorToStatue();}), }); - areaTable[RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER] = Region("Spirit Temple Central Chamber", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_ADULT_SIDE_HUB] = Region("Spirit Temple Adult Side Hub", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_FOYER, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SAND_PIT, []{return AnyAgeTime([]{return logic->CanHitSwitch(logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH) ? ED_BOMB_THROW : ED_BOOMERANG);});}), + Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return AnyAgeTime([]{return logic->CanHitSwitch(logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH) ? ED_BOMB_THROW : ED_BOOMERANG);});}), + Entrance(RR_SPIRIT_TEMPLE_1F_MIRROR_ROOM, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 1);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SAND_PIT] = Region("Spirit Temple Sand Pit", SCENE_SPIRIT_TEMPLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, ((logic->HasExplosives() || logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)) && - (logic->CanUse(RG_DINS_FIRE) || ((logic->CanUse(RG_FIRE_ARROWS) || ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST)) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_STICKS) ))) || - (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_STICKS)) || - (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3) && (logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW))) && logic->CanUse(RG_SILVER_GAUNTLETS))), - LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, ((logic->HasExplosives() || logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)) && - (logic->CanUse(RG_DINS_FIRE) || ((logic->CanUse(RG_FIRE_ARROWS) || ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST)) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_STICKS) ))) || - (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_STICKS)) || - (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3) && (logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))) && logic->CanUse(RG_SILVER_GAUNTLETS))), - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_ZELDAS_LULLABY)), - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_ZELDAS_LULLABY) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))), - 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(SCENE_SPIRIT_TEMPLE, 5) && logic->HasExplosives()) || - (logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2))), - LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, ((logic->HasExplosives() || logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)) && - ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) && logic->CanUse(RG_BOOMERANG) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) || - (ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_BOOMERANG)) || - (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)))), - LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_1, logic->CanBreakPots() && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)), - LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_2, logic->CanBreakPots() && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, logic->CanBreakPots() && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, logic->CanBreakPots() && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, logic->CanBreakPots() && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_4, logic->CanBreakPots() && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_5, logic->CanBreakPots() && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, logic->CanBreakPots() && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2)), + LOCATION(RC_SPIRIT_TEMPLE_COMPASS_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_ZELDAS_LULLABY)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_OUTDOOR_HANDS, []{return logic->CanJumpslashExceptHammer() || logic->HasExplosives();}), - Entrance(RR_SPIRIT_TEMPLE_BEYOND_CENTRAL_LOCKED_DOOR, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4) && logic->CanUse(RG_SILVER_GAUNTLETS);}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_SIDE_HUB, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_ABOVE_BOULDERS] = Region("Spirit Temple Above Boulders", SCENE_SPIRIT_TEMPLE, { + //Events + //Jumpslash 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 to land on the middle of the boulder + //to get enough distance to reach the rupee + EventAccess(LOGIC_SPIRIT_BOUNDERS_SILVERS, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT)/* || CanBunnyHop()*/;}), + }, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_ADULT_SIDE_HUB, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BOULDERS] = Region("Spirit Temple Boulders", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_GS_BOULDER_ROOM, logic->CanUse(RG_SONG_OF_TIME) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, []{return true/*CanClimb() || IsAdult() || CanGroundJump*/;}), + Entrance(RR_SPIRIT_TEMPLE_PAST_BOULDERS, []{return logic->Get(LOGIC_SPIRIT_BOUNDERS_SILVERS);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_PAST_BOULDERS] = Region("Spirit Temple Past Boulders", SCENE_SPIRIT_TEMPLE, {}, { + //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_BOULDERS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_1F_MIRROR_ROOM] = Region("Spirit Temple 1F Mirror Room", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_ADULT_SIDE_HUB, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR_ROOM, []{return true/*CanClimbHigh()*/;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_2F_MIRROR_ROOM] = Region("Spirit Temple 2F Mirror Room", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR_ROOM, []{return true/*str0 || SunlightArrows()*/;})), + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR_ROOM, []{return true/*str0 || SunlightArrows()*/;})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_1F_MIRROR_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_ADULT, []{return true/*str0 || SunlightArrows()*/;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD] = Region("Spirit Temple Statue Rooom Child", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM access + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));}, false, + RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource();})), + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT);}, false, + RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) ? ED_BOOMERANG : ED_HOOKSHOT);}, + RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SCARECROW);}), // 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->Get(LOGIC_SPIRIT_PLATFORM_LOWERED) && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), + Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true/*str0*/;}), + //!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) && /*str0 &&*/ + logic->IsChild/*CanUse(RG_CRAWL)*/ && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4) && logic->CanKillEnemy(RE_IRON_KNUCKLE);}), }); - areaTable[RR_SPIRIT_TEMPLE_OUTDOOR_HANDS] = Region("Spirit Temple Outdoor Hands", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_INNER_WEST_HAND] = Region("Spirit Temple Inner West Hand", SCENE_SPIRIT_TEMPLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, (logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_LONGSHOT) && logic->HasExplosives()) || logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5)), - LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->HasExplosives()), + //You can hit the skull with normal hookshot from a specific spot on the forearm, which is in logic on N64 and 3DS, pretty questionable though + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{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);}, + RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT);})), }, { //Exits Entrance(RR_DESERT_COLOSSUS, []{return (logic->IsChild && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5)) || (logic->CanUse(RG_SILVER_GAUNTLETS) && ((logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3) && logic->HasExplosives()) || logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5)));}), }); - areaTable[RR_SPIRIT_TEMPLE_BEYOND_CENTRAL_LOCKED_DOOR] = Region("Spirit Temple Beyond Central Locked Door", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_GS_LEDGE] = Region("Spirit Temple GS ledge", SCENE_SPIRIT_TEMPLE, {}, { //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_HALLWAY_LEFT_INVISIBLE_CHEST, (ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasExplosives()), - LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_RIGHT_INVISIBLE_CHEST, (ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasExplosives()), - LOCATION(RC_SPIRIT_TEMPLE_BEAMOS_HALL_POT_1, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, logic->HasExplosives() && logic->CanUse(RG_SUNS_SONG)), + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);}, false, + RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) ? ED_BOOMERANG : ED_HOOKSHOT);}, + RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT);})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BEYOND_FINAL_LOCKED_DOOR, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5) && (ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || ((logic->CanUse(RG_BOMB_BAG) || logic->CanUse(RG_NUTS) || logic->CanUse(RG_DINS_FIRE)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER))));}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT)/* || CanBunnyJump()*/;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return true;}) }); - areaTable[RR_SPIRIT_TEMPLE_BEYOND_FINAL_LOCKED_DOOR] = Region("Spirit Temple Beyond Final Locked Door", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM] = Region("Spirit Temple Statue Room", SCENE_SPIRIT_TEMPLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, logic->CanUse(RG_ZELDAS_LULLABY) && ((logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) || (logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_HOOKSHOT)))), - LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) && (logic->CanJumpslash() || logic->HasExplosives() || logic->CanUse(RG_GIANTS_KNIFE) || (ctx->GetTrickOption(RT_HOOKSHOT_EXTENSION) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_HOOKSHOT))))) || - (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))), - LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_LEFT_HEART, logic->CanUse(RG_HOOKSHOT)), - LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_RIGHT_HEART, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource();}, false, + RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, []{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_INSIDE_STATUE_HEAD, []{return logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, []{return true/*CanClimb()*/;}), + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->Get(LOGIC_SPIRIT_PLATFORM_LOWERED) && + (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_ADULT, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_SHORTCUT, []{return logic->Get(LOGIC_SPIRIT_STATUE_SOUTH_DOOR);}), + //!QUANTUM LOGIC! + //If dungeon entrance randomiser is off, Adult entered spirit in reverse, and they have str 2 to get there from the front and Explosives, + //it is always possible for them to reach 1F_EAST with only 2 keys. This is because you can only waste 1 key (on the first child side lock) + //before you either allow you to climb down through 2F mirror room, or give yourself access to a hand to jump down from. + //If for whatever reason you can reach east hand but not west hand, this becomes possible with 3 keys instead. + //If you do not have explosives to kill Beamos, but do have a way to defeat Iron Knuckles, this becomes possible with 4 keys instead. + Entrance(RR_SPIRIT_TEMPLE_ADULT_SIDE_HUB, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && + logic->CanUse(RG_SILVER_GAUNTLETS) && logic->IsAdult && + ((logic->CanKillEnemy(RE_BEAMOS) && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, /*CanClimb() && str0 ?*/ 2 /*: 3*/)) || + (/*CanClimb() && str0 && */logic->CanKillEnemy(RE_IRON_KNUCKLE) && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4)));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_EMPTY_STAIRS] = Region("Spirit Temple Empty Stairs", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", SCENE_SPIRIT_TEMPLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM] = Region("Spirit Temple Sun Block Room", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true/*str0 || SunlightArrows*/;}), + //The blocks can be used to get onto this ledge itemless + //true/*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() || + //((HasFireSource() || (SpiritSunBlockTorch && (logic->CanUse(STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))) && CanUse(RG_HOOKSHOT))*/ + Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_CHEST_LEDGE, []{return logic->SpiritSunBlockSouthLedge();}), + Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return true/*str0 || SunlightArrows*/;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SUN_BLOCK_CHEST_LEDGE] = Region("Spirit Temple Sun Block Chest ledge", SCENE_SPIRIT_TEMPLE, { + //Events + //Assumes RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM access + EventAccess(LOGIC_SPIRIT_SUN_BLOCK_TORCH, []{return SpiritShared(RR_SPIRIT_TEMPLE_SUN_BLOCK_CHEST_LEDGE, []{return true;}, true);}), + }, { + //Locations + //Spawning the chest to get here is accounted for in movement logic, so we only need to confirm it can be spawned here + LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_BLOCK_CHEST_LEDGE, + []{return logic->HasFireSource() || + (logic->Get(LOGIC_SPIRIT_SUN_BLOCK_TORCH) && //Possible without str0 by timing a shot during a backflip from the ledge, but it's probably a separate trick + (logic->CanUse(RG_STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW)/* && str0*/)));})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS] = Region("Spirit Temple Skulltula Stairs", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + 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_SUN_BLOCK_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_THRONE, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_CHILD_THRONE] = Region("Spirit Temple Child Throne", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 3);}), + Entrance(RR_SPIRIT_TEMPLE_RIGHT_HAND_EXIT, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + }); + + areaTable[RR_SPIRIT_TEMPLE_RIGHT_HAND_EXIT] = Region("Spirit Temple Right Hand Exit", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_CHILD_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_OUTER_RIGHT_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_OUTER_RIGHT_HAND] = Region("Spirit Temple Outer Right Hand", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_OUTER_RIGHT_HAND, []{return true;})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_RIGHT_HAND_EXIT, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return SpiritCertainAccess(RR_SPIRIT_TEMPLE_OUTER_RIGHT_HAND);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_ADULT] = Region("Spirit Temple Statue Room Adult", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_LEFT_HAND, []{return true;}), + //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_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(SCENE_SPIRIT_TEMPLE, 4);}), + //RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->Get(LOGIC_SPIRIT_PLATFORM_LOWERED) && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_INNER_LEFT_HAND] = Region("Spirit Temple Inner Left Hand", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + //Assumes RR_SPIRIT_TEMPLE_INNER_WEST_HAND access via RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD and RR_SPIRIT_TEMPLE_STATUE_ROOM. + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_INNER_LEFT_HAND, []{return logic->CanUse(RG_ZELDAS_LULLABY)/* && CanClimb()*/;})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_ADULT, []{return logic->CanUse(RG_HOVER_BOOTS)/* || CanBunnyJump()*/;}), + //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_ADULT access via RR_SPIRIT_TEMPLE_STATUE_ROOM + Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || + (logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanUse(RG_HOOKSHOT));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH] = Region("Spirit Temple Shortcut Switch", SCENE_SPIRIT_TEMPLE, { + //Events + EventAccess(LOGIC_SPIRIT_STATUE_SOUTH_DOOR, []{return SpiritShared(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->CanUse(RG_MEGATON_HAMMER);});}), + }, { + //Locations + //Assumes RR_SPIRIT_TEMPLE_INNER_LEFT_HAND access + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->CanUse(RG_ZELDAS_LULLABY);})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_ADULT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_LEFT_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", SCENE_SPIRIT_TEMPLE, {}, {}, { + //If child can ever use silver gauntlets, 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_FOYER, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_MEGATON_HAMMER);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_POT_STAIRS] = Region("Spirit Temple Pot Stairs", SCENE_SPIRIT_TEMPLE, {}, { + LOCATION(RC_SPIRIT_TEMPLE_BEAMOS_HALL_POT_1, logic->CanBreakPots()), + }, { + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_ADULT, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4);}), + Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_4_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_BASE, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_4_ARMOS] = Region("Spirit Temple 4 Armos", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || logic->SunlightArrows()) && 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_4_ARMOS_SIDE_ROOM, []{return logic->CanUse(RG_MIRROR_SHIELD) || logic->SunlightArrows();}), + Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_4_ARMOS_SIDE_ROOM] = Region("Spirit Temple 4 Armos Side Room", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, true), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_4_ARMOS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_CHEST_STAIRS] = Region("Spirit Temple Chest Stairs", SCENE_SPIRIT_TEMPLE, {}, { + //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_4_ARMOS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_THRONE, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_ADULT_THRONE] = Region("Spirit Temple Adult Throne", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_LEFT_HAND_EXIT, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + }); + + areaTable[RR_SPIRIT_TEMPLE_LEFT_HAND_EXIT] = Region("Spirit Temple Left Hand Exit", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_ADULT_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_OUTER_LEFT_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_OUTER_LEFT_HAND] = Region("Spirit Temple Outer Left Hand", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_LEFT_HAND_EXIT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_OUTER_RIGHT_HAND, []{return logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_BASE] = Region("Spirit Temple Big Wall Base", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return (ctx->GetTrickOption(RT_SPIRIT_WALL) || + (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && logic->CanPassEnemy(RE_WALLTULA, ED_BOOMERANG))) + /*&& CanClimbHigh()*/;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_UPPER] = Region("Spirit Temple Big Wall Upper", SCENE_SPIRIT_TEMPLE, {}, { + //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)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_BASE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_4F_CENTRAL] = Region("Spirit Temple 4F Central", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return true;}), + 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_FAKE_DOORS_ROOM] = Region("Spirit Temple Fake Doors Room", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, (logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) || + (AnyAgeTime([]{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", SCENE_SPIRIT_TEMPLE, { + //Events + EventAccess(LOGIC_SPIRIT_4F_SWITCH, []{return logic->CanJumpslash() || logic->HasExplosives() || logic->CanUse(RG_GIANTS_KNIFE) || + (ctx->GetTrickOption(RT_HOOKSHOT_EXTENSION) && ((logic->IsAdult && logic->CanUse(RG_HOOKSHOT)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT)));}), + EventAccess(LOGIC_SPIRIT_PLATFORM_LOWERED, []{return (logic->Get(LOGIC_SPIRIT_PUSHED_4F_MIRRORS) && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows();}), + + }, {}, { + Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), + //Rang can hit the switch on the way back but that's a trick + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, []{return logic->Get(LOGIC_SPIRIT_4F_SWITCH);}), + //Assumes RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE access + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->Get(LOGIC_SPIRIT_PLATFORM_LOWERED);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE] = Region("Spirit Temple Big Mirror Cave", SCENE_SPIRIT_TEMPLE, { + //Events + EventAccess(LOGIC_SPIRIT_4F_SWITCH, []{return logic->HasExplosives();}), + //WARNING: not currently a permanent flag, needs to be one for doorsanity + EventAccess(LOGIC_SPIRIT_PUSHED_4F_MIRRORS, []{return logic->HasExplosives()/*&& str0*/;}), + }, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, (logic->IsAdult && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows()), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), + }); + + //Assumes a SpiritPlatformLowered check on entry + areaTable[RR_SPIRIT_TEMPLE_PLATFORM] = Region("Spirit Temple Lowered Platform", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_ADULT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_LEFT_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return logic->Get(LOGIC_SPIRIT_PUSHED_4F_MIRRORS) && logic->CanUse(RG_MIRROR_SHIELD) && logic->CanUse(RG_HOOKSHOT);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_STATUE_HEAD] = Region("Spirit Temple Statue Head", SCENE_SPIRIT_TEMPLE, { + //Events + //WARNING these events are not glitchproofed and assume you need all keys to reach from the front + EventAccess(LOGIC_REVERSE_SPIRIT_CHILD, []{return logic->IsChild;}), + EventAccess(LOGIC_REVERSE_SPIRIT_ADULT, []{return logic->IsAdult;}), + }, {}, { // Exits - Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + //CanBunnyJump with a jumpslash can reach either hand and with good timing the platform as child. the latter is definitely a trick, the former may not be + //If this interaction with the hands is added, SpiritSharedLogic needs updating for it + Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_INNER_LEFT_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->Get(LOGIC_SPIRIT_PLATFORM_LOWERED) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT)/* || (IsAdult && CanBunnyJump())*/);}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion #pragma region MQ - areaTable[RR_SPIRIT_TEMPLE_MQ_LOBBY] = Region("Spirit Temple MQ Lobby", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_FOYER] = Region("Spirit Temple MQ Lobby", SCENE_SPIRIT_TEMPLE, { + // Events + //WARNING these events assume you need less or equal keys for forwards entry and reverse + EventAccess(LOGIC_FORWARDS_SPIRIT_CHILD, []{return logic->IsChild;}), + EventAccess(LOGIC_FORWARDS_SPIRIT_ADULT, []{return logic->IsAdult;}), + }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_FRONT_LEFT_CHEST, true), LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_BACK_LEFT_CHEST, AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets()), @@ -164,12 +562,13 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_POT_4, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH, []{return logic->CanUse(RG_LONGSHOT) && logic->CanUse(RG_BOMBCHU_5);}), + Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB, []{return logic->IsChild/*logic->CanUse(RG_CRAWL) && logic->HasSoul(RG_NABOORU_SOUL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEHIND_GEYSER, []{return false;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_HOLE, []{return logic->CanUse(RG_LONGSHOT) && logic->CanUse(RG_BOMBCHU_5);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_1F_WEST] = Region("Spirit Temple MQ 1F West", SCENE_SPIRIT_TEMPLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB] = Region("Spirit Temple MQ Child Side Hub", SCENE_SPIRIT_TEMPLE, { //Events //not technically a rusted switch, but a boulder through a wall, but is part of the same trick on N64 EventAccess(LOGIC_SPIRIT_MQ_CRAWL_BOULDER, []{return logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && logic->CanUse(RG_MEGATON_HAMMER));}), @@ -181,29 +580,40 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_RIGHT_HEART, logic->CanHitEyeTargets()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_TORCH_SLUG);});}), - Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_TORCH_SLUG);});}), - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, []{return logic->IsChild && logic->Get(LOGIC_SPIRIT_MQ_CRAWL_BOULDER);}), + //Nabooru's legs are technically visible one way collision here, but I'm not sure if this counts + Entrance(RR_SPIRIT_TEMPLE_MQ_FOYER, []{return logic->IsChild/*logic->CanUse(RG_CRAWL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_GIBDO_GRAVES, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_TORCH_SLUG);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_ANUBIS_BRIDGE_CHEST, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_TORCH_SLUG);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_1F_CHEST_SWITCH, []{return logic->IsChild/*logic->CanUse(RG_CRAWL)*/ && logic->Get(LOGIC_SPIRIT_MQ_CRAWL_BOULDER);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH] = Region("Spirit Temple MQ 1F Gibdo Room South", SCENE_SPIRIT_TEMPLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_GIBDO_GRAVES] = Region("Spirit Temple MQ Gibdo Graves", SCENE_SPIRIT_TEMPLE, { + //Events + EventAccess(LOGIC_SPIRIT_MQ_GIBDOS_CLEARED, []{return /*str0 && */ + (logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets()) || + logic->CanUse(RG_HOVER_BOOTS)/* || (IsAdult && CanBunnyJump())*/ + && logic->CanKillEnemy(RE_GIBDO, ED_CLOSE, true, 3) /* && str0*/;}), + }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets();}), - Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets() && logic->CanKillEnemy(RE_GIBDO);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_GIBDO_POTS, []{return /*str0 && */ + (logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets()) || + logic->CanUse(RG_HOVER_BOOTS)/* || (IsAdult && CanBunnyJump())*/;}), }); - // Room to store the 2 pots in to handle glitch logic going backwards around the loop later - areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ Gibdo Room North", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_GIBDO_POTS] = Region("Spirit Temple MQ Gibdo Pots", SCENE_SPIRIT_TEMPLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_2, logic->CanBreakPots()), - }, {}); + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE, []{return logic->Get(LOGIC_SPIRIT_MQ_GIBDOS_CLEARED);}), + }); - areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", SCENE_SPIRIT_TEMPLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE] = Region("Spirit Temple MQ Turntable", SCENE_SPIRIT_TEMPLE, { //Events - //For non-fairy pot items, you can also get them with rang without killing the stalfos - EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CanBreakPots() && AnyAgeTime([]{return logic->CanKillEnemy(RE_STALFOS);});}), + EventAccess(LOGIC_SPIRIT_MQ_TURNTABLE_ENEMY, []{return logic->CanKillEnemy(RE_STALFOS);}), + EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->Get(LOGIC_SPIRIT_MQ_TURNTABLE_ENEMY);}), }, { //Locations //implies logic->CanBreakPots() @@ -213,23 +623,30 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_STALFOS_POT_4, logic->CanUse(RG_BOOMERANG) || logic->CanKillEnemy(RE_STALFOS)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_STALFOS);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_GIBDO_POTS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_BEHIND_FIRE, []{return logic->Get(LOGIC_SPIRIT_MQ_TURNTABLE_ENEMY);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH] = Region("Spirit Temple MQ Map Room North", SCENE_SPIRIT_TEMPLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_BEHIND_FIRE] = Region("Spirit Temple MQ Turntable Behind Fire", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE, []{return logic->Get(LOGIC_SPIRIT_MQ_TURNTABLE_ENEMY);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_ANUBIS_BRIDGE_GRAVE, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_ANUBIS_BRIDGE_GRAVE] = Region("Spirit Temple MQ Anubis Bridge Grave", SCENE_SPIRIT_TEMPLE, { //Events - EventAccess(LOGIC_SPIRIT_MQ_MAP_ROOM_ENEMIES, []{return logic->CanKillEnemy(RE_ANUBIS) && logic->CanKillEnemy(RE_KEESE);}), + EventAccess(LOGIC_SPIRIT_MQ_MAP_ROOM_ENEMIES, []{return /*(*/logic->CanKillEnemy(RE_ANUBIS) && logic->CanKillEnemy(RE_KEESE)/*) && (str0 || CanUse(RG_HOVER_BOOTS) || CanUse(RG_HOOKSHOT) || (logic->IsAdult && CanBunnyJump())) || + logic->CanKillEnemy(RE_ANUBIS, ED_BOOMERANG) && logic->CanKillEnemy(RE_KEESE, ED_FAR)*/;}), }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_MAP_ROOM_ENEMY_CHEST, logic->Get(LOGIC_SPIRIT_MQ_MAP_ROOM_ENEMIES)), }, { //Exits - //Stalfos room blocks you in with fire until you kill the stalfos, which won't spawn from behind the fire - Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_ANUBIS_BRIDGE_CHEST, []{return true/*str0 || CanUse(RG_HOVER_BOOTS) || CanUse(RG_HOOKSHOT) || (logic->IsAdult && CanBunnyJump())*/;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH] = Region("Spirit Temple MQ Map Room South", SCENE_SPIRIT_TEMPLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_ANUBIS_BRIDGE_CHEST] = Region("Spirit Temple MQ Anubis Bridge Chest", SCENE_SPIRIT_TEMPLE, { //Events //You can lure the keese over by aggroing them with dins if you use it as close to the torch keese as possible, but it's a trick as it's not intuitive and basically never comes up EventAccess(LOGIC_SPIRIT_MQ_MAP_ROOM_ENEMIES, []{return logic->CanKillEnemy(RE_ANUBIS) && logic->CanKillEnemy(RE_KEESE, ED_BOOMERANG);}), @@ -239,156 +656,261 @@ 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_1F_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_ANUBIS_BRIDGE_GRAVE, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS)/* || (logic->IsAdult && CanBunnyJump())*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH] = Region("Spirit Temple MQ West 1F Rusted Switch", SCENE_SPIRIT_TEMPLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_1F_CHEST_SWITCH] = Region("Spirit Temple MQ West 1F Rusted Switch", SCENE_SPIRIT_TEMPLE, { //Events EventAccess(LOGIC_SPIRIT_MQ_TIME_TRAVEL_CHEST, []{return logic->CanUse(RG_MEGATON_HAMMER);}), EventAccess(LOGIC_SPIRIT_MQ_CRAWL_BOULDER, []{return logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && logic->CanUse(RG_MEGATON_HAMMER));}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return logic->IsChild && logic->Get(LOGIC_SPIRIT_MQ_CRAWL_BOULDER);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB, []{return logic->IsChild/*logic->CanUse(RG_CRAWL)*/ && logic->Get(LOGIC_SPIRIT_MQ_CRAWL_BOULDER);}), //This tracks possible child access, if adult has not entered STATUE_ROOM. Certain Child Access is checked for separately as 7 Keys Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 1);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE] = Region("Spirit Temple MQ Under Like Like", SCENE_SPIRIT_TEMPLE, {}, { //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 - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanHitSwitch();}), + //This covers adult and reverse access only, as child going forwards arrives here from the other side of this door + Entrance(RR_SPIRIT_TEMPLE_MQ_1F_CHEST_SWITCH, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return logic->CanHitSwitch()/* && CanClimbHigh()*/;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM] = Region("Spirit Temple MQ Broken Wall Room", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR] = Region("Spirit Temple MQ Sun on Floor Room", SCENE_SPIRIT_TEMPLE, {}, { //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_SUN_ON_FLOOR, []{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_SUN_ON_FLOOR, []{return (logic->HasExplosives() || logic->SunlightArrows()) && logic->CanUse(RG_HOOKSHOT);})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanHitSwitch();}), - //This exit only governs child possible access, adult access starts on the other side so never checks this - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanHitSwitch();}), + //This exit only governs child forwards access, adult and reverse access starts on the other side so never checks this + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_CHILD, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 2);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM] = Region("Spirit Temple MQ Statue Room", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_CHILD] = Region("Spirit Temple MQ Statue Room Child", SCENE_SPIRIT_TEMPLE, {}, { //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_SMALL_CRATE, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_CHILD, []{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(SCENE_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 AnyAgeTime([]{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_SUN_BLOCK_ROOM, []{return logic->IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || logic->CanUse(RG_SONG_OF_TIME);}), + //!QUANTUM LOGIC! + //If we entered in reverse and dungeon entrances are off, we only need 6 keys, access to Gauntlets Hand, and the ability to + //crawl through the boulder filled tunnel to reach colossus. + //This is because with 6 keys it becomes impossible to avoid opening either the west hand lock or the first child side lock + //and either direction lets child reach colossus. CanHitSwitch and CanKillEnemy(RE_IRON_KNUCKLE) is implied. + //Logic can then allow child back into spirit, putting 1F west in logic with only 6 keys without forwards entry + Entrance(RR_DESERT_COLOSSUS, []{return logic->IsChild/*CanUse(RG_CRAWL)*/ && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && + logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 6) && logic->MQSpiritStatueToSunBlock() && + (logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && AnyAgeTime([]{return logic->CanUse(RG_MEGATON_HAMMER);})));}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 6);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)/* || CanBunnyJump()*/;}), + //(IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME) || CanBunnyJump()) && str0 + 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);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_ADULT, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_POT_LEDGE] = Region("Spirit Temple MQ Pot Ledge", SCENE_SPIRIT_TEMPLE, { + //Events + //This event does not need handling in SpiritShared as it only affects navigation, adult access here is always Certain, and Child has no way through that Adult does not. + EventAccess(LOGIC_SPIRIT_STATUE_SOUTH_DOOR, []{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_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanBreakPots();}, false, + RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, []{return logic->CanUse(RG_BOOMERANG);})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanBreakPots();}, false, + RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, []{return ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) && logic->CanUse(RG_BOOMERANG);})), + }, { + //Exits + //This is pretty tight to reach the SoT block, but you can just go via the hand... + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_CHILD, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND] = Region("Spirit Temple MQ Inner Right Hand", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, []{return logic->CanUse(RG_BOOMERANG);}, false, + RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, []{return logic->CanUse(RG_BOOMERANG) && ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE);}, false, + RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanBreakPots();})), + }, { + //Exits + //Land on the SoT block + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_CHILD, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP);}), + // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled + Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->Get(LOGIC_SPIRIT_PLATFORM_LOWERED) && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM] = Region("Spirit Temple MQ Statue Room", SCENE_SPIRIT_TEMPLE, { + //Events + //This event does not need handling in SpiritShared as it only affects navigation, Adult access here is always Certain, and Child has no way through that adult does not. + EventAccess(LOGIC_SPIRIT_STATUE_SOUTH_DOOR, []{return logic->HasFireSource();}), + //Assuming all higher areas filter down to here for this despite there being many good angles to use FAs + EventAccess(LOGIC_SPIRIT_MQ_STATUE_ROOM_TORCHES, []{return logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_MQ_LOWER_ADULT) && logic->CanUse(RG_DINS_FIRE));}), + }, { + //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_CHILD, []{return true/*logic->CanClimb()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_DOOR, []{return logic->Get(LOGIC_SPIRIT_STATUE_SOUTH_DOOR);}), + //explicit adult check here is a precaution against possible Child logic leaking, Child with a hookshot can do this + //It's possible to climb the legs of the statue to line up a longshot hit, but you can just go via West + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_ADULT, []{return logic->IsAdult && logic->CanUse(RG_SCARECROW);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS] = Region("Spirit Temple MQ Flamethrower Stairs", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM] = Region("Spirit Temple MQ Sun Block Room", SCENE_SPIRIT_TEMPLE, {}, { //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, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true/*str0*/;})), + //RT_SPIRIT_MQ_SUN_BLOCK_GS should probably be expanded to cover all ground based methods when str0 is added, as it can be hit with longshot because the skull hitbox is larger than the model + LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return (logic->CanUse(RG_HOOKSHOT)/* && (str0 || SunlightArrows())*/) || + (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), - //This door causes the Universes to merge as it requires 7 keys for both ages - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true/*str0 || SunlightArrows()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, []{return true/*str0 || SunlightArrows()*/;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", SCENE_SPIRIT_TEMPLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS] = Region("Spirit Temple MQ Skulltula Stairs", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_THRONE, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Silver Gauntlets Hand", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_CHILD_THRONE] = Region("Spirit Temple MQ Child Throne", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_RIGHT_HAND_EXIT, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_RIGHT_HAND_EXIT] = Region("Spirit Temple MQ Right Hand Exit", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_RIGHT_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_OUTER_RIGHT_HAND] = Region("Spirit Temple MQ Outer Right Hand", SCENE_SPIRIT_TEMPLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), + LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_OUTER_RIGHT_HAND, []{return 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_WEST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_RIGHT_HAND_EXIT, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return SpiritCertainAccess(RR_SPIRIT_TEMPLE_MQ_OUTER_RIGHT_HAND);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH] = Region("Spirit Temple MQ Block Room South", SCENE_SPIRIT_TEMPLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_HOLE] = Region("Spirit Temple MQ Big Blocks Hole", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FOYER, []{return true;}), //The block here is unusual in that it is a permanent flag, but reset anyway as child. This is because there's a check that would be blocked off by pushing them otherwise //It may be worth considering making this always temp in future so adult doesn't have the same issue - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return logic->IsChild ? logic->CanUse(RG_SILVER_GAUNTLETS) : AnyAgeTime([]{return logic->CanUse(RG_SILVER_GAUNTLETS);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_DOOR, []{return logic->IsChild ? logic->CanUse(RG_SILVER_GAUNTLETS) : AnyAgeTime([]{return logic->CanUse(RG_SILVER_GAUNTLETS);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH] = Region("Spirit Temple MQ Block Room North", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_DOOR] = Region("Spirit Temple MQ Big Blocks Door", SCENE_SPIRIT_TEMPLE, {}, { //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(SCENE_SPIRIT_TEMPLE, 7) && logic->CanHitEyeTargets()), + //Hard child locked because pushing the block is a permanent flag that blocks the eye target and cannot be undone + //but it resets as Child as a rando QoL feature + LOCATION(RC_SPIRIT_TEMPLE_MQ_SILVER_BLOCK_HALLWAY_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_DOOR, []{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 + //if going to RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_HOLE from here is ever relevant, there needs to be an event to handle the block Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST] = Region("Spirit Temple MQ Statue Room East", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_ADULT] = Region("Spirit Temple MQ Statue Room Adult", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_LEFT_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)/* || CanBunnyJump()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || + ((ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanUse(RG_LONGSHOT));}), + Entrance(RR_SPIRIT_TEMPLE_MQ_3_SUNS_ROOM_2F, []{return logic->Get(LOGIC_SPIRIT_MQ_STATUE_ROOM_TORCHES);}), + /* logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && + logic->CanJumpslash() && (str0 || SunlightArrows) && + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && + logic->CanKillEnemy(RE_IRON_KNUCKLE) && + logic->CanUse(RG_HOOKSHOT)*/ + Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4) && logic->MQSpirit4KeyColossus();}), + /* logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && + logic->CanJumpslash() && (str0 || SunlightArrows) && + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && + logic->CanKillEnemy(RE_IRON_KNUCKLE) && + logic->CanUse(RG_LONGSHOT) */ + Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_RIGHT_HAND, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4) && logic->MQSpirit4KeyWestHand();}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5);}), + // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled + Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->Get(LOGIC_SPIRIT_PLATFORM_LOWERED) && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_INNER_LEFT_HAND] = Region("Spirit Temple MQ Inner East Hand", SCENE_SPIRIT_TEMPLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_ROOM_LULLABY_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_ZELDAS_LULLABY) && (logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS))), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_ROOM_INVISIBLE_CHEST, (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS))), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_ROOM_LULLABY_CHEST, logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanBreakCrates()), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_ADULT, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE, []{return ((logic->IsAdult || ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE)) && (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanUse(RG_HOOKSHOT)) + || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE] = Region("Spirit Temple MQ Chest Ledge", SCENE_SPIRIT_TEMPLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_ROOM_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), - //!QUANTUM LOGIC! - //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(SCENE_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)) && - logic->CanKillEnemy(RE_IRON_KNUCKLE) && - logic->CanUse(RG_LONGSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5) && logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_MQ_LOWER_ADULT) && logic->CanUse(RG_DINS_FIRE));}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_ADULT, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT)/* || CanBunnyJump()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_LEFT_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F] = Region("Spirit Temple MQ Three Suns Room 2F", SCENE_SPIRIT_TEMPLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_3_SUNS_ROOM_2F] = Region("Spirit Temple MQ Three Suns Room 2F", SCENE_SPIRIT_TEMPLE, { //Events //implies logic->CanKillEnemy(RE_WALLMASTER). If we have lights, we can kill stalfos and wallmasters with bow - EventAccess(LOGIC_SPIRIT_MQ_3SUNS_ENEMIES, []{return (logic->CanUse(RG_MIRROR_SHIELD) && logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2)) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + EventAccess(LOGIC_SPIRIT_MQ_3SUNS_ENEMIES, []{return (logic->CanUse(RG_MIRROR_SHIELD) && logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2)) || logic->SunlightArrows();}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F, []{return logic->Get(LOGIC_SPIRIT_MQ_3SUNS_ENEMIES);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_ADULT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_3_SUNS_ROOM_1F, []{return logic->Get(LOGIC_SPIRIT_MQ_3SUNS_ENEMIES);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F] = Region("Spirit Temple MQ Three Suns Room 1F", SCENE_SPIRIT_TEMPLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_3_SUNS_ROOM_1F] = Region("Spirit Temple MQ 3 Suns Room 1F", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->Get(LOGIC_SPIRIT_MQ_3SUNS_ENEMIES);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_EAST, []{return true;}), + //It is possible to clip through here with longshot by aiming high enough + Entrance(RR_SPIRIT_TEMPLE_MQ_3_SUNS_ROOM_2F, []{return logic->Get(LOGIC_SPIRIT_MQ_3SUNS_ENEMIES)/* && CanClimbHigh()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEHIND_GEYSER, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_1F_EAST] = Region("Spirit Temple MQ 1F East", SCENE_SPIRIT_TEMPLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BEHIND_GEYSER] = Region("Spirit Temple MQ 1F East", SCENE_SPIRIT_TEMPLE, { //Events - //Assumes RR_SPIRIT_TEMPLE_MQ_LOBBY access + //Assumes RR_SPIRIT_TEMPLE_MQ_FOYER access EventAccess(LOGIC_SPIRIT_1F_SILVER_RUPEES, []{return logic->CanUse(RG_MEGATON_HAMMER);}), }, { //Locations @@ -396,46 +918,69 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_EARLY_ADULT_POT_2, logic->CanBreakPots()), }, { //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_SYMPHONY_ROOM, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FOYER, []{return logic->CanUse(RG_MEGATON_HAMMER);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_3_SUNS_ROOM_1F, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SAND_PIT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM] = Region("Spirit Temple MQ Leever Room", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SAND_PIT] = Region("Spirit Temple MQ Sand Pit", SCENE_SPIRIT_TEMPLE, {}, { //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)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_EAST, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEHIND_GEYSER, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM] = Region("Spirit Temple MQ Symphony Room", SCENE_SPIRIT_TEMPLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER] = Region("Spirit Temple MQ Symphony Room Upper", SCENE_SPIRIT_TEMPLE, { + //Events + EventAccess(LOGIC_SPIRIT_MQ_SYMPHONY_ROOM_DOOR, []{return logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanUse(RG_EPONAS_SONG) && + logic->CanUse(RG_SUNS_SONG) && logic->CanUse(RG_SONG_OF_STORMS) && logic->CanUse(RG_ZELDAS_LULLABY);}), + }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_EAST, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEHIND_GEYSER, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), //Implies CanPassEnemy(RE_MOBLIN_CHIEF) - Entrance(RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanUse(RG_EPONAS_SONG) && logic->CanUse(RG_SUNS_SONG) && logic->CanUse(RG_SONG_OF_STORMS) && logic->CanUse(RG_ZELDAS_LULLABY);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_LOWER, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM] = Region("Spirit Temple MQ After Symphony Room", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_LOWER] = Region("Spirit Temple MQ Symphony Room Lower", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER, []{return true/*CanClimb() || IsAdult || CanGroundJump()*/;}), + //Implies CanPassEnemy(RE_MOBLIN_CHIEF) + Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_ROOM, []{return logic->Get(LOGIC_SPIRIT_MQ_SYMPHONY_ROOM_DOOR);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_SKULLTULA_ROOM] = Region("Spirit Temple MQ Skulltula Room", SCENE_SPIRIT_TEMPLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_CHEST, logic->CanPassEnemy(RE_BIG_SKULLTULA)), LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SYMPHONY_ROOM, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_LOWER, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM] = Region("Spirit Temple MQ Four Beamos Room", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER] = Region("Spirit Temple MQ Fire Wall Stairs Lower", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_ADULT, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, []{return logic->CanUse(RG_HOOKSHOT);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER] = Region("Spirit Temple MQ Fire Wall Stairs Upper", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, []{return logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS] = Region("Spirit Temple MQ Beamos Pits", SCENE_SPIRIT_TEMPLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_BEAMOS_ROOM_CHEST, logic->CanKillEnemy(RE_BEAMOS)), - LOCATION(RC_SPIRIT_TEMPLE_MQ_BEAMOS_SMALL_CRATE, logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanBreakSmallCrates()), + LOCATION(RC_SPIRIT_TEMPLE_MQ_BEAMOS_SMALL_CRATE, logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanBreakSmallCrates()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 5) && logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 6);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME)/* && str0*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME)/* && str0*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_BASE, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 6);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM] = Region("Spirit Temple MQ SoT Sun Room", SCENE_SPIRIT_TEMPLE, {}, { @@ -444,31 +989,36 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_DINALFOS_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return logic->CanJumpslash();}), - Entrance(RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, []{return AnyAgeTime([]{return (logic->IsAdult || logic->CanUse(RG_SONG_OF_TIME)) && logic->CanUse(RG_MIRROR_SHIELD);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS, []{return logic->CanJumpslash();}), + Entrance(RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, []{return AnyAgeTime([]{return ((logic->IsAdult || logic->CanUse(RG_SONG_OF_TIME)) && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows();});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND] = Region("Spirit Temple MQ East Stairs to Hand", SCENE_SPIRIT_TEMPLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS] = Region("Spirit Temple MQ Floormaster Stairs", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && AnyAgeTime([]{return logic->CanKillEnemy(RE_FLOORMASTER);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_ADULT_THRONE, []{return (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && AnyAgeTime([]{return logic->CanKillEnemy(RE_FLOORMASTER);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", SCENE_SPIRIT_TEMPLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_ADULT_THRONE] = Region("Spirit Temple MQ Adult Throne", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_LEFT_HAND_EXIT, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND] = Region("Spirit Temple MQ Mirror Shield Hand", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_LEFT_HAND_EXIT] = Region("Spirit Temple MQ Left Hand Exit", SCENE_SPIRIT_TEMPLE, {}, {}, { + Entrance(RR_SPIRIT_TEMPLE_MQ_ADULT_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_LEFT_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_OUTER_LEFT_HAND] = Region("Spirit Temple MQ Outer Left Hand", SCENE_SPIRIT_TEMPLE, {}, { //Locations 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_EAST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_RIGHT_HAND, []{return logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_ADULT_THRONE, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM] = Region("Spirit Temple MQ 3F Gibdo Room", SCENE_SPIRIT_TEMPLE, {}, { @@ -479,16 +1029,25 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL] = Region("Spirit Temple MQ Big Wall", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL_BASE] = Region("Spirit Temple MQ Big Wall Base", SCENE_SPIRIT_TEMPLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_LONG_CLIMB_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_LONG_CLIMB_POT_2, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, []{return true;}), //technically we only need to avoid them, but the sheer height and the moving walls makes getting to the top after only stunning them very difficult/impossible - //The silver rupees are irrelevant without silver shuffle - Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return logic->CanKillEnemy(RE_KEESE);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return /*(*/logic->CanKillEnemy(RE_KEESE)/*|| CanUse(RG_SKULL_MASK)) && CanClimbHigh()*/;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER] = Region("Spirit Temple MQ Big Wall Upper", SCENE_SPIRIT_TEMPLE, { + //Events + //Getting some of these with just climbing downwards is theoretically possible but definitely a trick + EventAccess(LOGIC_SPIRIT_MQ_BIG_WALL_SILVERS, []{return /*(*/logic->CanKillEnemy(RE_KEESE)/*|| CanUse(RG_SKULL_MASK)) && CanClimbHigh()*/;}), + }, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_BASE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return logic->Get(LOGIC_SPIRIT_MQ_BIG_WALL_SILVERS);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL] = Region("Spirit Temple MQ 4F Central", SCENE_SPIRIT_TEMPLE, {}, { @@ -497,22 +1056,26 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_BEFORE_MIRROR_POT_2, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_9_THRONES_ROOM, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM] = Region("Spirit Temple MQ Nine Chairs Room", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_9_THRONES_ROOM] = Region("Spirit Temple MQ 9 Thrones Room", SCENE_SPIRIT_TEMPLE, {}, { //Locations //These skulls rely on the iron knuckle existing without a trick to shoot through the chairs - LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_NINE_THRONES_ROOM_WEST, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), - LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_NINE_THRONES_ROOM_NORTH, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)), + LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_NINE_THRONES_ROOM_WEST, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), + LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_NINE_THRONES_ROOM_NORTH, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM] = Region("Spirit Temple MQ Big Mirror Room", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM] = Region("Spirit Temple MQ Big Mirror Room", SCENE_SPIRIT_TEMPLE, { + //Events + //Needs the mirror in the cave to be a perm flag and event for doorsanity + EventAccess(LOGIC_SPIRIT_PLATFORM_LOWERED, []{return (logic->Get(LOGIC_SPIRIT_PUSHED_4F_MIRRORS) && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows();}), + }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_BIG_MIRROR_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_BIG_MIRROR_POT_2, logic->CanBreakPots()), @@ -526,22 +1089,46 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return AnyAgeTime([]{return logic->CanUse(RG_MEGATON_HAMMER);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->Get(LOGIC_SPIRIT_PLATFORM_LOWERED);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE] = Region("Spirit Temple MQ Big Mirror Cave", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE] = Region("Spirit Temple MQ Big Mirror Cave", SCENE_SPIRIT_TEMPLE, { + //Events + //WARNING: Not currently a permanent flag, needs to be one for doorsanity + EventAccess(LOGIC_SPIRIT_PUSHED_4F_MIRRORS, []{return ((logic->IsAdult && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows())/* && str0*/;}), + }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_MIRROR_PUZZLE_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)), }, { //Exits - //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 AnyAgeTime([]{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return AnyAgeTime([]{return logic->CanUse(RG_MIRROR_SHIELD);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Assumes SpiritPlatformLowered is checked on entry + areaTable[RR_SPIRIT_TEMPLE_MQ_PLATFORM] = Region("Spirit Temple MQ Platform", SCENE_SPIRIT_TEMPLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, []{return logic->Get(LOGIC_SPIRIT_PUSHED_4F_MIRRORS) && logic->CanUse(RG_MIRROR_SHIELD) && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_CHILD, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_ADULT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_LEFT_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD] = Region("Spirit Temple MQ Statue Head", SCENE_SPIRIT_TEMPLE, { + //Events + //WARNING these events are not glitchproofed and assume you need all keys to reach from the front + EventAccess(LOGIC_REVERSE_SPIRIT_CHILD, []{return logic->IsChild;}), + EventAccess(LOGIC_REVERSE_SPIRIT_ADULT, []{return logic->IsAdult;}), + }, {}, { // Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + //CanBunnyJump with a jumpslash can reach either hand and with good timing the platform as child. the latter is definitely a trick, the former may not be + //If this interaction with the hands is added, SpiritSharedLogic needs updating for it + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_LEFT_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->Get(LOGIC_SPIRIT_PLATFORM_LOWERED) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT)/* || (IsAdult && CanBunnyJump())*/);}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -549,9 +1136,9 @@ void RegionTable_Init_SpiritTemple() { // Boss Room areaTable[RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY] = Region("Spirit Temple Boss Entryway", SCENE_SPIRIT_TEMPLE, {}, {}, { // 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 logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + 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 logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", SCENE_SPIRIT_TEMPLE_BOSS, { diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 53c60f996..3cba30061 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -566,14 +566,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: @@ -657,7 +654,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: @@ -738,12 +735,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: @@ -831,6 +827,35 @@ 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: + killed = killed || CanUse(RG_BOOMERANG); + [[fallthrough]]; + 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); @@ -889,6 +914,7 @@ bool Logic::CanPassEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal return true; // 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); @@ -935,9 +961,10 @@ 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) || + return !grounded || CanUse(RG_NUTS) || CanUse(RG_DINS_FIRE) || (quantity == 1 && (CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT))); case RE_MAD_SCRUB: return !grounded || CanUse(RG_NUTS); @@ -948,8 +975,10 @@ 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`."); + SPDLOG_ERROR("CanAvoidEnemy reached `default`."); assert(false); return false; } @@ -1380,6 +1409,10 @@ bool Logic::HasFireSourceWithTorch() { return HasFireSource() || CanUse(RG_STICKS); } +bool Logic::SunlightArrows() { + return ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS); +} + // Is this best off signaling what you have already traded, or what step you are currently on? bool Logic::TradeQuestStep(RandomizerGet rg) { if (ctx->GetOption(RSK_SHUFFLE_ADULT_TRADE)) { @@ -2402,6 +2435,110 @@ bool Logic::IsFireLoopLocked() { ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON); } +bool Logic::IsReverseAccessPossible() { + // If we ever allow dungeon entrances to connect to boss rooms directly in dungeon chains, or for 1 boss door to + // lead to another dungeons boss door, add RSK_MIX_DUNGEON_ENTRANCES to the final condition + // RANDOTODO Check for Age-Locked Boss entrances + decoupled + Ganon's tower when it is shuffled + return !ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) && + ((ctx->GetOption(RSK_DECOUPLED_ENTRANCES) && + ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_FULL)) || + (ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) && + (ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) || ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES)))); +} + +bool Logic::SpiritSunOnFloorToStatue() { + return /*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS))); +} + +bool Logic::SpiritExplosiveKeyLogic() { + return SmallKeys(SCENE_SPIRIT_TEMPLE, HasExplosives() ? 1 : 2); +} + +bool Logic::SpiritWestToSkull() { + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW); +} + +bool Logic::SpiritSunBlockSouthLedge() { + // It's also possible to do a backwalk hover + backflip if you equip hovers as you start the backwalk to accelerate + // faster + return true /*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() || + (CanUse(RG_HOOKSHOT) && (HasFireSource() || + (SpiritSunBlockTorch && (logic->CanUse(STICKS) || + (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))))*/ + ; +} + +bool Logic::SpiritEastToSwitch() { + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || + (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); +} + +// Combines crossing the ledge directly and the jump from the hand +bool Logic::MQSpiritWestToPots() { + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME); +} + +bool Logic::MQSpiritStatueToSunBlock() { + return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || + CanUse(RG_SONG_OF_TIME) /* || CanBunnyJump()*/) /* && str0*/; +} + +bool Logic::MQSpiritStatueSouthDoor() { + return HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && CanUse(RG_FAIRY_BOW) && + CanUse(RG_SONG_OF_TIME) /* && CanClimb()*/); +} + +bool Logic::MQSpirit4KeyColossus() { + // !QUANTUM LOGIC! + // We only need 4 keys and the ability to reach both hands for adult to logically be able to drop down onto Desert + // Colossus This is because there are only 3 keys that can be wasted without opening up either this lock to East + // hand, or the West Hand lock through Sun Block Room and both directions allow you to drop onto colossus + // logic->CanKillEnemy(RE_FLOORMASTER) is implied + return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) && + CanJumpslash() && /*(str0 || SunlightArrows) &&*/ + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || CanUse(RG_LENS_OF_TRUTH)) && CanKillEnemy(RE_IRON_KNUCKLE) && + CanUse(RG_HOOKSHOT); +} + +bool Logic::MQSpirit4KeyWestHand() { + // !QUANTUM LOGIC! + // Continuing from MQSpirit4KeyColossus, if we also have a longshot, we can go from the East hand to the West hand, + // meaning we always have access to East Hand + return CanUse(RG_LONGSHOT) && MQSpirit4KeyColossus(); +} +// This version of the function handles Shared Access for child, based on what adult could do if they existed +bool Logic::CouldMQSpirit4KeyWestHand() { + return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) && + (HasItem(RG_MASTER_SWORD) || HasItem(RG_BIGGORON_SWORD) || HasItem(RG_MEGATON_HAMMER)) && + /*(str0 || SunlightArrows) &&*/ + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || CanUse(RG_LENS_OF_TRUTH)) && HasItem(RG_LONGSHOT); +} + +// !QUANTUM LOGIC! +// With 3 keys, you cannot lock adult out of leaving spirit onto the hands and jumping down, as you would have to +// open the west hand door and then adult could climb through sun block room to jump down from there +// This requires that adult can complete both routes +// If we have the longshot, we can also guarantee access to the outer west hand as you can longshot from the east hand +// to the west Implies CanKillEnemy(RE_IRON_KNUCKLE) +bool Logic::OuterWestHandLogic() { + return HasExplosives() /* && CanClimbHigh() && str0*/ && + SmallKeys(SCENE_SPIRIT_TEMPLE, HasItem(RG_LONGSHOT) ? 3 : 5); +} + +bool Logic::OuterWestHandMQLogic() { + return MQSpiritStatueToSunBlock() && SmallKeys(SCENE_SPIRIT_TEMPLE, CouldMQSpirit4KeyWestHand() ? 4 : 7); +} + +bool Logic::StatueRoomMQKeyLogic() { + // !QUANTUM LOGIC! + // If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys, + // the ability to hit switches and the ability to climb because only child can reach the initial child lock + // without opening the Statue room to Broken Wall Room lock first + // if adult can ever cross crawlspaces this becomes more complicated. + return SmallKeys(SCENE_SPIRIT_TEMPLE, + IsChild && Get(LOGIC_REVERSE_SPIRIT_CHILD) && CanHitSwitch() /* && CanClimbHigh()*/ ? 6 : 7); +} + void Logic::Reset(bool resetSaveContext /*= true*/) { if (resetSaveContext) { NewSaveContext(); diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 61f9beed7..4ca376198 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -105,6 +105,7 @@ class Logic { bool CanBonkTrees(); bool HasFireSource(); bool HasFireSourceWithTorch(); + bool SunlightArrows(); bool TradeQuestStep(RandomizerGet rg); bool CanStandingShield(); bool CanShield(); @@ -146,6 +147,21 @@ class Logic { static std::map RandoGetToDungeonScene; static std::map RandoGetToEquipFlag; static std::map RandoGetToRandInf; + bool IsReverseAccessPossible(); + bool SpiritSunOnFloorToStatue(); + bool SpiritEastToSwitch(); + bool SpiritWestToSkull(); + bool SpiritSunBlockSouthLedge(); + bool MQSpiritWestToPots(); + bool MQSpiritStatueToSunBlock(); + bool MQSpiritStatueSouthDoor(); + bool MQSpirit4KeyColossus(); + bool MQSpirit4KeyWestHand(); + bool CouldMQSpirit4KeyWestHand(); + bool OuterWestHandLogic(); + bool OuterWestHandMQLogic(); + bool SpiritExplosiveKeyLogic(); + bool StatueRoomMQKeyLogic(); private: std::shared_ptr ctx; diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 5ae0abaeb..f5ffe3c77 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -7,6 +7,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. @@ -310,10 +312,27 @@ typedef enum { LOGIC_WATER_MQ_PILLAR_SOT_BLOCK, LOGIC_WATER_MQ_SIDE_TOWER_TARGETS, LOGIC_SPIRIT_1F_SILVER_RUPEES, + LOGIC_SPIRIT_CHILD_SWITCH_BRIDGE, + LOGIC_SPIRIT_SILVER_RUPEE_BRIDGE, + LOGIC_SPIRIT_SUN_BLOCK_TORCH, + LOGIC_SPIRIT_BOUNDERS_SILVERS, + LOGIC_SPIRIT_STATUE_SOUTH_DOOR, + LOGIC_SPIRIT_PLATFORM_LOWERED, + LOGIC_SPIRIT_4F_SWITCH, + LOGIC_SPIRIT_PUSHED_4F_MIRRORS, + LOGIC_SPIRIT_MQ_GIBDOS_CLEARED, LOGIC_SPIRIT_MQ_CRAWL_BOULDER, + LOGIC_SPIRIT_MQ_TURNTABLE_ENEMY, LOGIC_SPIRIT_MQ_MAP_ROOM_ENEMIES, LOGIC_SPIRIT_MQ_TIME_TRAVEL_CHEST, + LOGIC_SPIRIT_MQ_STATUE_ROOM_TORCHES, LOGIC_SPIRIT_MQ_3SUNS_ENEMIES, + LOGIC_SPIRIT_MQ_SYMPHONY_ROOM_DOOR, + LOGIC_SPIRIT_MQ_BIG_WALL_SILVERS, + LOGIC_REVERSE_SPIRIT_CHILD, + LOGIC_REVERSE_SPIRIT_ADULT, + LOGIC_FORWARDS_SPIRIT_CHILD, + LOGIC_FORWARDS_SPIRIT_ADULT, LOGIC_SHADOW_SHORTCUT_BLOCK, LOGIC_SHADOW_BRIDGE_BEYOND_BOAT_LOWERED, LOGIC_SHADOW_MQ_FLOOR_SPIKES_RUPEES, @@ -1162,51 +1181,105 @@ typedef enum { RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, - RR_SPIRIT_TEMPLE_LOBBY, - RR_SPIRIT_TEMPLE_CHILD, - RR_SPIRIT_TEMPLE_CHILD_CLIMB, - RR_SPIRIT_TEMPLE_EARLY_ADULT, - RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, - RR_SPIRIT_TEMPLE_OUTDOOR_HANDS, - RR_SPIRIT_TEMPLE_BEYOND_CENTRAL_LOCKED_DOOR, - RR_SPIRIT_TEMPLE_BEYOND_FINAL_LOCKED_DOOR, - RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, + RR_SPIRIT_TEMPLE_FOYER, + RR_SPIRIT_TEMPLE_CHILD_SIDE_HUB, + 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_CHILD_BOXES, + RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, + RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, + RR_SPIRIT_TEMPLE_ADULT_SIDE_HUB, + RR_SPIRIT_TEMPLE_SAND_PIT, + RR_SPIRIT_TEMPLE_BOULDERS, + RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, + RR_SPIRIT_TEMPLE_PAST_BOULDERS, + RR_SPIRIT_TEMPLE_1F_MIRROR_ROOM, + RR_SPIRIT_TEMPLE_2F_MIRROR_ROOM, + RR_SPIRIT_TEMPLE_STATUE_ROOM, + RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, + RR_SPIRIT_TEMPLE_INNER_WEST_HAND, + RR_SPIRIT_TEMPLE_GS_LEDGE, + RR_SPIRIT_TEMPLE_STATUE_ROOM_ADULT, + RR_SPIRIT_TEMPLE_INNER_LEFT_HAND, + RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, + RR_SPIRIT_TEMPLE_SHORTCUT, + RR_SPIRIT_TEMPLE_EMPTY_STAIRS, + RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, + RR_SPIRIT_TEMPLE_SUN_BLOCK_CHEST_LEDGE, + RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, + RR_SPIRIT_TEMPLE_CHILD_THRONE, + RR_SPIRIT_TEMPLE_RIGHT_HAND_EXIT, + RR_SPIRIT_TEMPLE_OUTER_RIGHT_HAND, + RR_SPIRIT_TEMPLE_POT_STAIRS, + RR_SPIRIT_TEMPLE_BEAMOS_PITS, + RR_SPIRIT_TEMPLE_4_ARMOS, + RR_SPIRIT_TEMPLE_4_ARMOS_SIDE_ROOM, + RR_SPIRIT_TEMPLE_CHEST_STAIRS, + RR_SPIRIT_TEMPLE_ADULT_THRONE, + RR_SPIRIT_TEMPLE_LEFT_HAND_EXIT, + RR_SPIRIT_TEMPLE_OUTER_LEFT_HAND, + RR_SPIRIT_TEMPLE_BIG_WALL_BASE, + RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, + RR_SPIRIT_TEMPLE_4F_CENTRAL, + RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, + RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, + RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, + RR_SPIRIT_TEMPLE_PLATFORM, + RR_SPIRIT_TEMPLE_STATUE_HEAD, - RR_SPIRIT_TEMPLE_MQ_LOBBY, - RR_SPIRIT_TEMPLE_MQ_1F_WEST, - RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, - RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH, - RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, - RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, - RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, - RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, + RR_SPIRIT_TEMPLE_MQ_FOYER, + RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB, + RR_SPIRIT_TEMPLE_MQ_GIBDO_GRAVES, + RR_SPIRIT_TEMPLE_MQ_GIBDO_POTS, + RR_SPIRIT_TEMPLE_MQ_TURNTABLE, + RR_SPIRIT_TEMPLE_MQ_TURNTABLE_BEHIND_FIRE, + RR_SPIRIT_TEMPLE_MQ_ANUBIS_BRIDGE_GRAVE, + RR_SPIRIT_TEMPLE_MQ_ANUBIS_BRIDGE_CHEST, + RR_SPIRIT_TEMPLE_MQ_1F_CHEST_SWITCH, RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, - RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, + RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, + RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_CHILD, + RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, + RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, + RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, - RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, - RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_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_SYMPHONY_ROOM, - RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM, - RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, + RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, + RR_SPIRIT_TEMPLE_MQ_CHILD_THRONE, + RR_SPIRIT_TEMPLE_MQ_RIGHT_HAND_EXIT, + RR_SPIRIT_TEMPLE_MQ_OUTER_RIGHT_HAND, + RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_HOLE, + RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_DOOR, + RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_ADULT, + RR_SPIRIT_TEMPLE_MQ_INNER_LEFT_HAND, + RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE, + RR_SPIRIT_TEMPLE_MQ_3_SUNS_ROOM_2F, + RR_SPIRIT_TEMPLE_MQ_3_SUNS_ROOM_1F, + RR_SPIRIT_TEMPLE_MQ_BEHIND_GEYSER, + RR_SPIRIT_TEMPLE_MQ_SAND_PIT, + RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER, + RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_LOWER, + RR_SPIRIT_TEMPLE_MQ_SKULLTULA_ROOM, + RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, + RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, + RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, - RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, - RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, - RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND, + RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS, + RR_SPIRIT_TEMPLE_MQ_ADULT_THRONE, + RR_SPIRIT_TEMPLE_MQ_LEFT_HAND_EXIT, + RR_SPIRIT_TEMPLE_MQ_OUTER_LEFT_HAND, RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, - RR_SPIRIT_TEMPLE_MQ_BIG_WALL, + RR_SPIRIT_TEMPLE_MQ_BIG_WALL_BASE, + RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, - RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM, + RR_SPIRIT_TEMPLE_MQ_9_THRONES_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_PLATFORM, + RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, @@ -4183,9 +4256,9 @@ 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_STATUE_JUMP, RT_SPIRIT_PLATFORM_HOOKSHOT, RT_SPIRIT_MAP_CHEST, RT_SPIRIT_SUN_CHEST, @@ -7067,6 +7140,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 c2fafd6ba..43681104c 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1342,10 +1342,15 @@ void Settings::CreateOptions() { "Allows the following possible without Tunics:\n- Enter Water Temple. The area below the center pillar " "still requires Zora Tunic. Applies to MQ also.\n- Enter Fire Temple. Volvagia still requires Goron " "Tunic. Applies to MQ also, and includes child access to first floor with dungeon shuffle."); - OPT_TRICK(RT_RUSTED_SWITCHES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, - "Hammer Rusted Switches Through Walls", - "Applies to:\n- Fire Temple Highest Goron Chest.\n- Water Trial\n- MQ Fire Temple Lizalfos Maze.\n- MQ " - "Spirit Trial."); + OPT_TRICK(RT_RUSTED_SWITCHES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "Hammer Through Collision", + "Applies to:\n" + "- Hitting Fire Temple Highest Goron Chest's Rusted Switch in the SoT Block without Song of Time.\n" + "- Hitting the rusted switch in Water Trial through the Ice." + "- Hitting MQ Fire Temple Lizalfos Maze's Rusted Switch in the wall.\n" + "- Having Adult hammer the rock in the west side crawlspace of MQ Spirit so child can get through " + "without bombchus." + "- MQ Spirit Trial's Rusted Switch between the thrones without hitting the eye target to drop an Iron " + "Knuckle.\n"); OPT_TRICK(RT_FLAMING_CHESTS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "Flaming Chests", "The chests encircled in flames in Gerudo Training Ground and in Spirit Temple can be opened by running " "into the flames while Link is invincible after taking damage."); @@ -2022,18 +2027,18 @@ 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 " "away and with precise timing."); OPT_TRICK( - RT_SPIRIT_LOBBY_JUMP, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Spirit Temple Main Room Jump from Hands to Upper Ledges", + RT_SPIRIT_STATUE_JUMP, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, + "Spirit Temple Statue Room Jump from Hands to Upper Ledges", "A precise jump to obtain the following as adult without needing one of Hover Boots, or Hookshot (in Vanilla) " "or Song of Time (in MQ): - Spirit Temple Statue Room Northeast Chest - Spirit Temple GS Lobby - Spirit Temple " "MQ Central Chamber Top Left Pot (Left) - Spirit Temple MQ Central Chamber Top Left Pot (Right)");