This commit is contained in:
Pepper0ni 2025-04-22 02:34:59 +01:00
commit 01800f74c6
7 changed files with 617 additions and 364 deletions

View file

@ -210,33 +210,129 @@ bool Here(const RandomizerRegion region, ConditionFn condition) {
return areaTable[region].Here(condition);
}
bool SpiritExplosiveLogic() {
return logic->HasExplosives() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3;
uint8_t SpiritExplosiveLogic() {
return logic->SpiritBrokenWallToStatue() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3;
}
// RANDOTODO basically every condition here will need climb or longshot.
bool SpiritSharedStatueRoom(ConditionFn condition, bool anyAge) {
return areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM].SpiritShared(
condition, [] { return logic->HasExplosives(); }, [] { return true; }, 5, 3, SpiritExplosiveLogic(), anyAge);
}
bool SpiritSharedSunBlockRoom(ConditionFn condition, bool anyAge) {
return areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE].SpiritShared(
condition, [] { return logic->HasExplosives(); }, [] { return true; }, 5, 3, SpiritExplosiveLogic(), anyAge);
}
// clang-format off
std::map<RandomizerRegion, SpiritLogicData> Region::spiritLogicData = {
{RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, SpiritLogicData(5, 5, 3, []{return true;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})},
{RR_SPIRIT_TEMPLE_BROKEN_WALL, SpiritLogicData(5, 5, 3, []{return true /*logic->CanClimbHigh()*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})},
{RR_SPIRIT_TEMPLE_2F_MIRROR, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();} , []{return true/*logic->CanClimbHigh()*/;} , []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})},
{RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})},
{RR_SPIRIT_TEMPLE_GS_LEDGE, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue() && logic->SpiritWestToSkull();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic()) && logic->SpiritWestToSkull()
/* && logic->CanClimbHigh() && str0*/;} , []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})},
{RR_SPIRIT_TEMPLE_STATUE_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true;})},
{RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && str0*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})},
{RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && str0*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})},
{RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();} , []{return true/*logic->CanClimbHigh() && str0*/;} , []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})},
{RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;} , []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));})},
{RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, SpiritLogicData(7, 6, 7, []{return true;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})},
{RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, SpiritLogicData(7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})},
{RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return true/*logic->Climb*/;} , []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})},
{RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return true;} , []{return true;})},
{RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})},
{RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() &&
areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();})
/* && logic->CanClimbHigh()*/;} , []{return false;} , []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});})},
};
// clang-format on
bool SpiritSharedBrokenWallRoom(ConditionFn condition, bool anyAge) {
return areaTable[RR_SPIRIT_TEMPLE_CHILD_CLIMB].SpiritShared(
condition, [] { return true; }, [] { return true; }, 5, 3, 1, anyAge);
}
bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) {
return areaTable[region].SpiritShared(
condition, [] { return true; }, [] { return true; }, 7, 0, 0, anyAge);
}
/*
* This logic covers checks that exist in the shared areas of Spirit from a glitchless standpoint.
* This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on
specific ages
bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) {
return areaTable[region].SpiritShared(
condition, [] { return true; }, [] { return true; }, 7, 7, 6, anyAge);
* There are 4 possibilities for passing a check, but first I have to talk about parallel universes.
* In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out
* In the second, they enter as adult and spend as many keys as they can to lock child out.
* When an Age can no longer be kept out by the other age, that age is said to have Certain Access to a region
* If both ages have access to a region with a certain number of keys, but there is no Certain Access,
* then a check is only in logic if both ages can collect the check independently
* If an age has Certain Access then that age can collect checks alone,
* and there is no reason to check the other age untile the universes converge.
* The universes converge when the player has all the keys, giving both ages Certain Access.
* We must check for these universes manually as we allow technical access with minimum keys for
* technical reasons as otherwise the code will never run
*/
bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion, ConditionFn otherCondition){
SpiritLogicData curRegionData = Region::spiritLogicData[region];
uint8_t childKeys = logic->ReverseSpiritChild ? curRegionData.childReverseKeys : curRegionData.childKeys;
// If we have enough keys that an age cannot be kept out, we have Certain Access
// otherwise if we have entered in reverse and can reach from the face, we have Certain Access
bool ChildCertainAccess = (logic->ReverseSpiritChild && curRegionData.reverseAccess) || logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys);
bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess) || logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys);
// If both ages have certain access, we can test with Either age
if (ChildCertainAccess && AdultCertainAccess) {
if (anyAge) {
return areaTable[region].Here(condition);
}
return condition();
// otherwise, we have to check the current age and...
} else if (areaTable[region].Child() && logic->IsChild) {
bool result = condition();
// If we have Certain Access, we just run the condition.
if (ChildCertainAccess) {
return result;
// Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Adult too
} else if (result) {
SpiritLogicData otherRegionData = Region::spiritLogicData[otherRegion];
// store current age variables
bool pastAdult = logic->IsAdult;
bool pastChild = logic->IsChild;
logic->IsChild = false;
logic->IsAdult = true;
// If Adult can get there and get the check, we can get the check in logic
// If reverse spirit is also possible, we need to make sure Adult can get it via reverse entry too
result = (curRegionData.adultAccess && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) ||
(otherRegion != RR_NONE &&
(otherRegionData.adultAccess && (!logic->IsReverseAccessPossible() || otherRegionData.reverseAccess) && otherCondition()));
logic->IsChild = pastChild;
logic->IsAdult = pastAdult;
return result;
}
} else if (areaTable[region].Adult() && logic->IsAdult) {
bool result = condition();
// if we have enough keys to have Certain Access, we just run the condition
// Alternatively, if we have entered in reverse and can reach from the face, we have Certain Access
if (AdultCertainAccess) {
return result;
// Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Child too
} else if (result){
SpiritLogicData otherRegionData = Region::spiritLogicData[otherRegion];
// store current age variables
bool pastAdult = logic->IsAdult;
bool pastChild = logic->IsChild;
logic->IsChild = true;
logic->IsAdult = false;
// If Child can get there and get the check, we can get the check in logic
// If reverse spirit is also possible, we need to make sure Child can get it via reverse entry too
result = (curRegionData.childAccess && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) ||
(otherRegion != RR_NONE &&
(otherRegionData.childAccess && (!logic->IsReverseAccessPossible() || otherRegionData.reverseAccess) && otherCondition()));
logic->IsChild = pastChild;
logic->IsAdult = pastAdult;
return result;
}
}
return false;
}
bool BeanPlanted(const RandomizerRegion region) {

View file

@ -111,6 +111,25 @@ class Entrance;
enum class EntranceType;
} // namespace Rando
struct SpiritLogicData {
uint8_t childKeys; //the number of keys that guarantees Child can reach this region
//The number of keys that guarantees Child can reach this region if they have reverse access
//This changes for MQ broken wall room as the first child lock can only be opened by Child
//guaranteeing access with 6 keys
uint8_t childReverseKeys;
uint8_t adultKeys; //the number of keys that guarantees Adult can reach this region
//The area access condition to reach this region as Child, from the first lock,
//including the minimum number of keys for ambiguous access
// 1 key is always assumed to be required
ConditionFn childAccess;
//The area access condition to reach this region as Adult, from the first lock
//including the minimum number of keys for ambiguous access
//1 key is always assumed to be required on vanilla
ConditionFn adultAccess;
//The area access condition to reach this region from the boss door,
ConditionFn reverseAccess;
};
class Region {
public:
Region();
@ -231,85 +250,11 @@ class Region {
"Adult Night: " +
std::to_string(adultNight);
}
/*
* This logic covers checks that exist in the shared areas of Spirit from a glitchless standpoint.
* This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on
specific ages
* There are 4 possibilities for passing a check, but first I have to talk about parallel universes.
* In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out
* In the second, they enter as adult and spend as many keys as they can to lock child out.
* When an Age can no longer be kept out by the other age, that age is said to have Certain Access to a region
* If both ages have access to a region with a certain number of keys, but there is no Certain Access,
* then a check is only in logic if both ages can collect the check independently
* If an age has Certain Access then that age can collect checks alone,
* and there is no reason to check the other age untile the universes converge.
* The universes converge when the player has all the keys, giving both ages Certain Access.
* We must check for these universes manually as we allow technical access with minimum keys for
* technical reasons as otherwise the code will never run
*/
bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, uint8_t childKeys,
uint8_t adultKeys, uint8_t eitherKeys, bool anyAge = false) {
// If we have all of the keys, we know that access is Certain Access
if (ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->IsMQ() ? logic->SmallKeys(RR_SPIRIT_TEMPLE, 7)
: logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)) {
if (anyAge) {
return Here(condition);
}
return condition();
// otherwise, we have to check the current age and...
} else if (Child() && logic->IsChild) {
bool result = condition();
// if we have enough keys to have Certain Access, we just run the condition
if (logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys)) {
return result;
// otherwise we need to check both ages if we have enough keys that either can get there
} else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && adultAccess) {
// store current age variables
bool pastAdult = logic->IsAdult;
bool pastChild = logic->IsChild;
logic->IsChild = false;
logic->IsAdult = true;
result = condition();
logic->IsChild = pastChild;
logic->IsAdult = pastAdult;
return result;
}
} else if (Adult() && logic->IsAdult) {
bool result = condition();
// if we have enough keys to have Certain Access, we just run the condition
if (logic->SmallKeys(RR_SPIRIT_TEMPLE, adultKeys)) {
return result;
// otherwise we need to check both ages
} else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && childAccess) {
// store current age variables
bool pastAdult = logic->IsAdult;
bool pastChild = logic->IsChild;
logic->IsChild = true;
logic->IsAdult = false;
result = condition();
logic->IsChild = pastChild;
logic->IsAdult = pastAdult;
return result;
}
}
return false;
}
static std::map<RandomizerRegion, SpiritLogicData> spiritLogicData;
bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, ConditionFn ReverseAccess,
uint8_t childKeys, uint8_t adultKeys, uint8_t eitherKeys, bool anyAge);
};
extern std::array<Region, RR_MAX> areaTable;
@ -318,11 +263,8 @@ extern std::vector<EventAccess> grottoEvents;
bool Here(const RandomizerRegion region,
ConditionFn
condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this
bool SpiritSharedBrokenWallRoom(ConditionFn condition, bool anyAge = false);
bool SpiritSharedStatueRoom(ConditionFn condition, bool anyAge = false);
bool SpiritSharedSunBlockRoom(ConditionFn condition, bool anyAge = false);
bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false);
bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false);
bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge = false,
RandomizerRegion otherRegion = RR_NONE, ConditionFn otherCondition = []{return false;});
bool CanPlantBean(const RandomizerRegion region);
bool BothAges(const RandomizerRegion region);
bool ChildCanAccess(const RandomizerRegion region);

