mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-08-21 05:43:42 -07:00
Merge 71aab826bc
into 7b4df9bdb2
This commit is contained in:
commit
6adde8685a
7 changed files with 1301 additions and 348 deletions
|
@ -563,96 +563,6 @@ void Region::ResetVariables() {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This logic covers checks that exist in the shared areas of MQ spirit from a glitchless standpoint.
|
||||
* This room has Quantum logic that I am currently handling with this function, however this is NOT suitable for
|
||||
glitch logic as it relies on specific ages
|
||||
* In this chunk there are 3 possibilities for passing a check, but first I have to talk about parallel universes.
|
||||
|
||||
* In MQ Spirit key logic, we mostly care about 2 possibilities for how the player can spend keys, creating 2
|
||||
Parralel universes
|
||||
* In the first universe, the player did not enter spirit as adult until after climbing as child, thus child spends
|
||||
keys linearly, only needing 2 to reach statue room.
|
||||
* In the second universe, the player went in as adult, possibly out of logic, and started wasting the keys to lock
|
||||
child out.
|
||||
* These Universes converge when the player has 7 keys (meaning adult can no longer lock child out) and adult is
|
||||
known to be able to reach Statue room. This creates "Certain Access", which is tracked seperatly for each age.
|
||||
* Child Certain Access is simple, if we have 7 keys and child access, it's Certain Access.
|
||||
* Adult Certain Access is also simple, adult is not key locked, so if they make it to a location, it's Certain
|
||||
Access.
|
||||
* Things get complicated when we handle the overlap of the 2 universes,
|
||||
* though an important detail is that if we have Certain Access as either age, we don't need to checked the overlap
|
||||
because overlap logic is strictly stricter than either Certain Access.
|
||||
|
||||
* In order to track the first universe, the logic allows technical child access with the minimum number of keys,
|
||||
and then checks in this function for if we have 7 keys to determine if that is Certain or not.
|
||||
* This is for technical reasons, as areas with no access at all will simply not be checked.
|
||||
* Normally we would need to do similar shenanigans to track the second universe, however adult must have go through
|
||||
statue room to waste keys,
|
||||
* so can go back there and get new keys for Child to use if they do, and the navigation logic for shared MQ spirit
|
||||
from Statue Room is very simple for Adult.
|
||||
* Additionally, we don't need to know if adult can actually reach spirit temple or climb to statue room, because if
|
||||
the player can't do that, then universe 2 can't happen anyway,
|
||||
* and if the player does so out of logic, they can do it again, as the only consumable used sets a permanent flag.
|
||||
|
||||
* The Adult Navigation logic is as such:
|
||||
* - Broken Wall room is 6 key locked, because if the player tries to spend 6 keys in a way that would block adults
|
||||
access, they would have to give child access instead.
|
||||
* - The child side hammer switch for the time travelling chest is 7 key locked for adult
|
||||
* - Reaching gauntlets hand is 7 key locked
|
||||
* - Going back into big block room is complex, but the only check there is child only so not a concern
|
||||
* - Everything else is possible with basic adult movement, or is impossible for child to reach glitchlessly
|
||||
* Anything 7 key locked does not need to be checked as shared, as all child access is Certain and because of this
|
||||
workaround we don't need to fake Adult access, meaning that is also Certain.
|
||||
* All of this combined means that when checking if adult can reach a location in universe 2, we only have to ask if
|
||||
it is a 6 key locked location or not.
|
||||
|
||||
* Knowing all of this this, we can confirm things are logical in 3 different ways:
|
||||
* - If we have Adult Access, we know it is Certain Access, so they can get checks alone.
|
||||
* - If we have 7 keys, child has Certain Access as we know they cannot be locked out, so can get checks alone,
|
||||
otherwise we check the logical overlap
|
||||
* - If Child and Adult can get the check (ignoring actual adult access to the location), and the location is either
|
||||
not 6 key locked or we have 6 keys, we can get the check with the overlap
|
||||
*/
|
||||
bool Region::MQSpiritShared(ConditionFn condition, bool IsBrokenWall, bool anyAge) {
|
||||
// if we have Certain Access as child, we can check anyAge and if true, resolve a condition with Here as if
|
||||
// adult is here it's also Certain Access
|
||||
if (logic->SmallKeys(RR_SPIRIT_TEMPLE, 7)) {
|
||||
if (anyAge) {
|
||||
return Here(condition);
|
||||
}
|
||||
return condition();
|
||||
// else, if we are here as adult, we have Certain Access from that and don't need special handling for
|
||||
// checking adult
|
||||
} else if (Adult() && logic->IsAdult) {
|
||||
return condition();
|
||||
// if we do not have Certain Access, we need to check the overlap by seeing if we are both here as child and
|
||||
// meet the adult universe's access condition We only need to do it as child, as only child access matters
|
||||
// for this check, as adult access is assumed based on keys
|
||||
} else if (Child() && logic->IsChild && (!IsBrokenWall || logic->SmallKeys(RR_SPIRIT_TEMPLE, 6))) {
|
||||
bool result = false;
|
||||
// store current age variables
|
||||
bool pastAdult = logic->IsAdult;
|
||||
bool pastChild = logic->IsChild;
|
||||
|
||||
// First check if the check is possible as child
|
||||
logic->IsChild = true;
|
||||
logic->IsAdult = false;
|
||||
result = condition();
|
||||
// If so, check again as adult. both have to be true for result to be true
|
||||
if (result) {
|
||||
logic->IsChild = false;
|
||||
logic->IsAdult = true;
|
||||
result = condition();
|
||||
}
|
||||
|
||||
// set back age variables
|
||||
logic->IsChild = pastChild;
|
||||
logic->IsAdult = pastAdult;
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Region::printAgeTimeAccess() {
|
||||
auto message = "Child Day: " + std::to_string(childDay) +
|
||||
|
@ -673,12 +583,207 @@ bool Here(const RandomizerRegion region, ConditionFn condition) {
|
|||
return areaTable[region].Here(condition);
|
||||
}
|
||||
|
||||
bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) {
|
||||
return areaTable[region].MQSpiritShared(condition, false, anyAge);
|
||||
/*
|
||||
* This logic covers checks that exist in the shared areas of Spirit
|
||||
* This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on
|
||||
specific ages
|
||||
|
||||
* In order to pass a check, we must either determine that Access is certain,
|
||||
or that it is always possible to get a check somehow.
|
||||
|
||||
* But first I have to talk about parallel universes.
|
||||
|
||||
* In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out
|
||||
* In the second, they enter as adult and spend as many keys as they can to lock child out.
|
||||
|
||||
* Additionally, if it is possible to enter spirit in reverse, there are 2 more universes:
|
||||
* In the third universe, adult enters in reverse, and wastes all the keys so noone can enter through the front
|
||||
* In the forth, child manages to do the same, and lock people out of the front
|
||||
* However all access from the boss door in Statue Room and adjacent areas is Certain, so this is not usually
|
||||
relevant
|
||||
|
||||
* While other universes exist, such as both ages entering in reverse or
|
||||
child using their key, getting stuck, then coming back to do the dungeon as adult, these
|
||||
are all sub-possibilities of these 4 universes
|
||||
|
||||
* As we do not know which universe we are in until the player chooses one in-game,
|
||||
we must be able to collect the check in all universes
|
||||
|
||||
* When an Age can no longer be kept out in any universe, that age is said to have Certain Access to a
|
||||
region
|
||||
* If both ages have potential access to a region with a certain number of keys, but there is no Certain Access,
|
||||
* then a check is only in logic if all possible universes can collect the check independently
|
||||
|
||||
* The universes converge when the player has all the keys, giving both ages Certain Access everywhere.
|
||||
|
||||
* We must check for these universes manually as we set access vairables to true with minimum keys for
|
||||
* technical reasons as otherwise the logic code will never run
|
||||
|
||||
* The 1st and 3rd column list how many keys are needed for each age to have Certain Access from the front
|
||||
* the 2nd and 4th column list how many keys are needed for each age to have Certain Access from the boss door
|
||||
* Sometimes, we may check for a higher number of keys in the condition, this happens in cases where the number of
|
||||
keys
|
||||
* for Certain Access depends on a certain condition, the listed number is the lowest possible to make sure the
|
||||
condition is checked.
|
||||
|
||||
* The 1st condition is the combined conditions needed to move from the 1F child lock to the area being checks
|
||||
* the 2nd condition is the same for adult 1F lock, and the 3rd is the access from the boss door.
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
std::map<RandomizerRegion, SpiritLogicData> Region::spiritLogicData = {
|
||||
//Vanilla Child uses ExplosiveKeyLogic here because they need to exist for shared adult checks
|
||||
{RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, {5, 0, 3, 0, []{return true;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, {5, 0, 3, 0, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_2F_MIRROR, {5, 0, 3, 0, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritSunOnFloorToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}},
|
||||
{RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_INNER_WEST_HAND, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_GS_LEDGE, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_STATUE_ROOM, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic();}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true;}}},
|
||||
//Assumes SpiritSunBlockSouthLedge() for all access
|
||||
{RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, {5, 5, 3, 3, []{return logic->OuterWestHandLogic();}, []{return logic->OuterWestHandLogic();}, []{return logic->OuterWestHandLogic();}}},
|
||||
{RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}},
|
||||
{RR_SPIRIT_TEMPLE_INNER_EAST_HAND, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}},
|
||||
{RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT) && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));}}},
|
||||
//MQ /*&& logic->CanClimbHigh()*/
|
||||
{RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, {7, 6, 7, 7, []{return logic->StatueRoomMQKeyLogic();}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->StatueRoomMQKeyLogic() && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, {7, 6, 7, 7, []{return logic->StatueRoomMQKeyLogic() && logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->StatueRoomMQKeyLogic()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;}}},
|
||||
{RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}},
|
||||
{RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, {7, 7, 4, 4, []{return logic->CanHitSwitch() && logic->OuterWestHandMQLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->OuterWestHandMQLogic();}, []{return logic->OuterWestHandMQLogic();}}},
|
||||
{RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, {7, 0, 0, 0, []{return logic->CanHitSwitch() &&
|
||||
areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();})
|
||||
/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});}}},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
bool SpiritCertainAccess(RandomizerRegion region) {
|
||||
SpiritLogicData& curRegionData = Region::spiritLogicData[region];
|
||||
if (logic->IsChild) {
|
||||
uint8_t keys = curRegionData.childKeys;
|
||||
uint8_t revKeys = curRegionData.childRevKeys;
|
||||
bool knownFrontAccess = logic->ForwardsSpiritChild || !logic->IsReverseAccessPossible();
|
||||
// If we have enough keys that an age cannot be kept out, we have Certain Access
|
||||
// otherwise if we have entered in reverse and can reach from the face, we have Certain Access
|
||||
return ((knownFrontAccess && curRegionData.childAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) ||
|
||||
((logic->ReverseSpiritChild && curRegionData.reverseAccess()) &&
|
||||
logic->SmallKeys(RR_SPIRIT_TEMPLE, revKeys)) ||
|
||||
(curRegionData.childAccess() && curRegionData.reverseAccess() &&
|
||||
logic->SmallKeys(RR_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys));
|
||||
} else {
|
||||
uint8_t keys = curRegionData.adultKeys;
|
||||
uint8_t revKeys = curRegionData.adultRevKeys;
|
||||
bool knownFrontAccess = logic->ForwardsSpiritAdult || !logic->IsReverseAccessPossible();
|
||||
// If we have enough keys that an age cannot be kept out, we have Certain Access
|
||||
// otherwise if we have entered in reverse and can reach from the face, we have Certain Access
|
||||
return ((knownFrontAccess && curRegionData.adultAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) ||
|
||||
((logic->ReverseSpiritAdult && curRegionData.reverseAccess()) &&
|
||||
logic->SmallKeys(RR_SPIRIT_TEMPLE, revKeys)) ||
|
||||
(curRegionData.adultAccess() && curRegionData.reverseAccess() &&
|
||||
logic->SmallKeys(RR_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys));
|
||||
}
|
||||
}
|
||||
|
||||
bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) {
|
||||
return areaTable[region].MQSpiritShared(condition, true, anyAge);
|
||||
/*
|
||||
Spirit Shared can take up to 3 regions, this is because checks can exist in many regions at the same time
|
||||
and the logic needs to be able to check the access logic from those regions to check the other universes properly.
|
||||
|
||||
anyAge is equivalent to a self referencing Here, used for events and any check where that is relevent.
|
||||
*/
|
||||
|
||||
bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion,
|
||||
ConditionFn otherCondition, RandomizerRegion thirdRegion, ConditionFn thirdCondition) {
|
||||
SpiritLogicData& curRegionData = Region::spiritLogicData[region];
|
||||
bool result = false;
|
||||
|
||||
// store current age variables
|
||||
bool pastAdult = logic->IsAdult;
|
||||
bool pastChild = logic->IsChild;
|
||||
|
||||
logic->IsChild = true;
|
||||
logic->IsAdult = false;
|
||||
|
||||
bool ChildCertainAccess = SpiritCertainAccess(region);
|
||||
|
||||
// Switch back to adult to check adult access
|
||||
logic->IsChild = false;
|
||||
logic->IsAdult = true;
|
||||
|
||||
bool AdultCertainAccess = SpiritCertainAccess(region);
|
||||
// If we are AnyAge and have any CertainAccess, then we can check those ages
|
||||
// we don't need to check ambiguity here as if this fails, then 1 of the ages has failed
|
||||
if (anyAge && (ChildCertainAccess || AdultCertainAccess)) {
|
||||
// set age access to the Certain Access
|
||||
logic->IsChild = ChildCertainAccess;
|
||||
logic->IsAdult = AdultCertainAccess;
|
||||
|
||||
// check condition as well as having at least child or adult access
|
||||
result = condition();
|
||||
|
||||
// otherwise, we have to check the current age and...
|
||||
} else if (areaTable[region].Child() && pastChild) {
|
||||
// Switch to Child
|
||||
logic->IsChild = true;
|
||||
logic->IsAdult = false;
|
||||
|
||||
result = condition();
|
||||
// If we have Certain Access, we just run the condition.
|
||||
// Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Adult
|
||||
// and if needed, in reverse
|
||||
if (!ChildCertainAccess && result) {
|
||||
// Switch to Adult
|
||||
logic->IsChild = false;
|
||||
logic->IsAdult = true;
|
||||
|
||||
// If Adult can get there and get the check, we can get the check in logic
|
||||
// If reverse spirit is also possible, we need to make sure Adult can get it via reverse entry too
|
||||
result =
|
||||
(curRegionData.adultAccess() &&
|
||||
(!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) ||
|
||||
(otherRegion != RR_NONE &&
|
||||
(Region::spiritLogicData[otherRegion].adultAccess() &&
|
||||
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) &&
|
||||
otherCondition())) ||
|
||||
(thirdRegion != RR_NONE &&
|
||||
(Region::spiritLogicData[thirdRegion].adultAccess() &&
|
||||
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) &&
|
||||
thirdCondition()));
|
||||
}
|
||||
} else if (areaTable[region].Adult() && pastAdult) {
|
||||
result = condition();
|
||||
// if we have enough keys to have Certain Access, we just run the condition
|
||||
// Alternatively, if we have entered in reverse and can reach from the face, we have Certain Access
|
||||
// Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Child
|
||||
// and if needed, in reverse
|
||||
if (!AdultCertainAccess && result) {
|
||||
// Switch to Child
|
||||
logic->IsChild = true;
|
||||
logic->IsAdult = false;
|
||||
|
||||
// If Child can get there and get the check, we can get the check in logic
|
||||
// If reverse spirit is also possible, we need to make sure Child can get it via reverse entry too
|
||||
result =
|
||||
(curRegionData.childAccess() &&
|
||||
(!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()) && condition()) ||
|
||||
(otherRegion != RR_NONE &&
|
||||
(Region::spiritLogicData[otherRegion].childAccess() &&
|
||||
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) &&
|
||||
otherCondition())) ||
|
||||
(thirdRegion != RR_NONE &&
|
||||
(Region::spiritLogicData[thirdRegion].childAccess() &&
|
||||
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) &&
|
||||
thirdCondition()));
|
||||
}
|
||||
}
|
||||
// set back age variables
|
||||
logic->IsChild = pastChild;
|
||||
logic->IsAdult = pastAdult;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BeanPlanted(const RandomizerRegion region) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "soh/Enhancements/randomizer/randomizerTypes.h"
|
||||
#include "soh/Enhancements/randomizer/context.h"
|
||||
#include "soh/Enhancements/randomizer/logic.h"
|
||||
#include "soh/Enhancements/randomizer/dungeon.h"
|
||||
|
||||
#define TIME_PASSES true
|
||||
#define TIME_DOESNT_PASS false
|
||||
|
@ -104,6 +105,27 @@ class Entrance;
|
|||
enum class EntranceType;
|
||||
} // namespace Rando
|
||||
|
||||
struct SpiritLogicData {
|
||||
// the minimum number of keys that guarantees Child can reach this region
|
||||
uint8_t childKeys;
|
||||
// The minimum number of keys that guarantees Child can reach this region if they have reverse access
|
||||
uint8_t childRevKeys;
|
||||
// the minimum number of keys that guarantees Adult can reach this region
|
||||
uint8_t adultKeys;
|
||||
// the minimum number of keys that guarantees Adult can reach this region with reverse entry
|
||||
uint8_t adultRevKeys;
|
||||
// The area access condition to reach this region as Child, from the first lock,
|
||||
// including the minimum number of keys for ambiguous access
|
||||
// 1 key is always assumed to be required
|
||||
ConditionFn childAccess;
|
||||
// The area access condition to reach this region as Adult, from the first lock
|
||||
// including the minimum number of keys for ambiguous access
|
||||
// 1 key is always assumed to be required on vanilla
|
||||
ConditionFn adultAccess;
|
||||
// The area access condition to reach this region from the boss door,
|
||||
ConditionFn reverseAccess;
|
||||
};
|
||||
|
||||
class Region {
|
||||
public:
|
||||
Region();
|
||||
|
@ -212,11 +234,11 @@ class Region {
|
|||
|
||||
bool CanPlantBeanCheck() const;
|
||||
bool AllAccountedFor() const;
|
||||
bool MQSpiritShared(ConditionFn condition, bool IsBrokenWall, bool anyAge = false);
|
||||
|
||||
void ResetVariables();
|
||||
|
||||
void printAgeTimeAccess();
|
||||
static std::map<RandomizerRegion, SpiritLogicData> spiritLogicData;
|
||||
};
|
||||
|
||||
extern std::array<Region, RR_MAX> areaTable;
|
||||
|
@ -225,8 +247,11 @@ 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 MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false);
|
||||
bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false);
|
||||
bool SpiritShared(
|
||||
RandomizerRegion region, ConditionFn condition, bool anyAge = false, RandomizerRegion otherRegion = RR_NONE,
|
||||
ConditionFn otherCondition = [] { return false; }, RandomizerRegion thirdRegion = RR_NONE,
|
||||
ConditionFn thirdCondition = [] { return false; });
|
||||
bool SpiritCertainAccess(RandomizerRegion region);
|
||||
bool CanPlantBean(const RandomizerRegion region);
|
||||
bool BothAges(const RandomizerRegion region);
|
||||
bool ChildCanAccess(const RandomizerRegion region);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -543,14 +543,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:
|
||||
|
@ -580,15 +577,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:
|
||||
|
@ -633,7 +630,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:
|
||||
|
@ -714,12 +711,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:
|
||||
|
@ -797,6 +793,35 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
|
|||
case RE_OCTOROK:
|
||||
return CanReflectNuts() || HookshotOrBoomerang() || CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT) ||
|
||||
CanUse(RG_BOMB_BAG) || (wallOrFloor && CanUse(RG_BOMBCHU_5));
|
||||
case RE_WALLTULA:
|
||||
switch (distance) {
|
||||
case ED_CLOSE:
|
||||
case ED_SHORT_JUMPSLASH:
|
||||
killed = CanUse(RG_KOKIRI_SWORD);
|
||||
[[fallthrough]];
|
||||
case ED_MASTER_SWORD_JUMPSLASH:
|
||||
killed = killed || CanUse(RG_MASTER_SWORD);
|
||||
[[fallthrough]];
|
||||
case ED_LONG_JUMPSLASH:
|
||||
killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS);
|
||||
[[fallthrough]];
|
||||
case ED_BOMB_THROW:
|
||||
killed = killed || (!inWater && CanUse(RG_BOMB_BAG)) || CanUse(RG_DINS_FIRE);
|
||||
[[fallthrough]];
|
||||
case ED_BOOMERANG:
|
||||
killed = killed || CanUse(RG_BOOMERANG);
|
||||
[[fallthrough]];
|
||||
case ED_HOOKSHOT:
|
||||
killed = killed || CanUse(RG_HOOKSHOT) || CanUse(RG_BOMBCHU_5) || CanUse(RG_MEGATON_HAMMER);
|
||||
[[fallthrough]];
|
||||
case ED_LONGSHOT:
|
||||
killed = killed || CanUse(RG_LONGSHOT);
|
||||
[[fallthrough]];
|
||||
case ED_FAR:
|
||||
killed = killed || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW);
|
||||
break;
|
||||
}
|
||||
return killed;
|
||||
default:
|
||||
SPDLOG_ERROR("CanKillEnemy reached `default`.");
|
||||
assert(false);
|
||||
|
@ -855,6 +880,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);
|
||||
|
@ -901,9 +927,10 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity
|
|||
case RE_WALLMASTER:
|
||||
case RE_ANUBIS:
|
||||
case RE_PURPLE_LEEVER:
|
||||
case RE_WALLTULA:
|
||||
return true;
|
||||
case RE_BEAMOS:
|
||||
return !grounded || CanUse(RG_NUTS) ||
|
||||
return !grounded || CanUse(RG_NUTS) || CanUse(RG_DINS_FIRE) ||
|
||||
(quantity == 1 && (CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT)));
|
||||
case RE_MAD_SCRUB:
|
||||
return !grounded || CanUse(RG_NUTS);
|
||||
|
@ -913,8 +940,10 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity
|
|||
case RE_BLUE_BUBBLE:
|
||||
// RANDOTODO Trick to use shield hylian shield as child to stun these guys
|
||||
return !grounded || CanUse(RG_NUTS) || HookshotOrBoomerang() || CanStandingShield();
|
||||
case RE_TORCH_SLUG:
|
||||
return !grounded || CanUse(RG_NUTS) || CanUse(RG_HOOKSHOT) || CanUse(RG_DINS_FIRE);
|
||||
default:
|
||||
SPDLOG_ERROR("CanPassEnemy reached `default`.");
|
||||
SPDLOG_ERROR("CanAvoidEnemy reached `default`.");
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
@ -1268,6 +1297,10 @@ bool Logic::HasFireSourceWithTorch() {
|
|||
return HasFireSource() || CanUse(RG_STICKS);
|
||||
}
|
||||
|
||||
bool Logic::SunlightArrows() {
|
||||
return ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS);
|
||||
}
|
||||
|
||||
// Is this best off signaling what you have already traded, or what step you are currently on?
|
||||
bool Logic::TradeQuestStep(RandomizerGet rg) {
|
||||
if (ctx->GetOption(RSK_SHUFFLE_ADULT_TRADE)) {
|
||||
|
@ -2332,6 +2365,108 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) {
|
|||
inLogic[logicVal] = value;
|
||||
}
|
||||
|
||||
bool Logic::IsReverseAccessPossible() {
|
||||
// If we ever allow dungeon entrances to connect to boss rooms directly in dungeon chains, or for 1 boss door to
|
||||
// lead to another dungeons boss door, add RSK_MIX_DUNGEON_ENTRANCES to the final condition
|
||||
// RANDOTODO Check for Age-Locked Boss entrances + decoupled + Ganon's tower when it is shuffled
|
||||
return !ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) &&
|
||||
((ctx->GetOption(RSK_DECOUPLED_ENTRANCES) &&
|
||||
ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_FULL)) ||
|
||||
(ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) &&
|
||||
(ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) || ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES))));
|
||||
}
|
||||
|
||||
bool Logic::SpiritSunOnFloorToStatue() {
|
||||
return /*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS)));
|
||||
}
|
||||
|
||||
bool Logic::SpiritExplosiveKeyLogic() {
|
||||
return SmallKeys(RR_SPIRIT_TEMPLE, HasExplosives() ? 1 : 2);
|
||||
}
|
||||
|
||||
bool Logic::SpiritWestToSkull() {
|
||||
return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW);
|
||||
}
|
||||
|
||||
bool Logic::SpiritSunBlockSouthLedge() {
|
||||
// It's also possible to do a backwalk hover + backflip if you equip hovers as you start the backwalk to accelerate
|
||||
// faster
|
||||
return true /*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() ||
|
||||
(CanUse(RG_HOOKSHOT) && (HasFireSource() ||
|
||||
(SpiritSunBlockTorch && (logic->CanUse(STICKS) ||
|
||||
(ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))))*/
|
||||
;
|
||||
}
|
||||
|
||||
bool Logic::SpiritEastToSwitch() {
|
||||
return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) ||
|
||||
(CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT));
|
||||
}
|
||||
|
||||
// Combines crossing the ledge directly and the jump from the hand
|
||||
bool Logic::MQSpiritWestToPots() {
|
||||
return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME);
|
||||
}
|
||||
|
||||
bool Logic::MQSpiritStatueToSunBlock() {
|
||||
return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) ||
|
||||
CanUse(RG_SONG_OF_TIME) /* || CanBunnyJump()*/) /* && str0*/;
|
||||
}
|
||||
|
||||
bool Logic::MQSpiritStatueSouthDoor() {
|
||||
return HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && CanUse(RG_FAIRY_BOW) &&
|
||||
CanUse(RG_SONG_OF_TIME) /* && CanClimb()*/);
|
||||
}
|
||||
|
||||
bool Logic::MQSpirit4KeyColossus() {
|
||||
// !QUANTUM LOGIC!
|
||||
// We only need 4 keys and the ability to reach both hands for adult to logically be able to drop down onto Desert
|
||||
// Colossus This is because there are only 3 keys that can be wasted without opening up either this lock to East
|
||||
// hand, or the West Hand lock through Sun Block Room and both directions allow you to drop onto colossus
|
||||
// logic->CanKillEnemy(RE_FLOORMASTER) is implied
|
||||
return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) &&
|
||||
CanJumpslash() && /*(str0 || SunlightArrows) &&*/
|
||||
(ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || CanUse(RG_LENS_OF_TRUTH)) && CanKillEnemy(RE_IRON_KNUCKLE) &&
|
||||
CanUse(RG_HOOKSHOT);
|
||||
}
|
||||
|
||||
bool Logic::MQSpirit4KeyWestHand() {
|
||||
// !QUANTUM LOGIC!
|
||||
// Continuing from MQSpirit4KeyColossus, if we also have a longshot, we can go from the East hand to the West hand,
|
||||
// meaning we always have access to East Hand
|
||||
return CanUse(RG_LONGSHOT) && MQSpirit4KeyColossus();
|
||||
}
|
||||
// This version of the function handles Shared Access for child, based on what adult could do if they existed
|
||||
bool Logic::CouldMQSpirit4KeyWestHand() {
|
||||
return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) &&
|
||||
(HasItem(RG_MASTER_SWORD) || HasItem(RG_BIGGORON_SWORD) || HasItem(RG_MEGATON_HAMMER)) &&
|
||||
/*(str0 || SunlightArrows) &&*/
|
||||
(ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || CanUse(RG_LENS_OF_TRUTH)) && HasItem(RG_LONGSHOT);
|
||||
}
|
||||
|
||||
// !QUANTUM LOGIC!
|
||||
// With 3 keys, you cannot lock adult out of leaving spirit onto the hands and jumping down, as you would have to
|
||||
// open the west hand door and then adult could climb through sun block room to jump down from there
|
||||
// This requires that adult can complete both routes
|
||||
// If we have the longshot, we can also guarantee access to the outer west hand as you can longshot from the east hand
|
||||
// to the west Implies CanKillEnemy(RE_IRON_KNUCKLE)
|
||||
bool Logic::OuterWestHandLogic() {
|
||||
return HasExplosives() /* && CanClimbHigh() && str0*/ && SmallKeys(RR_SPIRIT_TEMPLE, HasItem(RG_LONGSHOT) ? 3 : 5);
|
||||
}
|
||||
|
||||
bool Logic::OuterWestHandMQLogic() {
|
||||
return MQSpiritStatueToSunBlock() && SmallKeys(RR_SPIRIT_TEMPLE, CouldMQSpirit4KeyWestHand() ? 4 : 7);
|
||||
}
|
||||
|
||||
bool Logic::StatueRoomMQKeyLogic() {
|
||||
// !QUANTUM LOGIC!
|
||||
// If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys,
|
||||
// the ability to hit switches and the ability to climb because only child can reach the initial child lock
|
||||
// without opening the Statue room to Broken Wall Room lock first
|
||||
// if adult can ever cross crawlspaces this becomes more complicated.
|
||||
return SmallKeys(RR_SPIRIT_TEMPLE, IsChild && ReverseSpiritChild && CanHitSwitch() /* && CanClimbHigh()*/ ? 6 : 7);
|
||||
}
|
||||
|
||||
void Logic::Reset(bool resetSaveContext /*= true*/) {
|
||||
if (resetSaveContext) {
|
||||
NewSaveContext();
|
||||
|
@ -2514,6 +2649,7 @@ void Logic::Reset(bool resetSaveContext /*= true*/) {
|
|||
MQGTGRightSideSwitch = false;
|
||||
GTGPlatformSilverRupees = false;
|
||||
MQJabuHolesRoomDoor = false;
|
||||
JabuRutoIn1F = false;
|
||||
JabuWestTentacle = false;
|
||||
JabuEastTentacle = false;
|
||||
JabuNorthTentacle = false;
|
||||
|
@ -2526,11 +2662,25 @@ void Logic::Reset(bool resetSaveContext /*= true*/) {
|
|||
MQWaterB1Switch = false;
|
||||
// MQWaterPillarSoTBlock = false;
|
||||
MQWaterOpenedPillarB1 = false;
|
||||
MQSpiritGibdosCleared = false;
|
||||
MQSpiritCrawlBoulder = false;
|
||||
MQSpiritMapRoomEnemies = false;
|
||||
MQSpiritTimeTravelChest = false;
|
||||
MQSpiritStatueRoomTorches = false;
|
||||
MQSpirit3SunsEnemies = false;
|
||||
MQSpiritSymphonyRoomDoor = false;
|
||||
MQSpiritBigWallSilvers = false;
|
||||
Spirit1FSilverRupees = false;
|
||||
JabuRutoIn1F = false;
|
||||
SpiritChildSwitchBridge = false;
|
||||
SpiritRupeeBridge = false;
|
||||
SpiritSunBlockTorch = false;
|
||||
SpiritBouldersSilvers = false;
|
||||
SpiritStatueRoomSouthDoor = false;
|
||||
SpiritPlatformLowered = false;
|
||||
Spirit4FSwitch = false;
|
||||
SpiritPushed4FMirrors = false;
|
||||
ReverseSpiritChild = false;
|
||||
ReverseSpiritAdult = false;
|
||||
|
||||
CalculatingAvailableChecks = false;
|
||||
|
||||
|
|
|
@ -163,6 +163,7 @@ class Logic {
|
|||
bool MQGTGRightSideSwitch = false;
|
||||
bool GTGPlatformSilverRupees = false;
|
||||
bool MQJabuHolesRoomDoor = false;
|
||||
bool JabuRutoIn1F = false;
|
||||
bool JabuWestTentacle = false;
|
||||
bool JabuEastTentacle = false;
|
||||
bool JabuNorthTentacle = false;
|
||||
|
@ -175,12 +176,27 @@ class Logic {
|
|||
bool MQWaterB1Switch = false;
|
||||
// bool MQWaterPillarSoTBlock = false; should be irrelevant. SHOULD.
|
||||
bool MQWaterOpenedPillarB1 = false;
|
||||
bool MQSpiritGibdosCleared = false;
|
||||
bool MQSpiritCrawlBoulder = false;
|
||||
bool MQSpiritMapRoomEnemies = false;
|
||||
bool MQSpiritTimeTravelChest = false;
|
||||
bool MQSpiritStatueRoomTorches = false;
|
||||
bool MQSpirit3SunsEnemies = false;
|
||||
bool MQSpiritSymphonyRoomDoor = false;
|
||||
bool MQSpiritBigWallSilvers = false;
|
||||
bool Spirit1FSilverRupees = false;
|
||||
bool JabuRutoIn1F = false;
|
||||
bool SpiritChildSwitchBridge = false;
|
||||
bool SpiritRupeeBridge = false;
|
||||
bool SpiritSunBlockTorch = false;
|
||||
bool SpiritBouldersSilvers = false;
|
||||
bool SpiritStatueRoomSouthDoor = false;
|
||||
bool SpiritPlatformLowered = false;
|
||||
bool Spirit4FSwitch = false;
|
||||
bool SpiritPushed4FMirrors = false;
|
||||
bool ReverseSpiritChild = false;
|
||||
bool ReverseSpiritAdult = false;
|
||||
bool ForwardsSpiritChild = false;
|
||||
bool ForwardsSpiritAdult = false;
|
||||
|
||||
/* --- END OF HELPERS AND LOCATION ACCESS --- */
|
||||
|
||||
|
@ -251,6 +267,7 @@ class Logic {
|
|||
bool CanBreakSmallCrates();
|
||||
bool HasFireSource();
|
||||
bool HasFireSourceWithTorch();
|
||||
bool SunlightArrows();
|
||||
bool TradeQuestStep(RandomizerGet rg);
|
||||
bool CanStandingShield();
|
||||
bool CanShield();
|
||||
|
@ -291,6 +308,21 @@ 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 IsReverseAccessPossible();
|
||||
bool SpiritSunOnFloorToStatue();
|
||||
bool SpiritEastToSwitch();
|
||||
bool SpiritWestToSkull();
|
||||
bool SpiritSunBlockSouthLedge();
|
||||
bool MQSpiritWestToPots();
|
||||
bool MQSpiritStatueToSunBlock();
|
||||
bool MQSpiritStatueSouthDoor();
|
||||
bool MQSpirit4KeyColossus();
|
||||
bool MQSpirit4KeyWestHand();
|
||||
bool CouldMQSpirit4KeyWestHand();
|
||||
bool OuterWestHandLogic();
|
||||
bool OuterWestHandMQLogic();
|
||||
bool SpiritExplosiveKeyLogic();
|
||||
bool StatueRoomMQKeyLogic();
|
||||
|
||||
private:
|
||||
std::shared_ptr<Context> ctx;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#define TWO_ACTOR_PARAMS(a, b) ((((a)&0xFFFF) << 16) | ((b)&0xFFFF))
|
||||
|
||||
typedef bool (*ConditionFn)();
|
||||
|
||||
// This should probably go in a less rando-specific location
|
||||
// but the best location will probably be in the modding engine
|
||||
// which doesn't exist yet.
|
||||
|
@ -886,50 +888,103 @@ typedef enum {
|
|||
RR_WATER_TEMPLE_BOSS_ROOM,
|
||||
|
||||
RR_SPIRIT_TEMPLE_LOBBY,
|
||||
RR_SPIRIT_TEMPLE_CHILD,
|
||||
RR_SPIRIT_TEMPLE_CHILD_CLIMB,
|
||||
RR_SPIRIT_TEMPLE_EARLY_ADULT,
|
||||
RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER,
|
||||
RR_SPIRIT_TEMPLE_OUTDOOR_HANDS,
|
||||
RR_SPIRIT_TEMPLE_BEYOND_CENTRAL_LOCKED_DOOR,
|
||||
RR_SPIRIT_TEMPLE_BEYOND_FINAL_LOCKED_DOOR,
|
||||
RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD,
|
||||
RR_SPIRIT_TEMPLE_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_SUN_ON_FLOOR,
|
||||
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_INNER_WEST_HAND,
|
||||
RR_SPIRIT_TEMPLE_GS_LEDGE,
|
||||
RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST,
|
||||
RR_SPIRIT_TEMPLE_INNER_EAST_HAND,
|
||||
RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH,
|
||||
RR_SPIRIT_TEMPLE_SHORTCUT,
|
||||
RR_SPIRIT_TEMPLE_EMPTY_STAIRS,
|
||||
RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM,
|
||||
RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE,
|
||||
RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS,
|
||||
RR_SPIRIT_TEMPLE_WEST_THRONE,
|
||||
RR_SPIRIT_TEMPLE_WEST_HAND_EXIT,
|
||||
RR_SPIRIT_TEMPLE_OUTER_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_CHEST_STAIRS,
|
||||
RR_SPIRIT_TEMPLE_EAST_THRONE,
|
||||
RR_SPIRIT_TEMPLE_EAST_HAND_EXIT,
|
||||
RR_SPIRIT_TEMPLE_OUTER_EAST_HAND,
|
||||
RR_SPIRIT_TEMPLE_BIG_WALL_BASE,
|
||||
RR_SPIRIT_TEMPLE_BIG_WALL_UPPER,
|
||||
RR_SPIRIT_TEMPLE_4F_CENTRAL,
|
||||
RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM,
|
||||
RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM,
|
||||
RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE,
|
||||
RR_SPIRIT_TEMPLE_PLATFORM,
|
||||
RR_SPIRIT_TEMPLE_STATUE_HEAD,
|
||||
|
||||
RR_SPIRIT_TEMPLE_MQ_LOBBY,
|
||||
RR_SPIRIT_TEMPLE_MQ_1F_WEST,
|
||||
RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH,
|
||||
RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH,
|
||||
RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH,
|
||||
RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH,
|
||||
RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH,
|
||||
RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH,
|
||||
RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE,
|
||||
RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR,
|
||||
RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST,
|
||||
RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND,
|
||||
RR_SPIRIT_TEMPLE_MQ_POT_LEDGE,
|
||||
RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS,
|
||||
RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE,
|
||||
RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND,
|
||||
RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS,
|
||||
RR_SPIRIT_TEMPLE_MQ_WEST_THRONE,
|
||||
RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT,
|
||||
RR_SPIRIT_TEMPLE_MQ_OUTER_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_INNER_EAST_HAND,
|
||||
RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE,
|
||||
RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F,
|
||||
RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F,
|
||||
RR_SPIRIT_TEMPLE_MQ_1F_EAST,
|
||||
RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_SAND_PIT,
|
||||
RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER,
|
||||
RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_LOWER,
|
||||
RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER,
|
||||
RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER,
|
||||
RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS,
|
||||
RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND,
|
||||
RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE,
|
||||
RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND,
|
||||
RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS,
|
||||
RR_SPIRIT_TEMPLE_MQ_EAST_THRONE,
|
||||
RR_SPIRIT_TEMPLE_MQ_EAST_HAND_EXIT,
|
||||
RR_SPIRIT_TEMPLE_MQ_OUTER_EAST_HAND,
|
||||
RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_BIG_WALL,
|
||||
RR_SPIRIT_TEMPLE_MQ_BIG_WALL_LOWER,
|
||||
RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER,
|
||||
RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL,
|
||||
RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_NINE_THRONES_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM,
|
||||
RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE,
|
||||
RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD,
|
||||
RR_SPIRIT_TEMPLE_MQ_PLATFORM,
|
||||
RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD,
|
||||
|
||||
RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY,
|
||||
RR_SPIRIT_TEMPLE_BOSS_ROOM,
|
||||
|
@ -3679,9 +3734,9 @@ typedef enum {
|
|||
RT_SHADOW_MQ_WINDY_WALKWAY,
|
||||
RT_LENS_SPIRIT,
|
||||
RT_SPIRIT_CHILD_CHU,
|
||||
RT_SPIRIT_LOBBY_GS,
|
||||
RT_SPIRIT_WEST_LEDGE,
|
||||
RT_SPIRIT_LOWER_ADULT_SWITCH,
|
||||
RT_SPIRIT_LOBBY_JUMP,
|
||||
RT_SPIRIT_STATUE_JUMP,
|
||||
RT_SPIRIT_PLATFORM_HOOKSHOT,
|
||||
RT_SPIRIT_MAP_CHEST,
|
||||
RT_SPIRIT_SUN_CHEST,
|
||||
|
@ -6531,6 +6586,7 @@ typedef enum {
|
|||
RE_BARI,
|
||||
RE_SHABOM,
|
||||
RE_OCTOROK,
|
||||
RE_WALLTULA,
|
||||
} RandomizerEnemy;
|
||||
|
||||
// RANDOTODO compare child long jumpslash range with adult short
|
||||
|
|
|
@ -380,9 +380,14 @@ void Settings::CreateOptions() {
|
|||
"Allows the following possible without Tunics:\n- Enter Water Temple. The area below the center pillar "
|
||||
"still requires Zora Tunic. Applies to MQ also.\n- Enter Fire Temple. Volvagia still requires Goron "
|
||||
"Tunic. Applies to MQ also, and includes child access to first floor with dungeon shuffle.");
|
||||
OPT_TRICK(RT_RUSTED_SWITCHES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE },
|
||||
"Hammer Rusted Switches Through Walls",
|
||||
"Applies to:\n- Fire Temple Highest Goron Chest.\n- MQ Fire Temple Lizalfos Maze.\n- MQ Spirit Trial.");
|
||||
OPT_TRICK(RT_RUSTED_SWITCHES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "Hammer Through Collision",
|
||||
"Applies to:\n"
|
||||
"- Hitting Fire Temple Highest Goron Chest's Rusted Switch in the SoT Block without Song of Time.\n"
|
||||
"- Hitting MQ Fire Temple Lizalfos Maze's Rusted Switch in the wall.\n"
|
||||
"- Having Adult hammer the rock in the west side crawlspace of MQ Spirit so child can get through "
|
||||
"without bombchus."
|
||||
"- MQ Spirit Trial's Rusted Switch between the thrones without hitting the eye target to drop an Iron "
|
||||
"Knuckle.\n");
|
||||
OPT_TRICK(RT_FLAMING_CHESTS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "Flaming Chests",
|
||||
"The chests encircled in flames in Gerudo Training Ground and in Spirit Temple can be opened by running "
|
||||
"into the flames while Link is invincible after taking damage.");
|
||||
|
@ -1032,18 +1037,18 @@ void Settings::CreateOptions() {
|
|||
"Removes the requirements for the Lens of Truth in Spirit Temple.");
|
||||
OPT_TRICK(RT_SPIRIT_CHILD_CHU, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE },
|
||||
"Spirit Temple Child Side Bridge with Bombchu", "A carefully-timed Bombchu can hit the switch.");
|
||||
OPT_TRICK(RT_SPIRIT_LOBBY_GS, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE },
|
||||
"Spirit Temple Main Room GS with Boomerang",
|
||||
"Standing on the highest part of the arm of the statue, a precise Boomerang throw can kill and obtain "
|
||||
"this Gold Skulltula. You must throw the Boomerang slightly off to the side so that it curves into the "
|
||||
"Skulltula, as aiming directly at it will clank off of the wall in front.");
|
||||
OPT_TRICK(RT_SPIRIT_WEST_LEDGE, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE },
|
||||
"Spirit Temple Statue Room West Ledge Checks with Boomerang",
|
||||
"By carefully walking onto the upper arm of the statue, it's possible to get a good angle on the "
|
||||
"Gold Skulltula (In Vanilla) and the farthest pot (In MQ) to collect the checks with Boomerang. "
|
||||
"The nearest pot in MQ can be reached from the forearm and is always in logic.");
|
||||
OPT_TRICK(RT_SPIRIT_LOWER_ADULT_SWITCH, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED },
|
||||
"Spirit Temple Lower Adult Switch with Bombs",
|
||||
"A bomb can be used to hit the switch on the ceiling, but it must be thrown from a particular distance "
|
||||
"away and with precise timing.");
|
||||
OPT_TRICK(
|
||||
RT_SPIRIT_LOBBY_JUMP, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE },
|
||||
"Spirit Temple Main Room Jump from Hands to Upper Ledges",
|
||||
RT_SPIRIT_STATUE_JUMP, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE },
|
||||
"Spirit Temple Statue Room Jump from Hands to Upper Ledges",
|
||||
"A precise jump to obtain the following as adult without needing one of Hover Boots, or Hookshot (in Vanilla) "
|
||||
"or Song of Time (in MQ): - Spirit Temple Statue Room Northeast Chest - Spirit Temple GS Lobby - Spirit Temple "
|
||||
"MQ Central Chamber Top Left Pot (Left) - Spirit Temple MQ Central Chamber Top Left Pot (Right)");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue