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
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.
* 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 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
* 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
* 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 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,
* 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
* 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 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
* 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.
*/
@ -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.
*/
bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge,
RandomizerRegion otherRegion, ConditionFn otherCondition,
RandomizerRegion thirdRegion, ConditionFn thirdCondition){
bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion,
ConditionFn otherCondition, RandomizerRegion thirdRegion, ConditionFn thirdCondition) {
SpiritLogicData curRegionData = Region::spiritLogicData[region];
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
logic->IsChild = true;
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
// 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->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
//we don't need to check ambiguity here as if this fails, then 1 of the ages has failed
if (anyAge && (ChildCertainAccess || AdultCertainAccess)){
// 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;
@ -331,7 +335,7 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge,
// otherwise, we have to check the current age and...
} else if (areaTable[region].Child() && pastChild) {
//Switch to Child
// Switch to Child
logic->IsChild = true;
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.
// 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 && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess())) {
//Switch to Adult
if (!ChildCertainAccess && result &&
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess())) {
// 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()) ||
result = (curRegionData.adultAccess() &&
(!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) ||
(otherRegion != RR_NONE &&
(Region::spiritLogicData[otherRegion].adultAccess() &&
(Region::spiritLogicData[otherRegion].adultAccess() &&
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) &&
otherCondition())) ||
(thirdRegion != RR_NONE &&
(Region::spiritLogicData[thirdRegion].adultAccess() &&
(Region::spiritLogicData[thirdRegion].adultAccess() &&
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) &&
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
// 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 && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess)){
//Switch to Child
if (!AdultCertainAccess && result &&
(!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess)) {
// 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()));
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

View file

@ -112,21 +112,21 @@ 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
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
// 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,
// The area access condition to reach this region from the boss door,
ConditionFn reverseAccess;
};
@ -250,7 +250,7 @@ class Region {
"Adult Night: " +
std::to_string(adultNight);
}
static std::map<RandomizerRegion, SpiritLogicData> spiritLogicData;
};
@ -260,9 +260,10 @@ 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 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 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 CanPlantBean(const RandomizerRegion region);
bool BothAges(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);
case RE_IRON_KNUCKLE:
case RE_BIG_OCTO:
case RE_WALLTULA: //consistent with RT_SPIRIT_WALL
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);
@ -2362,10 +2362,9 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) {
inLogic[logicVal] = value;
}
bool Logic::IsReverseAccessPossible(){
bool Logic::IsReverseAccessPossible() {
return ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES) &&
(ctx->GetOption(RSK_DECOUPLED_ENTRANCES) ||
ctx->GetOption(RSK_MIX_BOSS_ENTRANCES));
(ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || ctx->GetOption(RSK_MIX_BOSS_ENTRANCES));
}
bool Logic::SpiritBrokenWallToStatue() {
@ -2373,7 +2372,8 @@ bool Logic::SpiritBrokenWallToStatue() {
}
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() {
@ -2381,22 +2381,25 @@ bool Logic::SpiritWestToSkull() {
}
bool Logic::SpiritSunBlockSouthLedge() {
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))))))*/;
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))))))*/
;
}
//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() {
return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_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))/* && str0*/;
return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME)) /* && str0*/;
}
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() {

View file

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