View file

@ -22,44 +22,45 @@ void RegionTable_Init_SpiritTemple() {
LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_2, logic->CanBreakPots()),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return logic->IsChild;}),
Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}),
Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild;}),
Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}),
});
areaTable[RR_SPIRIT_TEMPLE_CHILD_LOBBY] = Region("Child Spirit Temple", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
areaTable[RR_SPIRIT_TEMPLE_1F_WEST] = Region("Spirit Temple 1F West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->NutCrate, []{return true;}),
}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild;}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->IsChild;}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);}),
Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild;}),
Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->IsChild;}),
Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}),
Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}),
});
areaTable[RR_SPIRIT_TEMPLE_CHILD_STALFOS] = Region("Child Spirit Temple Stalfos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH] = Region("Spirit Temple Switch Bridge South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->SpiritChildStalfosBridge, []{return logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU));}),
//RANDOTODO a version od CanHitSwitch that takes WallOrFloor
EventAccess(&logic->SpiritChildSwitchBridge, []{return logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU));}),
}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, []{return (logic->SpiritChildStalfosBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return (logic->SpiritChildSwitchBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}),
});
areaTable[RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE] = Region("Child Spirit Temple Stalfos Across Bridge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH] = Region("Spirit Temple Switch Bridge North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->SpiritChildStalfosBridge, []{return logic->CanHitSwitch();}),
EventAccess(&logic->SpiritChildSwitchBridge, []{return logic->CanHitSwitch();}),
}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST, true),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS, []{return logic->CanUse(RG_HOVER_BOOTS) || (logic->SpiritChildStalfosBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false));}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_ANUBIS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return logic->CanUse(RG_HOVER_BOOTS) || (logic->SpiritChildSwitchBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false));}),
Entrance(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_CHILD_ANUBIS] = Region("Child Spirit Temple Anubis", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_1F_ANUBIS] = Region("Spirit Temple 1F Anubis", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_1, true),
LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_2, true),
@ -67,214 +68,297 @@ void RegionTable_Init_SpiritTemple() {
LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_4, true),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);}),
Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return Here(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);});}),
Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, []{return Here(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);});}),
});
areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
areaTable[RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH] = Region("Spirit Temple Rupee Bridge North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->SpiritChildTorchesBridge, []{return true;}),
EventAccess(&logic->SpiritRupeeBridge, []{return true;}),
}, {
//Locations
// these assume SpiritChildTorchesBridge, silver rupee shuffle & shuffle climb will want to adjust
// these assume SpiritRupeeBridge, silver rupee shuffle & shuffle climb will want to adjust
LOCATION(RC_SPIRIT_TEMPLE_CHILD_EARLY_TORCHES_CHEST, logic->HasFireSourceWithTorch()),
// possible to collect without lowering fence, should be a trick
LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES, []{return logic->SpiritChildTorchesBridge;}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_ANUBIS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return logic->SpiritRupeeBridge;}),
Entrance(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH] = Region("Spirit Temple Rupee Bridge South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOOMERANG)),
LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->SpiritChildTorchesBridge;}),
Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, []{return logic->SpiritRupeeBridge;}),
});
areaTable[RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB] = Region("Child Spirit Temple Before Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_1F_BOXES] = Region("Child Spirit Temple Before Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_1, logic->CanBreakSmallCrates()),
LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_2, logic->CanBreakSmallCrates()),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return logic->IsChild;}),
Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}),
Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild;}),
Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}),
});
areaTable[RR_SPIRIT_TEMPLE_CHILD_CLIMB] = Region("Child Spirit Temple Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE] = Region("Spirit Temple Child Climb Base", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritSharedBrokenWallRoom([]{return logic->CanHitSwitch(ED_BOMB_THROW);})),
LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritSharedBrokenWallRoom([]{return logic->CanHitSwitch(ED_BOMB_THROW);})),
LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritSharedBrokenWallRoom([]{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})),
LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);})),
LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return logic->CanBreakPots();})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}),
Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}),
Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_ADULT_LOBBY] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->SpiritAdultLobbySwitch, []{return logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH));}),
}, {}, {
areaTable[RR_SPIRIT_TEMPLE_BROKEN_WALL] = Region("Spirit Temple Broken Wall", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanHitSwitch(ED_BOMB_THROW);})),
LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanHitSwitch(ED_BOMB_THROW);})),
LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_ADULT_SAND_PIT, []{return logic->SpiritAdultLobbySwitch;}),
Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return logic->SpiritAdultLobbySwitch;}),
Entrance(RR_SPIRIT_TEMPLE_ADULT_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}),
Entrance(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return true;}),
///*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS)))
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->SpiritBrokenWallToStatue();}),
});
areaTable[RR_SPIRIT_TEMPLE_ADULT_SAND_PIT] = Region("Adult Spirit Temple Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_1F_EAST] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_SAND_PIT, []{return Here(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanHitSwitch(logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH) ? ED_BOMB_THROW : ED_BOOMERANG);});}),
Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return Here(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanHitSwitch(logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH) ? ED_BOMB_THROW : ED_BOOMERANG);});}),
Entrance(RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}),
});
areaTable[RR_SPIRIT_TEMPLE_SAND_PIT] = Region("Spirit Temple Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_COMPASS_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_ZELDAS_LULLABY)),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_ADULT_BOULDERS] = Region("Adult Spirit Temple Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_ABOVE_BOULDERS] = Region("Spirit Temple Above Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
//Jump slash is possible as child, but pretty tight. Jumpslash as late as you can
//A damage boost off the boulder is also possible, but you need
EventAccess(&logic->SpiritBouldersSilvers, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT);}),
}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_BOULDERS] = Region("Spirit Temple Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_GS_BOULDER_ROOM, logic->CanUse(RG_SONG_OF_TIME) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, []{return true;}),
//Jump slash is possible as child, but pretty tight. Jumpslash as late as you can
//A damage boost off the boulder is also possible, but you need
Entrance(RR_SPIRIT_TEMPLE_PAST_BOULDERS, []{return logic->SpiritBouldersSilvers;}),
});
areaTable[RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS] = Region("Adult Spirit Temple Past Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_PAST_BOULDERS] = Region("Spirit Temple Past Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_EARLY_ADULT_RIGHT_CHEST, true),
LOCATION(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_ADULT_CLIMB] = Region("Adult Spirit Temple Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE] = Region("Spirit Temple East Climb Base", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}),
Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_2F_MIRROR] = Region("Spirit Temple 2F Mirror", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, true),
LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, true),
LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;})),
LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}),
Entrance(RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST] = Region("Spirit Temple Statue Rooom West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
//Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM access
LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})),
//Current and N64 logic doesn't need scarecrow, you can hit the skull with normal hookshot from a specific spot on the forearm
//Child can get this with hook by backflipping onto the upper arm and standing in a precise place
LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) ? ED_BOOMERANG : ED_HOOKSHOT);}, false,
RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}),
//(IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW)
Entrance(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->SpiritWestToSkull();}),
// RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled
Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered &&
(logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}),
Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true;}),
//!QUANTUM LOGIC!
//When child enters spirit in reverse, has 4 keys, and dungeon entrance shuffle is off,
//Child cannot lock themselves out of desert colossus access as if they save the west hand lock for last
//they will be able to exit the dungeon through the intended entrance and vice versa
//for needing to open the west hand lock to block the intended child route
Entrance(RR_DESERT_COLOSSUS, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && logic->ReverseSpiritChild &&
logic->IsChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanKillEnemy(RE_IRON_KNUCKLE);}),
});
areaTable[RR_SPIRIT_TEMPLE_GS_LEDGE] = Region("Spirit Temple GS ledge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;})
});
areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM] = Region("Spirit Temple Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritSharedStatueRoom([]{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})),
LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritSharedStatueRoom([]{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA,
(ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS)) ? ED_CLOSE : ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) ? ED_BOOMERANG : ED_HOOKSHOT);})),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_4, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_5, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource();}, false,
RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_4, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_5, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}),
// RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled
Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered &&
(logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}),
//explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_SHORTCUT, []{return logic->SpiritStatueRoomSouthDoor;}),
});
areaTable[RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE] = Region("Spirit Temple Stairs to Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_EMPTY_STAIRS] = Region("Spirit Temple Empty Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE] = Region("Spirit Temple Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->SpiritSunBlockTorch, []{return SpiritSharedSunBlockRoom([]{return logic->IsAdult || logic->CanKillEnemy(RE_BEAMOS);});}),
}, {
areaTable[RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM] = Region("Spirit Temple Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
//Child can push blocks to get to the chest without killing the beamos, but it's likely a trick for similar reasons to armos push, and is not relevant without doorsanity
LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritSharedSunBlockRoom([]{return (logic->HasFireSource() ||
(logic->SpiritSunBlockTorch && (logic->CanUse(RG_STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))) &&
(logic->IsAdult || logic->CanKillEnemy(RE_BEAMOS) || logic->CanUse(RG_HOOKSHOT));})),
//the blocks can be used to get all the silver rupees and the chest itemless
LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM,
[]{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW));})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND] = Region("Spirit Temple West Stairs to Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS] = Region("Spirit Temple Skulltula Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_GS_HALL_AFTER_SUN_BLOCK_ROOM, (logic->HasExplosives() && logic->CanUse(RG_BOOMERANG) && logic->CanUse(RG_HOOKSHOT)) ||
(logic->CanUse(RG_BOOMERANG) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives()) ||
(logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) &&
logic->CanUse(RG_BOOMERANG) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))))),
LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_1, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))),
LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_2, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))),
LOCATION(RC_SPIRIT_TEMPLE_GS_HALL_AFTER_SUN_BLOCK_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);})),
LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->CanBreakPots();})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}),
Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}),
});
areaTable[RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_WEST_THRONE] = Region("Spirit Temple West Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}),
Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return Here(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}),
Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}),
Entrance(RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}),
});
areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_WEST_HAND_EXIT] = Region("Spirit Temple West Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_WEST_HAND] = Region("Spirit Temple West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return true;}),
Entrance(RR_DESERT_COLOSSUS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, []{return true;}),
Entrance(RR_DESERT_COLOSSUS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST] = Region("Spirit Temple Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)),
LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->CanUse(RG_ZELDAS_LULLABY) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))),
//Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST access. WARNING: ZL spawning chests here is a temp flag
LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_ADULT_CLIMB, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}),
Entrance(RR_SPIRIT_TEMPLE_SHORTCUT, []{return logic->CanUse(RG_MEGATON_HAMMER) && (ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS) || (logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanUse(RG_HOOKSHOT)));}),
Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}),
//(IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT));
Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->SpiritEastToSwitch();}),
Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}),
//!QUANTUM LOGIC!
//With 3 keys, you cannot lock adult out of longshotting to the west hand as you would have to
//open the west hand door and then adult could climb through sun block room
//This requires that adult can complete both routes
//Implies CanKillEnemy(RE_IRON_KNUCKLE)
Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_LONGSHOT) && logic->CanKillEnemy(RE_BEAMOS);}),
//Similarly, we can gurantee jumping into desert colossus the same way
//except this does not need the longshot as we don't care which hand we jump down from
Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS);}),
});
areaTable[RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS] = Region("Spirit Temple Stairs to Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH] = Region("Spirit Temple Shortcut Switch", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->CanUse(RG_MEGATON_HAMMER);});}),
}, {
//Locations
//Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST access
LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//If child can ever use silver gauntlets and adult items, there needs to be an event here to account for child entering in reverse
//opening the way for adult entering via the front.
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_MEGATON_HAMMER);}),
});
areaTable[RR_SPIRIT_TEMPLE_POT_STAIRS] = Region("Spirit Temple Stairs to Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
LOCATION(RC_SPIRIT_TEMPLE_BEAMOS_HALL_POT_1, logic->CanBreakPots()),
}, {
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}),
Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->HasExplosives()),
LOCATION(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, logic->HasExplosives() && logic->CanUse(RG_SUNS_SONG)),
}, {
areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->CanKillEnemy(RE_BEAMOS);}),
Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}),
Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}),
//Implies killing the anubis with the fire ring, doing so itemless requires voiding out, which can lock hardcore + OHKO seeds
Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->CanKillEnemy(RE_BEAMOS);}),
Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}),
Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}),
});
areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->HasExplosives()),
LOCATION(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, logic->HasExplosives() && logic->CanUse(RG_SUNS_SONG)),
}, {
Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM, []{return logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}),
Entrance(RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM] = Region("Spirit Temple Four Armos Side Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
@ -285,46 +369,47 @@ void RegionTable_Init_SpiritTemple() {
Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_CHEST_STAIRS] = Region("Spirit Temple Chest Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_LEFT_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)),
LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_RIGHT_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_EAST_THRONE] = Region("Spirit Temple East Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return Here(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}),
Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}),
});
areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_EAST_HAND_EXIT] = Region("Spirit Temple East hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_EAST_HAND, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND] = Region("Spirit Temple Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_EAST_HAND] = Region("Spirit Temple East Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, []{return logic->CanUse(RG_LONGSHOT);}),
Entrance(RR_DESERT_COLOSSUS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}),
Entrance(RR_DESERT_COLOSSUS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_LOWER] = Region("Spirit Temple Big Wall Lower", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER)));}),
Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && logic->CanPassEnemy(RE_WALLTULA));}),
});
areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_UPPER] = Region("Spirit Temple Big Wall Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
//Grabbing these with rang is possible, but requires a blind shot aimed high
LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_LEFT_HEART, logic->CanUse(RG_HOOKSHOT)),
LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_RIGHT_HEART, logic->CanUse(RG_HOOKSHOT)),
}, {
@ -336,41 +421,69 @@ void RegionTable_Init_SpiritTemple() {
areaTable[RR_SPIRIT_TEMPLE_4F_CENTRAL] = Region("Spirit Temple 4F Central", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}),
Entrance(RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}),
Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM] = Region("Spirit Temple Boss Key Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM] = Region("Spirit Temple Fake Doors Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, (logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) || (logic->CanHitEyeTargets() && logic->CanUse(RG_HOOKSHOT))),
LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, (logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) ||
(Here(RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, []{return logic->CanHitEyeTargets() && logic->CanAvoidEnemy(RE_TORCH_SLUG, true, 4);})
&& logic->CanUse(RG_HOOKSHOT))),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM] = Region("Spirit Temple Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM] = Region("Spirit Temple Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->Spirit4FSwitch, []{return logic->CanJumpslash() || logic->HasExplosives();}),
//Needs the mirror in the cave to be a perm flag and event for doorsanity
EventAccess(&logic->SpiritPlatformLowered, []{return (Here(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return logic->CanJumpslash() || logic->HasExplosives();}) || logic->CanUse(RG_MIRROR_SHIELD)) ||
(ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}),
}, {}, {
Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, []{return logic->CanJumpslash() || logic->HasExplosives();}),
//Rang can hit the switch on the way back but that's a trick
Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, []{return logic->Spirit4FSwitch;}),
//Assumes RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE access
Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return (logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->Spirit4FSwitch) ||
(ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}),
});
areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE] = Region("Spirit Temple Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE] = Region("Spirit Temple Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->Spirit4FSwitch, []{return logic->HasExplosives();}),
}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))),
LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, (logic->IsAdult && logic->CanUse(RG_MIRROR_SHIELD)) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->CanUse(RG_HOOKSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_MEGATON_HAMMER);}),
//Assumes a SpiritPlatformLowered check on entry
areaTable[RR_SPIRIT_TEMPLE_PLATFORM] = Region("Spirit Temple Lowered Platform", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->Spirit4FSwitch;})
&& logic->CanUse(RG_HOOKSHOT);}),
});
areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_STATUE_HEAD] = Region("Spirit Temple Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
//WARNING these events are not glitchproofed and assume you need all keys to reach from the front
EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}),
EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}),
}, {}, {
// Exits
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS);}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS);}),
Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT));}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
});
#pragma endregion
@ -432,7 +545,6 @@ void RegionTable_Init_SpiritTemple() {
areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
//For non-fairy pot items, you can also get them with rang without killing the stalfos
EventAccess(&logic->FairyPot, []{return Here(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}),
}, {
//Locations
@ -470,7 +582,7 @@ void RegionTable_Init_SpiritTemple() {
}, {
//Exits
//The bridge is a temp flag, so not a way to cross south to north in logic
Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, []{return logic->CanUse(RG_HOOKSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}),
});
@ -487,7 +599,7 @@ void RegionTable_Init_SpiritTemple() {
areaTable[RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE] = Region("Spirit Temple MQ Under Like Like", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_LIKE_LIKE_POT, MQSpiritSharedBrokenWallRoom(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_LIKE_LIKE_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanBreakPots();})),
}, {
//Exits
//This covers adult access only, as child arrives here from the other side of this door
@ -498,10 +610,9 @@ void RegionTable_Init_SpiritTemple() {
areaTable[RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM] = Region("Spirit Temple MQ Broken Wall Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
//Implies CanKillEnemy(RE_LIKE_LIKE)
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, MQSpiritSharedBrokenWallRoom(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanKillEnemy(RE_BEAMOS);})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanKillEnemy(RE_BEAMOS);})),
//Sunlights only temp spawn this chest, which is unintuitive/a bug.
//chest is only reachable as adult glitchlessly, so we can skip the shared in favour of IsAdult as adult access is always Certain
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, logic->IsAdult && (logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->CanUse(RG_HOOKSHOT)),
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return (logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->CanUse(RG_HOOKSHOT);})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanHitSwitch();}),
@ -509,28 +620,48 @@ void RegionTable_Init_SpiritTemple() {
Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 2);}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM] = Region("Spirit Temple MQ Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST] = Region("Spirit Temple MQ Statue Room West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME);});}),
}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_MQ_COMPASS_CHEST, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanHitEyeTargets();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG)) || ((logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) && logic->CanBreakPots());})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) && logic->CanBreakPots());})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_CENTER_EAST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_WEST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_EASTMOST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_1, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_2, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakSmallCrates();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG)) ||
((logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) && logic->CanBreakPots());})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG) && ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE)) ||
(logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) && logic->CanBreakPots());})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return logic->CanBreakSmallCrates();})),
}, {
//Exits
//we check possible adult access directly in MQSpiritSharedBrokenWallRoom, so this exit only covers Certain Access
Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
//We can use Here instead of Shared here because adult will never need to rely on child access to reach this room, and adult access is Certain
Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return Here(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME));});}),
Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || logic->CanUse(RG_SONG_OF_TIME);}),
//Minimal entry, real key logic handled in Shared functions
Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}),
//IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME)
Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->MQSpiritStatueToSunBlock();}),
//explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this
Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM] = Region("Spirit Temple MQ Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->HasFireSource();});}),
}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_MQ_COMPASS_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanHitEyeTargets();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_CENTER_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_EASTMOST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_1, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_2, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})),
//the drop sometimes flies off the block when the crate is blown up, but not always, so I added a rang requirement
//a trick to reload for it is plausible
LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return (logic->CanUse(RG_SONG_OF_TIME) && logic->CanBreakSmallCrates()) ||
(logic->CanUse(RG_BOOMERANG) && logic->HasExplosives());})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return true/*logic->CanClimb()*/;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return logic->SpiritStatueRoomSouthDoor;}),
//explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this
Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_SCARECROW);}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS] = Region("Spirit Temple MQ Flamethrower Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
@ -541,43 +672,43 @@ void RegionTable_Init_SpiritTemple() {
areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM] = Region("Spirit Temple MQ Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
//We don't need Shared here because If we are checking as child, universe 2 adult access needs nothing so it always passes, and if we are checking as adult, it is Certain Access
LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, true),
LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, SpiritMQSharedSunBlockRoom([]{return true/*str0*/;})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritMQSharedSunBlockRoom([]{return logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, SpiritMQSharedSunBlockRoom([]{return logic->CanBreakPots();})),
LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, SpiritMQSharedSunBlockRoom([]{return logic->CanBreakPots();})),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true/*str0*/;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, []{return true/*str0*/;}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS] = Region("Spirit Temple MQ Skulltula Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}),
//This door causes the Universes to merge as it requires 7 keys for both ages
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND] = Region("Spirit Temple MQ West Stairs to Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_THRONE] = Region("Spirit Temple MQ West Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT] = Region("Spirit Temple MQ West Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return Here(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}),
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Exit to Silver Gauntlets Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Silver Gauntlets Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_HAND] = Region("Spirit Temple MQ West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true),
}, {
//Exits
//If it is ever relevent for 1 age to spawn the mirror shield chest for the other can longshot across, it needs an eventAccess
Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return true;}),
Entrance(RR_DESERT_COLOSSUS, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, []{return true;}),
Entrance(RR_DESERT_COLOSSUS, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH] = Region("Spirit Temple MQ Block Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
@ -590,8 +721,8 @@ void RegionTable_Init_SpiritTemple() {
areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH] = Region("Spirit Temple MQ Block Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
//Does not need to be shared as it's hard child locked, because adult pushing the block is a permanent flag that blocks the eye target and cannot be undone
LOCATION(RC_SPIRIT_TEMPLE_MQ_SILVER_BLOCK_HALLWAY_CHEST, logic->IsChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 7) && logic->CanHitEyeTargets()),
//Dhard child locked because adult pushing the block is a permanent flag that blocks the eye target and cannot be undone
LOCATION(RC_SPIRIT_TEMPLE_MQ_SILVER_BLOCK_HALLWAY_CHEST, SpiritMQSharedBigBlock([]{return logic->IsChild && logic->CanHitEyeTargets();})),
}, {
//Exits
//if going to RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH from here is ever relevant, there needs to be an event to handle the block
@ -609,7 +740,7 @@ void RegionTable_Init_SpiritTemple() {
//We only need 4 keys, access to Shield hand and longshot to reach Gauntlets hand, as if we waste the 5th key we have given ourselves Gauntlets hand access through child climb
//This exit handles that possibility as cleanly as possible without quantum logic, but will not survive glitch logic
//logic->CanKillEnemy(RE_FLOORMASTER) is implied
Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) &&
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) &&
logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) &&
logic->CanJumpslash() &&
(ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) &&
@ -647,11 +778,11 @@ void RegionTable_Init_SpiritTemple() {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return logic->CanUse(RG_MEGATON_HAMMER);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_SAND_PIT, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM] = Region("Spirit Temple MQ Leever Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_MQ_SAND_PIT] = Region("Spirit Temple MQ Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_MQ_LEEVER_ROOM_CHEST, logic->CanKillEnemy(RE_PURPLE_LEEVER) && logic->CanUse(RG_HOOKSHOT)),
LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_LEEVER_ROOM, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)),
@ -732,7 +863,7 @@ void RegionTable_Init_SpiritTemple() {
LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true),
}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->CanUse(RG_LONGSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return true;}),
Entrance(RR_DESERT_COLOSSUS, []{return true;}),
});
@ -810,11 +941,16 @@ void RegionTable_Init_SpiritTemple() {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->MQSpiritOpenedBigMirrorCave;}),
//If it's ever relevant to longshot into head from lobby, this needs to be an event access
Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);});}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD] = Region("Spirit Temple MQ Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
//WARNING these events are not glitchproofed and assume you need all keys to reach from the front
EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}),
EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}),
}, {}, {
// Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
@ -825,9 +961,9 @@ void RegionTable_Init_SpiritTemple() {
// Boss Room
areaTable[RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY] = Region("Spirit Temple Boss Entryway", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}),
});
areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", "Spirit Temple", {}, NO_DAY_NIGHT_CYCLE, {

View file

@ -539,14 +539,11 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS);
[[fallthrough]];
case ED_BOMB_THROW:
killed = killed || CanUse(RG_BOMB_BAG);
killed = killed || CanUse(RG_BOMB_BAG) || CanUse(RG_DINS_FIRE);
[[fallthrough]];
case ED_BOOMERANG:
// RANDOTODO test dins and chu range in a practical example
killed = killed || CanUse(RG_DINS_FIRE);
[[fallthrough]];
case ED_HOOKSHOT:
// RANDOTODO test dins and chu range in a practical example
// RANDOTODO test chu range in a practical example
killed = killed || CanUse(RG_HOOKSHOT) || (wallOrFloor && CanUse(RG_BOMBCHU_5));
[[fallthrough]];
case ED_LONGSHOT:
@ -576,15 +573,15 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS);
[[fallthrough]];
case ED_BOMB_THROW:
// RANDOTODO test dins and chu range in a practical example
// RANDOTODO test chu range in a practical example
killed = killed || (!inWater && CanUse(RG_BOMB_BAG));
[[fallthrough]];
case ED_BOOMERANG:
// RANDOTODO test dins and chu range in a practical example
// RANDOTODO test chu range in a practical example
killed = killed || CanUse(RG_BOOMERANG);
[[fallthrough]];
case ED_HOOKSHOT:
// RANDOTODO test dins, bomb and chu range in a practical example
// RANDOTODO test chu range in a practical example
killed = killed || CanUse(RG_HOOKSHOT) || (wallOrFloor && CanUse(RG_BOMBCHU_5));
[[fallthrough]];
case ED_LONGSHOT:
@ -629,7 +626,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
[[fallthrough]];
case ED_BOOMERANG:
case ED_HOOKSHOT:
// RANDOTODO test dins and chu range in a practical example
// RANDOTODO test chu range in a practical example
killed = killed || (wallOrFloor && CanUse(RG_BOMBCHU_5));
[[fallthrough]];
case ED_LONGSHOT:
@ -710,12 +707,11 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS);
[[fallthrough]];
case ED_BOMB_THROW:
// RANDOTODO test dins and chu range in a practical example
killed = killed || (!inWater && CanUse(RG_BOMB_BAG));
[[fallthrough]];
case ED_BOOMERANG:
case ED_HOOKSHOT:
// RANDOTODO test dins, bomb and chu range in a practical example
// RANDOTODO test chu range in a practical example
killed = killed || CanUse(RG_HOOKSHOT) || (wallOrFloor && CanUse(RG_BOMBCHU_5));
[[fallthrough]];
case ED_LONGSHOT:
@ -789,6 +785,33 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
case RE_OCTOROK:
return CanReflectNuts() || HookshotOrBoomerang() || CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT) ||
CanUse(RG_BOMB_BAG) || (wallOrFloor && CanUse(RG_BOMBCHU_5));
case RE_WALLTULA:
switch (distance) {
case ED_CLOSE:
case ED_SHORT_JUMPSLASH:
killed = CanUse(RG_KOKIRI_SWORD);
[[fallthrough]];
case ED_MASTER_SWORD_JUMPSLASH:
killed = killed || CanUse(RG_MASTER_SWORD);
[[fallthrough]];
case ED_LONG_JUMPSLASH:
killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS);
[[fallthrough]];
case ED_BOMB_THROW:
killed = killed || (!inWater && CanUse(RG_BOMB_BAG)) || CanUse(RG_DINS_FIRE);
[[fallthrough]];
case ED_BOOMERANG:
case ED_HOOKSHOT:
killed = killed || CanUse(RG_HOOKSHOT) || CanUse(RG_BOMBCHU_5) || CanUse(RG_MEGATON_HAMMER);
[[fallthrough]];
case ED_LONGSHOT:
killed = killed || CanUse(RG_LONGSHOT);
[[fallthrough]];
case ED_FAR:
killed = killed || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW);
break;
}
return killed;
default:
SPDLOG_ERROR("CanKillEnemy reached `default`.");
assert(false);
@ -841,6 +864,7 @@ bool Logic::CanPassEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
return CanUse(RG_HOOKSHOT) || CanUse(RG_SUNS_SONG);
case RE_IRON_KNUCKLE:
case RE_BIG_OCTO:
case RE_WALLTULA: //consistent with RT_SPIRIT_WALL
return false;
case RE_GREEN_BUBBLE:
return TakeDamage() || CanUse(RG_NUTS) || CanUse(RG_BOOMERANG) || CanUse(RG_HOOKSHOT);
@ -887,6 +911,7 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity
case RE_WALLMASTER:
case RE_ANUBIS:
case RE_PURPLE_LEEVER:
case RE_WALLTULA:
return true;
case RE_BEAMOS:
return !grounded || CanUse(RG_NUTS) || CanUse(RG_DINS_FIRE) ||
@ -899,6 +924,8 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity
case RE_BLUE_BUBBLE:
// RANDOTODO Trick to use shield hylian shield as child to stun these guys
return !grounded || CanUse(RG_NUTS) || HookshotOrBoomerang() || CanStandingShield();
case RE_TORCH_SLUG:
return !grounded || CanUse(RG_NUTS) || CanUse(RG_HOOKSHOT) || CanUse(RG_DINS_FIRE);
default:
SPDLOG_ERROR("CanPassEnemy reached `default`.");
assert(false);
@ -2331,6 +2358,32 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) {
inLogic[logicVal] = value;
}
bool Logic::IsReverseAccessPossible(){
return ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES) &&
(ctx->GetOption(RSK_DECOUPLED_ENTRANCES) ||
ctx->GetOption(RSK_MIX_BOSS_ENTRANCES));
}
bool Logic::SpiritBrokenWallToStatue() {
return /*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS)));
}
bool Logic::SpiritEastToSwitch() {
return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT));
}
bool Logic::SpiritWestToSkull() {
return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW);
}
bool Logic::MQSpiritStatueToSunBlock() {
return IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME);
}
bool Logic::MQSpiritStatueSouthDoor() {
return HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && CanUse(RG_FAIRY_BOW) && CanUse(RG_SONG_OF_TIME)/* && CanClimb()*/);
}
void Logic::Reset() {
NewSaveContext();
StartPerformanceTimer(PT_LOGIC_RESET);
@ -2520,11 +2573,15 @@ void Logic::Reset() {
MQSpirit3SunsEnemies = false;
MQSpiritOpenedBigMirrorCave = false;
Spirit1FSilverRupees = false;
SpiritChildStalfosBridge = false;
SpiritChildTorchesBridge = false;
SpiritSunBlockTorch = false;
SpiritAdultLobbySwitch = false;
SpiritChildSwitchBridge = false;
SpiritRupeeBridge = false;
SpiritBouldersSilvers = false;
SpiritPlatformLowered = false;
Spirit4FSwitch = false;
ReverseSpiritChild = false;
ReverseSpiritAdult = false;
StopPerformanceTimer(PT_LOGIC_RESET);
}
} // namespace Rando

