This commit is contained in:
Pepper0ni 2025-04-24 21:50:53 +01:00
commit 9b2d092a5b
4 changed files with 73 additions and 62 deletions

View file

@ -219,7 +219,7 @@ uint8_t SpiritExplosiveLogic() {
* This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on * 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 specific ages
* In order to pass a check, we must either determine that Access is certain, * 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. or that it is always possible to get a check somehow.
* But first I have to talk about parallel universes. * But first I have to talk about parallel universes.
@ -231,15 +231,16 @@ uint8_t SpiritExplosiveLogic() {
* In the third universe, adult enters in reverse, and wastes all the keys so noone can enter through the front * 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 * In the forth, child manages to do the same, and lock people out of the front
* All access from the boss door in shared areas is Certain * All access from the boss door in shared areas is Certain
* While other universes exist, such as both ages entering in reverse or * 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 child using their key, getting stuck, then coming back to do the dungeon as adult, these
are all sub-possibilities of these 4 universes 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, * 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 both universes we must be able to collect the check in both universes
* When an Age can no longer be kept out by conflicting universes, that age is said to have Certain Access to a region * When an Age can no longer be kept out by conflicting universes, 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, * 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 all possible universes can collect the check independently * then a check is only in logic if all possible universes can collect the check independently
@ -247,11 +248,11 @@ uint8_t SpiritExplosiveLogic() {
* We must check for these universes manually as we allow technical access with minimum keys for * We must check for these universes manually as we allow technical access with minimum keys for
* technical reasons as otherwise the logic code will never run * technical reasons as otherwise the logic code will never run
* The first and 3rd column list how many keys are needed for each age to have Certain Access * The first and 3rd column list how many keys are needed for each age to have Certain Access
* the second column is child keys in case there's Child reverse access, due to an edge case in MQ spirit logic * the second column is child keys in case there's Child reverse access, due to an edge case in MQ spirit logic
* where the broken wall room can be reached with 6 keys if you can hit switches and have reverse Child access * where the broken wall room can be reached with 6 keys if you can hit switches and have reverse Child access
* The first condition is the combined conditions needed to move from the 1F child lock to the area being checks * The first condition is the combined conditions needed to move from the 1F child lock to the area being checks
* the second condition is the same for adult 1F lock, and the third is the access from the boss door. * the second condition is the same for adult 1F lock, and the third is the access from the boss door.
*/ */
@ -294,9 +295,8 @@ std::map<RandomizerRegion, SpiritLogicData> Region::spiritLogicData = {
*anyAge is equivalent to a self referencing Here, used for events and any check where that is relevent. *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, bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion,
RandomizerRegion otherRegion, ConditionFn otherCondition, ConditionFn otherCondition, RandomizerRegion thirdRegion, ConditionFn thirdCondition) {
RandomizerRegion thirdRegion, ConditionFn thirdCondition){
SpiritLogicData curRegionData = Region::spiritLogicData[region]; SpiritLogicData curRegionData = Region::spiritLogicData[region];
bool result = false; bool result = false;
@ -308,20 +308,24 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge,
// without opening the Statue room to Broken Wall Room lock first // without opening the Statue room to Broken Wall Room lock first
logic->IsChild = true; logic->IsChild = true;
logic->IsAdult = false; logic->IsAdult = false;
uint8_t childKeys = (logic->ReverseSpiritChild && logic->CanHitSwitch()/* && CanClimbHigh()*/) ? curRegionData.childReverseKeys : curRegionData.childKeys; uint8_t childKeys = (logic->ReverseSpiritChild && logic->CanHitSwitch() /* && CanClimbHigh()*/)
? curRegionData.childReverseKeys
: curRegionData.childKeys;
// If we have enough keys that an age cannot be kept out, we have Certain Access // 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 // 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 ChildCertainAccess =
(logic->ReverseSpiritChild && curRegionData.reverseAccess()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys);
//Switch back to adult to check adult access // Switch back to adult to check adult access
logic->IsChild = false; logic->IsChild = false;
logic->IsAdult = true; logic->IsAdult = true;
bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys); bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess()) ||
logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys);
// If we are AnyAge and have any CeratinAccess, then we can check those ages // If we are AnyAge and have any CeratinAccess, 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 // we don't need to check ambiguity here as if this fails, then 1 of the ages has failed
if (anyAge && (ChildCertainAccess || AdultCertainAccess)){ if (anyAge && (ChildCertainAccess || AdultCertainAccess)) {
// set age access to the Certain Access // set age access to the Certain Access
logic->IsChild = ChildCertainAccess; logic->IsChild = ChildCertainAccess;
logic->IsAdult = AdultCertainAccess; logic->IsAdult = AdultCertainAccess;
@ -331,7 +335,7 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge,
// otherwise, we have to check the current age and... // otherwise, we have to check the current age and...
} else if (areaTable[region].Child() && pastChild) { } else if (areaTable[region].Child() && pastChild) {
//Switch to Child // Switch to Child
logic->IsChild = true; logic->IsChild = true;
logic->IsAdult = false; logic->IsAdult = false;
@ -339,19 +343,21 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge,
// If we have Certain Access, we just run the 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 // 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 // and if needed, in reverse
if (!ChildCertainAccess && result && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess())) { if (!ChildCertainAccess && result &&
//Switch to Adult (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess())) {
// Switch to Adult
logic->IsChild = false; logic->IsChild = false;
logic->IsAdult = true; logic->IsAdult = true;
// If Adult can get there and get the check, we can get the check in logic // 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 // 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()) || result = (curRegionData.adultAccess() &&
(!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) ||
(otherRegion != RR_NONE && (otherRegion != RR_NONE &&
(Region::spiritLogicData[otherRegion].adultAccess() && (Region::spiritLogicData[otherRegion].adultAccess() &&
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) &&
otherCondition())) || otherCondition())) ||
(thirdRegion != RR_NONE && (thirdRegion != RR_NONE &&
(Region::spiritLogicData[thirdRegion].adultAccess() && (Region::spiritLogicData[thirdRegion].adultAccess() &&
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) &&
thirdCondition())); thirdCondition()));
} }
@ -361,22 +367,24 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge,
// Alternatively, if we have entered in reverse and can reach from the face, we have Certain Access // 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 // 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 // and if needed, in reverse
if (!AdultCertainAccess && result && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess)){ if (!AdultCertainAccess && result &&
//Switch to Child (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess)) {
// Switch to Child
logic->IsChild = true; logic->IsChild = true;
logic->IsAdult = false; logic->IsAdult = false;
// If Child can get there and get the check, we can get the check in logic // 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 // 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()) || result = (curRegionData.childAccess() &&
(otherRegion != RR_NONE && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()) && condition()) ||
(Region::spiritLogicData[otherRegion].childAccess() && (otherRegion != RR_NONE &&
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && (Region::spiritLogicData[otherRegion].childAccess() &&
otherCondition())) || (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) &&
(thirdRegion != RR_NONE && otherCondition())) ||
(Region::spiritLogicData[thirdRegion].childAccess() && (thirdRegion != RR_NONE &&
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && (Region::spiritLogicData[thirdRegion].childAccess() &&
thirdCondition())); (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) &&
thirdCondition()));
} }
} }
// set back age variables // set back age variables

View file

@ -112,21 +112,21 @@ enum class EntranceType;
} // namespace Rando } // namespace Rando
struct SpiritLogicData { struct SpiritLogicData {
uint8_t childKeys; //the number of keys that guarantees Child can reach this region 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 // 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 // This changes for MQ broken wall room as the first child lock can only be opened by Child
//guaranteeing access with 6 keys // guaranteeing access with 6 keys
uint8_t childReverseKeys; uint8_t childReverseKeys;
uint8_t adultKeys; //the number of keys that guarantees Adult can reach this region 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, // The area access condition to reach this region as Child, from the first lock,
//including the minimum number of keys for ambiguous access // including the minimum number of keys for ambiguous access
// 1 key is always assumed to be required // 1 key is always assumed to be required
ConditionFn childAccess; ConditionFn childAccess;
//The area access condition to reach this region as Adult, from the first lock // The area access condition to reach this region as Adult, from the first lock
//including the minimum number of keys for ambiguous access // including the minimum number of keys for ambiguous access
//1 key is always assumed to be required on vanilla // 1 key is always assumed to be required on vanilla
ConditionFn adultAccess; ConditionFn adultAccess;
//The area access condition to reach this region from the boss door, // The area access condition to reach this region from the boss door,
ConditionFn reverseAccess; ConditionFn reverseAccess;
}; };
@ -250,7 +250,7 @@ class Region {
"Adult Night: " + "Adult Night: " +
std::to_string(adultNight); std::to_string(adultNight);
} }
static std::map<RandomizerRegion, SpiritLogicData> spiritLogicData; static std::map<RandomizerRegion, SpiritLogicData> spiritLogicData;
}; };
@ -260,9 +260,10 @@ extern std::vector<EventAccess> grottoEvents;
bool Here(const RandomizerRegion region, bool Here(const RandomizerRegion region,
ConditionFn ConditionFn
condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this
bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge = false, bool SpiritShared(
RandomizerRegion otherRegion = RR_NONE, ConditionFn otherCondition = []{return false;}, RandomizerRegion region, ConditionFn condition, bool anyAge = false, RandomizerRegion otherRegion = RR_NONE,
RandomizerRegion thirdRegion = RR_NONE, ConditionFn thirdCondition = []{return false;}); ConditionFn otherCondition = [] { return false; }, RandomizerRegion thirdRegion = RR_NONE,
ConditionFn thirdCondition = [] { return false; });
bool CanPlantBean(const RandomizerRegion region); bool CanPlantBean(const RandomizerRegion region);
bool BothAges(const RandomizerRegion region); bool BothAges(const RandomizerRegion region);
bool ChildCanAccess(const RandomizerRegion region); bool ChildCanAccess(const RandomizerRegion region);

View file

@ -864,7 +864,7 @@ bool Logic::CanPassEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
return CanUse(RG_HOOKSHOT) || CanUse(RG_SUNS_SONG); return CanUse(RG_HOOKSHOT) || CanUse(RG_SUNS_SONG);
case RE_IRON_KNUCKLE: case RE_IRON_KNUCKLE:
case RE_BIG_OCTO: case RE_BIG_OCTO:
case RE_WALLTULA: //consistent with RT_SPIRIT_WALL case RE_WALLTULA: // consistent with RT_SPIRIT_WALL
return false; return false;
case RE_GREEN_BUBBLE: case RE_GREEN_BUBBLE:
return TakeDamage() || CanUse(RG_NUTS) || CanUse(RG_BOOMERANG) || CanUse(RG_HOOKSHOT); return TakeDamage() || CanUse(RG_NUTS) || CanUse(RG_BOOMERANG) || CanUse(RG_HOOKSHOT);
@ -2362,10 +2362,9 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) {
inLogic[logicVal] = value; inLogic[logicVal] = value;
} }
bool Logic::IsReverseAccessPossible(){ bool Logic::IsReverseAccessPossible() {
return ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES) && return ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES) &&
(ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || ctx->GetOption(RSK_MIX_BOSS_ENTRANCES));
ctx->GetOption(RSK_MIX_BOSS_ENTRANCES));
} }
bool Logic::SpiritBrokenWallToStatue() { bool Logic::SpiritBrokenWallToStatue() {
@ -2373,7 +2372,8 @@ bool Logic::SpiritBrokenWallToStatue() {
} }
bool Logic::SpiritEastToSwitch() { bool Logic::SpiritEastToSwitch() {
return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) ||
(CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT));
} }
bool Logic::SpiritWestToSkull() { bool Logic::SpiritWestToSkull() {
@ -2381,22 +2381,25 @@ bool Logic::SpiritWestToSkull() {
} }
bool Logic::SpiritSunBlockSouthLedge() { bool Logic::SpiritSunBlockSouthLedge() {
return true/*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() || return true /*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() ||
(CanUse(RG_HOOKSHOT) && (HasFireSource() || (CanUse(RG_HOOKSHOT) && (HasFireSource() ||
(SpiritSunBlockTorch && (logic->CanUse(STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))))*/; (SpiritSunBlockTorch && (logic->CanUse(STICKS) ||
(ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))))*/
;
} }
//Combines crossing the ledge directly and the jump from the hand // Combines crossing the ledge directly and the jump from the hand
bool Logic::MQSpiritWestToPots() { bool Logic::MQSpiritWestToPots() {
return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME); return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME);
} }
bool Logic::MQSpiritStatueToSunBlock() { bool Logic::MQSpiritStatueToSunBlock() {
return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME))/* && str0*/; return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME)) /* && str0*/;
} }
bool Logic::MQSpiritStatueSouthDoor() { bool Logic::MQSpiritStatueSouthDoor() {
return HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && CanUse(RG_FAIRY_BOW) && CanUse(RG_SONG_OF_TIME)/* && CanClimb()*/); return HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && CanUse(RG_FAIRY_BOW) &&
CanUse(RG_SONG_OF_TIME) /* && CanClimb()*/);
} }
void Logic::Reset() { void Logic::Reset() {

View file

@ -26,7 +26,6 @@ enum class GlitchDifficulty {
HERO, HERO,
}; };
class Logic { class Logic {
public: public:
bool noVariable = false; bool noVariable = false;