View file

@ -26,6 +26,7 @@ enum class GlitchDifficulty {
HERO,
};
class Logic {
public:
bool noVariable = false;
@ -182,10 +183,14 @@ class Logic {
bool MQSpirit3SunsEnemies = false;
bool MQSpiritOpenedBigMirrorCave = false;
bool Spirit1FSilverRupees = false;
bool SpiritChildStalfosBridge = false;
bool SpiritChildTorchesBridge = false;
bool SpiritSunBlockTorch = false;
bool SpiritAdultLobbySwitch = false;
bool SpiritChildSwitchBridge = false;
bool SpiritRupeeBridge = false;
bool SpiritBouldersSilvers = false;
bool SpiritStatueRoomSouthDoor = false;
bool SpiritPlatformLowered = false;
bool Spirit4FSwitch = false;
bool ReverseSpiritChild = false;
bool ReverseSpiritAdult = true;
/* --- END OF HELPERS AND LOCATION ACCESS --- */
@ -294,6 +299,12 @@ class Logic {
static std::map<uint32_t, uint32_t> RandoGetToDungeonScene;
static std::map<RandomizerGet, uint32_t> RandoGetToEquipFlag;
static std::map<RandomizerGet, uint32_t> RandoGetToRandInf;
bool Logic::IsReverseAccessPossible();
bool Logic::SpiritBrokenWallToStatue();
bool Logic::SpiritEastToSwitch();
bool Logic::SpiritWestToSkull();
bool Logic::MQSpiritStatueToSunBlock();
bool Logic::MQSpiritStatueSouthDoor();
private:
std::shared_ptr<Context> ctx;

View file

@ -8,6 +8,8 @@
#define TWO_ACTOR_PARAMS(a, b) ((((a)&0xFFFF) << 16) | ((b)&0xFFFF))
typedef bool (*ConditionFn)();
// This should probably go in a less rando-specific location
// but the best location will probably be in the modding engine
// which doesn't exist yet.
@ -844,43 +846,50 @@ typedef enum {
RR_WATER_TEMPLE_BOSS_ROOM,
RR_SPIRIT_TEMPLE_LOBBY,
RR_SPIRIT_TEMPLE_CHILD_LOBBY,
RR_SPIRIT_TEMPLE_CHILD_STALFOS,
RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE,
RR_SPIRIT_TEMPLE_CHILD_ANUBIS,
RR_SPIRIT_TEMPLE_CHILD_TORCHES,
RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE,
RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB,
RR_SPIRIT_TEMPLE_CHILD_CLIMB,
RR_SPIRIT_TEMPLE_ADULT_LOBBY,
RR_SPIRIT_TEMPLE_ADULT_SAND_PIT,
RR_SPIRIT_TEMPLE_ADULT_BOULDERS,
RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS,
RR_SPIRIT_TEMPLE_ADULT_CLIMB,
RR_SPIRIT_TEMPLE_1F_WEST,
RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH,
RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH,
RR_SPIRIT_TEMPLE_1F_ANUBIS,
RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH,
RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH,
RR_SPIRIT_TEMPLE_1F_BOXES,
RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE,
RR_SPIRIT_TEMPLE_BROKEN_WALL,
RR_SPIRIT_TEMPLE_1F_EAST,
RR_SPIRIT_TEMPLE_SAND_PIT,
RR_SPIRIT_TEMPLE_BOULDERS,
RR_SPIRIT_TEMPLE_ABOVE_BOULDERS,
RR_SPIRIT_TEMPLE_PAST_BOULDERS,
RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE,
RR_SPIRIT_TEMPLE_2F_MIRROR,
RR_SPIRIT_TEMPLE_STATUE_ROOM,
RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST,
RR_SPIRIT_TEMPLE_GS_LEDGE,
RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST,
RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE,
RR_SPIRIT_TEMPLE_BLOCK_PUZZLE,
RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND,
RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE,
RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND,
RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND,
RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS,
RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH,
RR_SPIRIT_TEMPLE_SHORTCUT,
RR_SPIRIT_TEMPLE_EMPTY_STAIRS,
RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM,
RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS,
RR_SPIRIT_TEMPLE_WEST_THRONE,
RR_SPIRIT_TEMPLE_WEST_HAND_EXIT,
RR_SPIRIT_TEMPLE_WEST_HAND,
RR_SPIRIT_TEMPLE_POT_STAIRS,
RR_SPIRIT_TEMPLE_BEAMOS_PITS,
RR_SPIRIT_TEMPLE_FOUR_ARMOS,
RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM,
RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND,
RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE,
RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND,
RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND,
RR_SPIRIT_TEMPLE_CHEST_STAIRS,
RR_SPIRIT_TEMPLE_EAST_THRONE,
RR_SPIRIT_TEMPLE_EAST_HAND_EXIT,
RR_SPIRIT_TEMPLE_EAST_HAND,
RR_SPIRIT_TEMPLE_BIG_WALL_LOWER,
RR_SPIRIT_TEMPLE_BIG_WALL_UPPER,
RR_SPIRIT_TEMPLE_4F_CENTRAL,
RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM,
RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM,
RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM,
RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE,
RR_SPIRIT_TEMPLE_SHORTCUT,
RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD,
RR_SPIRIT_TEMPLE_PLATFORM,
RR_SPIRIT_TEMPLE_STATUE_HEAD,
RR_SPIRIT_TEMPLE_MQ_LOBBY,
RR_SPIRIT_TEMPLE_MQ_1F_WEST,
@ -893,19 +902,20 @@ typedef enum {
RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE,
RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM,
RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM,
RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST,
RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS,
RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM,
RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND,
RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE,
RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND,
RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND,
RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS,
RR_SPIRIT_TEMPLE_MQ_WEST_THRONE,
RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT,
RR_SPIRIT_TEMPLE_MQ_WEST_HAND,
RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH,
RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH,
RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST,
RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F,
RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F,
RR_SPIRIT_TEMPLE_MQ_1F_EAST,
RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM,
RR_SPIRIT_TEMPLE_MQ_SAND_PIT,
RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM,
RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM,
RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER,
@ -923,7 +933,7 @@ typedef enum {
RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM,
RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM,
RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE,
RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD,
RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD,
RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY,
RR_SPIRIT_TEMPLE_BOSS_ROOM,
@ -3665,7 +3675,7 @@ typedef enum {
RT_SHADOW_MQ_WINDY_WALKWAY,
RT_LENS_SPIRIT,
RT_SPIRIT_CHILD_CHU,
RT_SPIRIT_LOBBY_GS,
RT_SPIRIT_WEST_LEDGE,
RT_SPIRIT_LOWER_ADULT_SWITCH,
RT_SPIRIT_LOBBY_JUMP,
RT_SPIRIT_PLATFORM_HOOKSHOT,
@ -6481,6 +6491,7 @@ typedef enum {
RE_BARI,
RE_SHABOM,
RE_OCTOROK,
RE_WALLTULA,
} RandomizerEnemy;
// RANDOTODO compare child long jumpslash range with adult short

View file

@ -1006,11 +1006,11 @@ void Settings::CreateOptions() {
"Removes the requirements for the Lens of Truth in Spirit Temple.");
OPT_TRICK(RT_SPIRIT_CHILD_CHU, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE },
"Spirit Temple Child Side Bridge with Bombchu", "A carefully-timed Bombchu can hit the switch.");
OPT_TRICK(RT_SPIRIT_LOBBY_GS, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE },
"Spirit Temple Main Room GS with Boomerang",
"Standing on the highest part of the arm of the statue, a precise Boomerang throw can kill and obtain "
"this Gold Skulltula. You must throw the Boomerang slightly off to the side so that it curves into the "
"Skulltula, as aiming directly at it will clank off of the wall in front.");
OPT_TRICK(RT_SPIRIT_WEST_LEDGE, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE },
"Spirit Temple Statue Room West Ledge Checks with Boomerang",
"By carefully walking onto the upper arm of the statue, it's possible to get a good angle on the "
"Gold Skulltula (In Vanilla) and the farthest pot (In MQ) to collect the checks with Boomerang. "
"The nearest pot in MQ can be reached from the forearm and is always in logic.");
OPT_TRICK(RT_SPIRIT_LOWER_ADULT_SWITCH, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED },
"Spirit Temple Lower Adult Switch with Bombs",
"A bomb can be used to hit the switch on the ceiling, but it must be thrown from a particular distance "