[Rando] Add Mix and Decouple support to Boss Entrances (#3261)

* initial support for mixing boss entrances in the mixed pool

* support decouple for boss entrances

* missed decouple change

* add blue warp exits to boss rooms in location access

* add entrance get helper methods

* fix assumed targets not using root in entrance name

* add dedicated blue warp entrances and handle blue warp changes during generation

* change bluewarp handling in game to use unique blue warp entrances

* handle blue warps in entrance tracker

* fix overriding all jabu and water temple rooms

* fix grotto returns when exiting boss rooms/dungeons

* fix blue warp logic by tracking original connected region key

* use entrance enums

* remove unneeded entrance values

* fix decouple generation crash

* fix jabu mq backwards logic
This commit is contained in:
Archez 2024-02-15 20:14:24 -05:00 committed by GitHub
parent 3187564f5b
commit 13fce8258d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 349 additions and 195 deletions

View File

@ -26,7 +26,6 @@ typedef struct {
AreaKey parentRegion;
AreaKey connectedRegion;
int16_t index;
int16_t blueWarp;
} EntranceLinkInfo;
EntranceLinkInfo NO_RETURN_ENTRANCE = {EntranceType::None, NONE, NONE, -1};
@ -40,6 +39,11 @@ using EntranceInfoPair = std::pair<EntranceLinkInfo, EntranceLinkInfo>;
using EntrancePair = std::pair<Entrance*, Entrance*>;
using EntrancePools = std::map<EntranceType, std::vector<Entrance*>>;
// Construct entrance name from parent and connected region keys
std::string EntranceNameByRegions(uint32_t parentRegion, uint32_t connectedRegion) {
return AreaTable(parentRegion)->regionName + " -> " + AreaTable(connectedRegion)->regionName;
}
//The entrance randomization algorithm used here is a direct copy of
//the algorithm used in the original N64 randomizer (except now in C++ instead
//of python). It may be easier to understand the algorithm by looking at the
@ -77,26 +81,22 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
//set data
Entrance* forwardEntrance = AreaTable(forwardEntry.parentRegion)->GetExit(forwardEntry.connectedRegion);
forwardEntrance->SetIndex(forwardEntry.index);
forwardEntrance->SetBlueWarp(forwardEntry.blueWarp);
forwardEntrance->SetType(forwardEntry.type);
forwardEntrance->SetAsPrimary();
// When decouple entrances is on, mark it for entrances except boss rooms
if (Settings::DecoupleEntrances && forwardEntry.type != EntranceType::ChildBoss &&
forwardEntry.type != EntranceType::AdultBoss) {
// When decouple entrances is on, mark the forward entrance
if (Settings::DecoupleEntrances) {
forwardEntrance->SetDecoupled();
}
if (returnEntry.parentRegion != NONE) {
Entrance* returnEntrance = AreaTable(returnEntry.parentRegion)->GetExit(returnEntry.connectedRegion);
returnEntrance->SetIndex(returnEntry.index);
returnEntrance->SetBlueWarp(returnEntry.blueWarp);
returnEntrance->SetType(returnEntry.type);
forwardEntrance->BindTwoWay(returnEntrance);
// Mark reverse entrance as decoupled
if (Settings::DecoupleEntrances && returnEntry.type != EntranceType::ChildBoss &&
returnEntry.type != EntranceType::AdultBoss) {
if (Settings::DecoupleEntrances) {
returnEntrance->SetDecoupled();
}
}
@ -687,31 +687,31 @@ int ShuffleAllEntrances() {
curNumRandomizedEntrances = 0;
std::vector<EntranceInfoPair> entranceShuffleTable = {
//Parent Region Connected Region index blue warp
//Parent Region Connected Region index
{{EntranceType::Dungeon, KF_OUTSIDE_DEKU_TREE, DEKU_TREE_ENTRYWAY, 0x0000},
{EntranceType::Dungeon, DEKU_TREE_ENTRYWAY, KF_OUTSIDE_DEKU_TREE, 0x0209, 0x0457}},
{EntranceType::Dungeon, DEKU_TREE_ENTRYWAY, KF_OUTSIDE_DEKU_TREE, 0x0209}},
{{EntranceType::Dungeon, DEATH_MOUNTAIN_TRAIL, DODONGOS_CAVERN_ENTRYWAY, 0x0004},
{EntranceType::Dungeon, DODONGOS_CAVERN_ENTRYWAY, DEATH_MOUNTAIN_TRAIL, 0x0242, 0x047A}},
{EntranceType::Dungeon, DODONGOS_CAVERN_ENTRYWAY, DEATH_MOUNTAIN_TRAIL, 0x0242}},
{{EntranceType::Dungeon, ZORAS_FOUNTAIN, JABU_JABUS_BELLY_ENTRYWAY, 0x0028},
{EntranceType::Dungeon, JABU_JABUS_BELLY_ENTRYWAY, ZORAS_FOUNTAIN, 0x0221, 0x010E}},
{EntranceType::Dungeon, JABU_JABUS_BELLY_ENTRYWAY, ZORAS_FOUNTAIN, 0x0221}},
{{EntranceType::Dungeon, SACRED_FOREST_MEADOW, FOREST_TEMPLE_ENTRYWAY, 0x0169},
{EntranceType::Dungeon, FOREST_TEMPLE_ENTRYWAY, SACRED_FOREST_MEADOW, 0x0215, 0x0608}},
{EntranceType::Dungeon, FOREST_TEMPLE_ENTRYWAY, SACRED_FOREST_MEADOW, 0x0215}},
{{EntranceType::Dungeon, DMC_CENTRAL_LOCAL, FIRE_TEMPLE_ENTRYWAY, 0x0165},
{EntranceType::Dungeon, FIRE_TEMPLE_ENTRYWAY, DMC_CENTRAL_LOCAL, 0x024A, 0x0564}},
{EntranceType::Dungeon, FIRE_TEMPLE_ENTRYWAY, DMC_CENTRAL_LOCAL, 0x024A}},
{{EntranceType::Dungeon, LAKE_HYLIA, WATER_TEMPLE_ENTRYWAY, 0x0010},
{EntranceType::Dungeon, WATER_TEMPLE_ENTRYWAY, LAKE_HYLIA, 0x021D, 0x060C}},
{EntranceType::Dungeon, WATER_TEMPLE_ENTRYWAY, LAKE_HYLIA, 0x021D}},
{{EntranceType::Dungeon, DESERT_COLOSSUS, SPIRIT_TEMPLE_ENTRYWAY, 0x0082},
{EntranceType::Dungeon, SPIRIT_TEMPLE_ENTRYWAY, DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY, 0x01E1, 0x0610}},
{EntranceType::Dungeon, SPIRIT_TEMPLE_ENTRYWAY, DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY, 0x01E1}},
{{EntranceType::Dungeon, GRAVEYARD_WARP_PAD_REGION, SHADOW_TEMPLE_ENTRYWAY, 0x0037},
{EntranceType::Dungeon, SHADOW_TEMPLE_ENTRYWAY, GRAVEYARD_WARP_PAD_REGION, 0x0205, 0x0580}},
{EntranceType::Dungeon, SHADOW_TEMPLE_ENTRYWAY, GRAVEYARD_WARP_PAD_REGION, 0x0205}},
{{EntranceType::Dungeon, KAKARIKO_VILLAGE, BOTTOM_OF_THE_WELL_ENTRYWAY, 0x0098},
{EntranceType::Dungeon, BOTTOM_OF_THE_WELL_ENTRYWAY, KAKARIKO_VILLAGE, 0x02A6}},
{{EntranceType::Dungeon, ZORAS_FOUNTAIN, ICE_CAVERN_ENTRYWAY, 0x0088},
{EntranceType::Dungeon, ICE_CAVERN_ENTRYWAY, ZORAS_FOUNTAIN, 0x03D4}},
{{EntranceType::Dungeon, GERUDO_FORTRESS, GERUDO_TRAINING_GROUNDS_ENTRYWAY, 0x0008},
{EntranceType::Dungeon, GERUDO_TRAINING_GROUNDS_ENTRYWAY, GERUDO_FORTRESS, 0x03A8}},
{{EntranceType::GanonDungeon, GANONS_CASTLE_LEDGE, GANONS_CASTLE_ENTRYWAY, 0x0467},
{EntranceType::GanonDungeon, GANONS_CASTLE_ENTRYWAY, CASTLE_GROUNDS_FROM_GANONS_CASTLE, 0x023D}},
{{EntranceType::GanonDungeon, GANONS_CASTLE_LEDGE, GANONS_CASTLE_ENTRYWAY, 0x0467},
{EntranceType::GanonDungeon, GANONS_CASTLE_ENTRYWAY, CASTLE_GROUNDS_FROM_GANONS_CASTLE, 0x023D}},
{{EntranceType::Interior, KOKIRI_FOREST, KF_MIDOS_HOUSE, 0x0433},
{EntranceType::Interior, KF_MIDOS_HOUSE, KOKIRI_FOREST, 0x0443}},
@ -947,21 +947,30 @@ int ShuffleAllEntrances() {
{{EntranceType::WarpSong, PRELUDE_OF_LIGHT_WARP, TEMPLE_OF_TIME, 0x05F4}, NO_RETURN_ENTRANCE},
{{EntranceType::ChildBoss, DEKU_TREE_BOSS_ENTRYWAY, DEKU_TREE_BOSS_ROOM, 0x040F},
{EntranceType::ChildBoss, DEKU_TREE_BOSS_ROOM, DEKU_TREE_BOSS_ENTRYWAY, 0x0252, 0x0457}},
{EntranceType::ChildBoss, DEKU_TREE_BOSS_ROOM, DEKU_TREE_BOSS_ENTRYWAY, 0x0252}},
{{EntranceType::ChildBoss, DODONGOS_CAVERN_BOSS_ENTRYWAY, DODONGOS_CAVERN_BOSS_ROOM, 0x040B},
{EntranceType::ChildBoss, DODONGOS_CAVERN_BOSS_ROOM, DODONGOS_CAVERN_BOSS_ENTRYWAY, 0x00C5, 0x047A}},
{EntranceType::ChildBoss, DODONGOS_CAVERN_BOSS_ROOM, DODONGOS_CAVERN_BOSS_ENTRYWAY, 0x00C5}},
{{EntranceType::ChildBoss, JABU_JABUS_BELLY_BOSS_ENTRYWAY, JABU_JABUS_BELLY_BOSS_ROOM, 0x0301},
{EntranceType::ChildBoss, JABU_JABUS_BELLY_BOSS_ROOM, JABU_JABUS_BELLY_BOSS_ENTRYWAY, 0x0407, 0x010E}},
{EntranceType::ChildBoss, JABU_JABUS_BELLY_BOSS_ROOM, JABU_JABUS_BELLY_BOSS_ENTRYWAY, 0x0407}},
{{EntranceType::AdultBoss, FOREST_TEMPLE_BOSS_ENTRYWAY, FOREST_TEMPLE_BOSS_ROOM, 0x000C},
{EntranceType::AdultBoss, FOREST_TEMPLE_BOSS_ROOM, FOREST_TEMPLE_BOSS_ENTRYWAY, 0x024E, 0x0608}},
{EntranceType::AdultBoss, FOREST_TEMPLE_BOSS_ROOM, FOREST_TEMPLE_BOSS_ENTRYWAY, 0x024E}},
{{EntranceType::AdultBoss, FIRE_TEMPLE_BOSS_ENTRYWAY, FIRE_TEMPLE_BOSS_ROOM, 0x0305},
{EntranceType::AdultBoss, FIRE_TEMPLE_BOSS_ROOM, FIRE_TEMPLE_BOSS_ENTRYWAY, 0x0175, 0x0564}},
{EntranceType::AdultBoss, FIRE_TEMPLE_BOSS_ROOM, FIRE_TEMPLE_BOSS_ENTRYWAY, 0x0175}},
{{EntranceType::AdultBoss, WATER_TEMPLE_BOSS_ENTRYWAY, WATER_TEMPLE_BOSS_ROOM, 0x0417},
{EntranceType::AdultBoss, WATER_TEMPLE_BOSS_ROOM, WATER_TEMPLE_BOSS_ENTRYWAY, 0x0423, 0x060C}},
{EntranceType::AdultBoss, WATER_TEMPLE_BOSS_ROOM, WATER_TEMPLE_BOSS_ENTRYWAY, 0x0423}},
{{EntranceType::AdultBoss, SPIRIT_TEMPLE_BOSS_ENTRYWAY, SPIRIT_TEMPLE_BOSS_ROOM, 0x008D},
{EntranceType::AdultBoss, SPIRIT_TEMPLE_BOSS_ROOM, SPIRIT_TEMPLE_BOSS_ENTRYWAY, 0x02F5, 0x0610}},
{EntranceType::AdultBoss, SPIRIT_TEMPLE_BOSS_ROOM, SPIRIT_TEMPLE_BOSS_ENTRYWAY, 0x02F5}},
{{EntranceType::AdultBoss, SHADOW_TEMPLE_BOSS_ENTRYWAY, SHADOW_TEMPLE_BOSS_ROOM, 0x0413},
{EntranceType::AdultBoss, SHADOW_TEMPLE_BOSS_ROOM, SHADOW_TEMPLE_BOSS_ENTRYWAY, 0x02B2, 0x0580}},
{EntranceType::AdultBoss, SHADOW_TEMPLE_BOSS_ROOM, SHADOW_TEMPLE_BOSS_ENTRYWAY, 0x02B2}},
{{EntranceType::BlueWarp, DEKU_TREE_BOSS_ROOM, KF_OUTSIDE_DEKU_TREE, 0x0457}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, DODONGOS_CAVERN_BOSS_ROOM, DEATH_MOUNTAIN_TRAIL, 0x047A}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, JABU_JABUS_BELLY_BOSS_ROOM, ZORAS_FOUNTAIN, 0x010E}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, FOREST_TEMPLE_BOSS_ROOM, SACRED_FOREST_MEADOW, 0x0608}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, FIRE_TEMPLE_BOSS_ROOM, DMC_CENTRAL_LOCAL, 0x0564}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, WATER_TEMPLE_BOSS_ROOM, LAKE_HYLIA, 0x060C}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, SPIRIT_TEMPLE_BOSS_ROOM, DESERT_COLOSSUS, 0x0610}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, SHADOW_TEMPLE_BOSS_ROOM, GRAVEYARD_WARP_PAD_REGION, 0x0580}, NO_RETURN_ENTRANCE},
};
std::map<std::string, PriorityEntrance> priorityEntranceTable = {
@ -1011,6 +1020,11 @@ int ShuffleAllEntrances() {
FilterAndEraseFromPool(entrancePools[EntranceType::Boss], [](const Entrance* entrance){return entrance->GetParentRegionKey() == DEKU_TREE_BOSS_ENTRYWAY &&
entrance->GetConnectedRegionKey() == DEKU_TREE_BOSS_ROOM;});
}
if (Settings::DecoupleEntrances) {
for (Entrance* entrance : entrancePools[EntranceType::Boss]) {
entrancePools[EntranceType::BossReverse].push_back(entrance->GetReverse());
}
}
} else {
entrancePools[EntranceType::ChildBoss] = GetShuffleableEntrances(EntranceType::ChildBoss);
entrancePools[EntranceType::AdultBoss] = GetShuffleableEntrances(EntranceType::AdultBoss);
@ -1019,6 +1033,14 @@ int ShuffleAllEntrances() {
FilterAndEraseFromPool(entrancePools[EntranceType::ChildBoss], [](const Entrance* entrance){return entrance->GetParentRegionKey() == DEKU_TREE_BOSS_ENTRYWAY &&
entrance->GetConnectedRegionKey() == DEKU_TREE_BOSS_ROOM;});
}
if (Settings::DecoupleEntrances) {
for (Entrance* entrance : entrancePools[EntranceType::ChildBoss]) {
entrancePools[EntranceType::ChildBossReverse].push_back(entrance->GetReverse());
}
for (Entrance* entrance : entrancePools[EntranceType::AdultBoss]) {
entrancePools[EntranceType::AdultBossReverse].push_back(entrance->GetReverse());
}
}
}
}
@ -1082,10 +1104,13 @@ int ShuffleAllEntrances() {
SetShuffledEntrances(oneWayEntrancePools);
//combine entrance pools if mixing pools. Only continue if more than one pool is selected.
int totalMixedPools = (Settings::MixDungeons ? 1 : 0) + (Settings::MixOverworld ? 1 : 0) + (Settings::MixInteriors ? 1 : 0) + (Settings::MixGrottos ? 1 : 0);
int totalMixedPools = (Settings::MixDungeons ? 1 : 0) + (Settings::MixBosses ? 1 : 0) +
(Settings::MixOverworld ? 1 : 0) + (Settings::MixInteriors ? 1 : 0) +
(Settings::MixGrottos ? 1 : 0);
if (totalMixedPools < 2) {
Settings::MixedEntrancePools.SetSelectedIndex(OFF);
Settings::MixDungeons.SetSelectedIndex(OFF);
Settings::MixBosses.SetSelectedIndex(OFF);
Settings::MixOverworld.SetSelectedIndex(OFF);
Settings::MixInteriors.SetSelectedIndex(OFF);
Settings::MixGrottos.SetSelectedIndex(OFF);
@ -1099,6 +1124,12 @@ int ShuffleAllEntrances() {
poolsToMix.insert(EntranceType::DungeonReverse);
}
}
if (Settings::MixBosses) {
poolsToMix.insert(EntranceType::Boss);
if (Settings::DecoupleEntrances) {
poolsToMix.insert(EntranceType::BossReverse);
}
}
if (Settings::MixOverworld) {
poolsToMix.insert(EntranceType::Overworld);
}
@ -1232,6 +1263,87 @@ int ShuffleAllEntrances() {
}
}
// Determine blue warp targets
if (true /* Settings.BlueWarps.Is(BLUEWARPS_DUNGEON) */) { // RANDOTODO: add bluewarp shuffle
// If a boss room is inside a boss door, make the blue warp go outside the dungeon's entrance
std::map<std::string, Entrance*> bossExits = {
{ EntranceNameByRegions(DEKU_TREE_BOSS_ROOM, DEKU_TREE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(DEKU_TREE_ENTRYWAY, KF_OUTSIDE_DEKU_TREE)) },
{ EntranceNameByRegions(DODONGOS_CAVERN_BOSS_ROOM, DODONGOS_CAVERN_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(DODONGOS_CAVERN_ENTRYWAY, DEATH_MOUNTAIN_TRAIL)) },
{ EntranceNameByRegions(JABU_JABUS_BELLY_BOSS_ROOM, JABU_JABUS_BELLY_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(JABU_JABUS_BELLY_ENTRYWAY, ZORAS_FOUNTAIN)) },
{ EntranceNameByRegions(FOREST_TEMPLE_BOSS_ROOM, FOREST_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(FOREST_TEMPLE_ENTRYWAY, SACRED_FOREST_MEADOW)) },
{ EntranceNameByRegions(FIRE_TEMPLE_BOSS_ROOM, FIRE_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(FIRE_TEMPLE_ENTRYWAY, DMC_CENTRAL_LOCAL)) },
{ EntranceNameByRegions(WATER_TEMPLE_BOSS_ROOM, WATER_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(WATER_TEMPLE_ENTRYWAY, LAKE_HYLIA)) },
{ EntranceNameByRegions(SPIRIT_TEMPLE_BOSS_ROOM, SPIRIT_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(SPIRIT_TEMPLE_ENTRYWAY, DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY)) },
{ EntranceNameByRegions(SHADOW_TEMPLE_BOSS_ROOM, SHADOW_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(SHADOW_TEMPLE_ENTRYWAY, GRAVEYARD_WARP_PAD_REGION)) },
};
// If a boss room is inside a dungeon entrance (or inside a dungeon which is inside a dungeon entrance), make the blue warp go to that dungeon's blue warp target
std::map<std::string, Entrance*> dungeonExits = {
{ EntranceNameByRegions(DEKU_TREE_ENTRYWAY, KF_OUTSIDE_DEKU_TREE),
GetEntrance(EntranceNameByRegions(DEKU_TREE_BOSS_ROOM, KF_OUTSIDE_DEKU_TREE)) },
{ EntranceNameByRegions(DODONGOS_CAVERN_ENTRYWAY, DEATH_MOUNTAIN_TRAIL),
GetEntrance(EntranceNameByRegions(DODONGOS_CAVERN_BOSS_ROOM, DEATH_MOUNTAIN_TRAIL)) },
{ EntranceNameByRegions(JABU_JABUS_BELLY_ENTRYWAY, ZORAS_FOUNTAIN),
GetEntrance(EntranceNameByRegions(JABU_JABUS_BELLY_BOSS_ROOM, ZORAS_FOUNTAIN)) },
{ EntranceNameByRegions(FOREST_TEMPLE_ENTRYWAY, SACRED_FOREST_MEADOW),
GetEntrance(EntranceNameByRegions(FOREST_TEMPLE_BOSS_ROOM, SACRED_FOREST_MEADOW)) },
{ EntranceNameByRegions(FIRE_TEMPLE_ENTRYWAY, DMC_CENTRAL_LOCAL),
GetEntrance(EntranceNameByRegions(FIRE_TEMPLE_BOSS_ROOM, DMC_CENTRAL_LOCAL)) },
{ EntranceNameByRegions(WATER_TEMPLE_ENTRYWAY, LAKE_HYLIA),
GetEntrance(EntranceNameByRegions(WATER_TEMPLE_BOSS_ROOM, LAKE_HYLIA)) },
{ EntranceNameByRegions(SPIRIT_TEMPLE_ENTRYWAY, DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY),
GetEntrance(EntranceNameByRegions(SPIRIT_TEMPLE_BOSS_ROOM, DESERT_COLOSSUS)) },
{ EntranceNameByRegions(SHADOW_TEMPLE_ENTRYWAY, GRAVEYARD_WARP_PAD_REGION),
GetEntrance(EntranceNameByRegions(SHADOW_TEMPLE_BOSS_ROOM, GRAVEYARD_WARP_PAD_REGION)) },
};
// Pair <BlueWarp exit, BossRoom reverse exit>
std::vector<EntrancePair> bossRoomExitPairs = {
{ GetEntrance(EntranceNameByRegions(DEKU_TREE_BOSS_ROOM, KF_OUTSIDE_DEKU_TREE)),
GetEntrance(EntranceNameByRegions(DEKU_TREE_BOSS_ROOM, DEKU_TREE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(DODONGOS_CAVERN_BOSS_ROOM, DEATH_MOUNTAIN_TRAIL)),
GetEntrance(EntranceNameByRegions(DODONGOS_CAVERN_BOSS_ROOM, DODONGOS_CAVERN_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(JABU_JABUS_BELLY_BOSS_ROOM, ZORAS_FOUNTAIN)),
GetEntrance(EntranceNameByRegions(JABU_JABUS_BELLY_BOSS_ROOM, JABU_JABUS_BELLY_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(FOREST_TEMPLE_BOSS_ROOM, SACRED_FOREST_MEADOW)),
GetEntrance(EntranceNameByRegions(FOREST_TEMPLE_BOSS_ROOM, FOREST_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(FIRE_TEMPLE_BOSS_ROOM, DMC_CENTRAL_LOCAL)),
GetEntrance(EntranceNameByRegions(FIRE_TEMPLE_BOSS_ROOM, FIRE_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(WATER_TEMPLE_BOSS_ROOM, LAKE_HYLIA)),
GetEntrance(EntranceNameByRegions(WATER_TEMPLE_BOSS_ROOM, WATER_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(SPIRIT_TEMPLE_BOSS_ROOM, DESERT_COLOSSUS)),
GetEntrance(EntranceNameByRegions(SPIRIT_TEMPLE_BOSS_ROOM, SPIRIT_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(SHADOW_TEMPLE_BOSS_ROOM, GRAVEYARD_WARP_PAD_REGION)),
GetEntrance(EntranceNameByRegions(SHADOW_TEMPLE_BOSS_ROOM, SHADOW_TEMPLE_BOSS_ENTRYWAY)) },
};
for (EntrancePair pair : bossRoomExitPairs) {
Entrance* target = pair.second->GetReplacement() != nullptr ? pair.second->GetReplacement() : pair.second;
if (!Settings::DecoupleEntrances) {
while (bossExits.find(target->GetName()) != bossExits.end()) {
Entrance* next = bossExits.at(target->GetName());
target = next->GetReplacement() != nullptr ? next->GetReplacement() : next;
}
if (dungeonExits.find(target->GetName()) != dungeonExits.end()) {
target = dungeonExits.at(target->GetName());
}
}
pair.first->Connect(target->GetOriginalConnectedRegionKey());
pair.first->SetReplacement(target);
}
}
// Validate the world one last time to ensure all special conditions are still valid
if (!ValidateWorld(nullptr)) {
return ENTRANCE_SHUFFLE_FAILURE;
@ -1259,8 +1371,8 @@ void CreateEntranceOverrides() {
auto message = "Setting " + entrance->to_string() + "\n";
SPDLOG_DEBUG(message);
uint8_t type = (uint8_t)entrance->GetType();
int16_t originalIndex = entrance->GetIndex();
int16_t originalBlueWarp = entrance->GetBlueWarp();
int16_t replacementIndex = entrance->GetReplacement()->GetIndex();
int16_t destinationIndex = -1;
@ -1274,9 +1386,9 @@ void CreateEntranceOverrides() {
}
entranceOverrides.push_back({
.type = type,
.index = originalIndex,
.destination = destinationIndex,
.blueWarp = originalBlueWarp,
.override = replacementIndex,
.overrideDestination = replacementDestinationIndex,
});

View File

@ -18,12 +18,16 @@ enum class EntranceType {
OwlDrop,
Spawn,
WarpSong,
BlueWarp,
Dungeon,
GanonDungeon,
DungeonReverse,
Boss,
BossReverse,
ChildBoss,
ChildBossReverse,
AdultBoss,
AdultBossReverse,
Interior,
InteriorReverse,
SpecialInterior,
@ -40,6 +44,7 @@ public:
Entrance(uint32_t connectedRegion_, std::vector<ConditionFn> conditions_met_)
: connectedRegion(connectedRegion_) {
originalConnectedRegion = connectedRegion_;
conditions_met.resize(2);
for (size_t i = 0; i < conditions_met_.size(); i++) {
conditions_met[i] = conditions_met_[i];
@ -134,6 +139,10 @@ public:
return connectedRegion;
}
uint32_t GetOriginalConnectedRegionKey() const {
return originalConnectedRegion;
}
Area* GetConnectedRegion() const {
return AreaTable(connectedRegion);
}
@ -198,14 +207,6 @@ public:
index = newIndex;
}
int16_t GetBlueWarp() const {
return blueWarp;
}
void SetBlueWarp(int16_t newBlueWarp) {
blueWarp = newBlueWarp;
}
Entrance* GetAssumed() const {
return assumed;
}
@ -251,7 +252,7 @@ public:
AreaTable(ROOT)->AddExit(ROOT, connectedRegion, []{return true;});
Entrance* targetEntrance = AreaTable(ROOT)->GetExit(connectedRegion);
targetEntrance->SetReplacement(this);
targetEntrance->SetName(GetParentRegion()->regionName + " -> " + GetConnectedRegion()->regionName);
targetEntrance->SetName(AreaTable(ROOT)->regionName + " -> " + GetConnectedRegion()->regionName);
return targetEntrance;
}
@ -266,6 +267,7 @@ public:
private:
uint32_t parentRegion;
uint32_t connectedRegion;
uint32_t originalConnectedRegion;
std::vector<ConditionFn> conditions_met;
//Entrance Randomizer stuff
@ -275,7 +277,6 @@ private:
Entrance* assumed = nullptr;
Entrance* replacement = nullptr;
int16_t index = 0xFFFF;
int16_t blueWarp = 0;
bool shuffled = false;
bool primary = false;
bool addedToPool = false;
@ -285,6 +286,7 @@ private:
int ShuffleAllEntrances();
void CreateEntranceOverrides();
std::string EntranceNameByRegions(uint32_t parentRegion, uint32_t connectedRegion);
extern std::vector<std::list<Entrance*>> playthroughEntrances;
extern bool noRandomEntrances;

View File

@ -294,7 +294,12 @@ std::vector<uint32_t> GetAccessibleLocations(const std::vector<uint32_t>& allowe
}
// Add shuffled entrances to the entrance playthrough
if (mode == SearchMode::GeneratePlaythrough && exit.IsShuffled() && !exit.IsAddedToPool() && !noRandomEntrances) {
// Include bluewarps when unshuffled but dungeon or boss shuffle is on
if (mode == SearchMode::GeneratePlaythrough &&
(exit.IsShuffled() || (exit.GetType() == EntranceType::BlueWarp &&
(Settings::ShuffleDungeonEntrances.IsNot(SHUFFLEDUNGEONS_OFF) ||
Settings::ShuffleBossEntrances.IsNot(SHUFFLEBOSSES_OFF)))) &&
!exit.IsAddedToPool() && !noRandomEntrances) {
entranceSphere.push_back(&exit);
exit.AddToPool();
// Don't list a two-way coupled entrance from both directions

View File

@ -503,3 +503,16 @@ std::vector<Entrance*> GetShuffleableEntrances(EntranceType type, bool onlyPrima
}
return entrancesToShuffle;
}
// Get the specific entrance by name
Entrance* GetEntrance(const std::string name) {
for (uint32_t area : Areas::GetAllAreas()) {
for (auto& exit : AreaTable(area)->exits) {
if (exit.GetName() == name) {
return &exit;
}
}
}
return nullptr;
}

View File

@ -242,6 +242,7 @@ namespace Areas {
void AreaTable_Init();
Area* AreaTable(const uint32_t areaKey);
std::vector<Entrance*> GetShuffleableEntrances(EntranceType type, bool onlyPrimary = true);
Entrance* GetEntrance(const std::string name);
// Overworld
void AreaTable_Init_LostWoods();

View File

@ -267,5 +267,6 @@ void AreaTable_Init_DekuTree() {
{
// Exits
Entrance(DEKU_TREE_BOSS_ENTRYWAY, { [] { return true; } }),
Entrance(KF_OUTSIDE_DEKU_TREE, { [] { return DekuTreeClear; } }),
});
}

View File

@ -309,5 +309,6 @@ void AreaTable_Init_DodongosCavern() {
{
// Exits
Entrance(DODONGOS_CAVERN_BOSS_ENTRYWAY, { [] { return true; } }),
Entrance(DEATH_MOUNTAIN_TRAIL, { [] { return DodongosCavernClear; } }),
});
}

View File

@ -420,5 +420,6 @@ void AreaTable_Init_FireTemple() {
{
// Exits
Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(DMC_CENTRAL_LOCAL, { [] { return FireTempleClear; } }),
});
}

View File

@ -433,5 +433,6 @@ void AreaTable_Init_ForestTemple() {
{
// Exits
Entrance(FOREST_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(SACRED_FOREST_MEADOW, { [] { return ForestTempleClear; } }),
});
}

View File

@ -179,17 +179,17 @@ void AreaTable_Init_JabuJabusBelly() {
//Locations
LocationAccess(JABU_JABUS_BELLY_MQ_SECOND_ROOM_LOWER_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_SECOND_ROOM_UPPER_CHEST, {[]{return (IsAdult && (CanUse(HOVER_BOOTS) || CanUse(HOOKSHOT))) || ChildCanAccess(JABU_JABUS_BELLY_MQ_BOSS_AREA);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_COMPASS_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_VINES_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_SWITCHES_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_COMPASS_CHEST, {[]{return (IsChild || CanDive || CanUse(IRON_BOOTS) || LogicJabuAlcoveJumpDive) && (CanUse(SLINGSHOT) || CanUse(BOW) || CanUse(HOOKSHOT) || HasBombchus || (LogicJabuMQRangJump && CanUse(BOOMERANG)));}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_VINES_CHEST, {[]{return CanUse(SLINGSHOT);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_SWITCHES_CHEST, {[]{return CanUse(SLINGSHOT);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BOOMERANG_ROOM_SMALL_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BOOMERANG_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BOOMERANG_CHEST, {[]{return CanUse(KOKIRI_SWORD) || CanUse(MASTER_SWORD) || CanUse(BIGGORON_SWORD) || CanUse(MEGATON_HAMMER) || CanUse(SLINGSHOT) || CanUse(BOW) || CanUse(STICKS) || Bombs;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_GS_BOOMERANG_CHEST_ROOM, {[]{return CanPlay(SongOfTime) || (LogicJabuMQSoTGS && IsChild && CanUse(BOOMERANG));}}),
//Trick: CanPlay(SongOfTime) || (LogicJabuMQSoTGS && IsChild && CanUse(BOOMERANG))
}, {
//Exits
Entrance(JABU_JABUS_BELLY_MQ_BEGINNING, {[]{return true;}}),
Entrance(JABU_JABUS_BELLY_MQ_DEPTHS, {[]{return HasExplosives && IsChild && CanUse(BOOMERANG);}}),
Entrance(JABU_JABUS_BELLY_MQ_DEPTHS, {[]{return HasExplosives && CanUse(SLINGSHOT) && CanUse(BOOMERANG);}}),
});
areaTable[JABU_JABUS_BELLY_MQ_DEPTHS] = Area("Jabu Jabus Belly MQ Depths", "Jabu Jabus Belly", JABU_JABUS_BELLY, NO_DAY_NIGHT_CYCLE, {}, {
@ -209,8 +209,8 @@ void AreaTable_Init_JabuJabusBelly() {
}, {
//Locations
LocationAccess(JABU_JABUS_BELLY_MQ_COW, {[]{return CanPlay(EponasSong);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_NEAR_BOSS_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_GS_NEAR_BOSS, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_NEAR_BOSS_CHEST, {[]{return CanUse(SLINGSHOT);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_GS_NEAR_BOSS, {[]{return CanUse(BOOMERANG) || (LogicJabuNearBossRanged && CanUse(HOOKSHOT));}}),
}, {
//Exits
Entrance(JABU_JABUS_BELLY_MQ_MAIN, {[]{return true;}}),
@ -245,5 +245,6 @@ void AreaTable_Init_JabuJabusBelly() {
{
// Exits
Entrance(JABU_JABUS_BELLY_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(ZORAS_FOUNTAIN, { [] { return JabuJabusBellyClear; } }),
});
}

View File

@ -209,5 +209,6 @@ void AreaTable_Init_ShadowTemple() {
{
// Exits
Entrance(SHADOW_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(GRAVEYARD_WARP_PAD_REGION, { [] { return ShadowTempleClear; } }),
});
}

View File

@ -271,5 +271,6 @@ void AreaTable_Init_SpiritTemple() {
{
// Exits
Entrance(SPIRIT_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(DESERT_COLOSSUS, { [] { return SpiritTempleClear; } }),
});
}

View File

@ -331,5 +331,6 @@ void AreaTable_Init_WaterTemple() {
{
// Exits
Entrance(WATER_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(LAKE_HYLIA, { [] { return WaterTempleClear; } }),
});
}

View File

@ -94,6 +94,7 @@ namespace Settings {
Option ShuffleOverworldSpawns = Option::Bool("Overworld Spawns", {"Off", "On"});
Option MixedEntrancePools = Option::Bool("Mixed Entrance Pools", {"Off", "On"});
Option MixDungeons = Option::Bool("Mix Dungeons", {"Off", "On"});
Option MixBosses = Option::Bool("Mix Bosses", {"Off", "On"});
Option MixOverworld = Option::Bool("Mix Overworld", {"Off", "On"});
Option MixInteriors = Option::Bool("Mix Interiors", {"Off", "On"});
Option MixGrottos = Option::Bool("Mix Grottos", {"Off", "On"});
@ -135,6 +136,7 @@ namespace Settings {
&ShuffleOverworldSpawns,
&MixedEntrancePools,
&MixDungeons,
&MixBosses,
&MixOverworld,
&MixInteriors,
&MixGrottos,
@ -1298,6 +1300,7 @@ namespace Settings {
ctx.shuffleOverworldSpawns = (ShuffleOverworldSpawns) ? 1 : 0;
ctx.mixedEntrancePools = (MixedEntrancePools) ? 1 : 0;
ctx.mixDungeons = (MixDungeons) ? 1 : 0;
ctx.mixBosses = (MixBosses) ? 1 : 0;
ctx.mixOverworld = (MixOverworld) ? 1 : 0;
ctx.mixInteriors = (MixInteriors) ? 1 : 0;
ctx.mixGrottos = (MixGrottos) ? 1 : 0;
@ -1891,6 +1894,13 @@ namespace Settings {
MixDungeons.SetSelectedIndex(OFF);
}
if (ShuffleBossEntrances.Is(SHUFFLEBOSSES_FULL)) {
MixBosses.Unhide();
} else {
MixBosses.Hide();
MixBosses.SetSelectedIndex(OFF);
}
if (ShuffleOverworldEntrances) {
MixOverworld.Unhide();
} else {
@ -1914,6 +1924,8 @@ namespace Settings {
} else {
MixDungeons.Hide();
MixDungeons.SetSelectedIndex(OFF);
MixBosses.Hide();
MixBosses.SetSelectedIndex(OFF);
MixOverworld.Hide();
MixOverworld.SetSelectedIndex(OFF);
MixInteriors.Hide();
@ -2190,6 +2202,7 @@ namespace Settings {
if (!MixedEntrancePools) {
MixDungeons.SetSelectedIndex(OFF);
MixBosses.SetSelectedIndex(OFF);
MixOverworld.SetSelectedIndex(OFF);
MixInteriors.SetSelectedIndex(OFF);
MixGrottos.SetSelectedIndex(OFF);
@ -2344,6 +2357,7 @@ namespace Settings {
ShuffleOverworldSpawns.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_OVERWORLD_SPAWNS]);
MixedEntrancePools.SetSelectedIndex(cvarSettings[RSK_MIXED_ENTRANCE_POOLS]);
MixDungeons.SetSelectedIndex(cvarSettings[RSK_MIX_DUNGEON_ENTRANCES]);
MixBosses.SetSelectedIndex(cvarSettings[RSK_MIX_BOSS_ENTRANCES]);
MixOverworld.SetSelectedIndex(cvarSettings[RSK_MIX_OVERWORLD_ENTRANCES]);
MixInteriors.SetSelectedIndex(cvarSettings[RSK_MIX_INTERIOR_ENTRANCES]);
MixGrottos.SetSelectedIndex(cvarSettings[RSK_MIX_GROTTO_ENTRANCES]);

View File

@ -397,6 +397,7 @@ typedef struct {
uint8_t shuffleOverworldSpawns;
uint8_t mixedEntrancePools;
uint8_t mixDungeons;
uint8_t mixBosses;
uint8_t mixOverworld;
uint8_t mixInteriors;
uint8_t mixGrottos;
@ -784,6 +785,7 @@ void UpdateSettings(std::unordered_map<RandomizerSettingKey, uint8_t> cvarSettin
extern Option ShuffleOverworldSpawns;
extern Option MixedEntrancePools;
extern Option MixDungeons;
extern Option MixBosses;
extern Option MixOverworld;
extern Option MixInteriors;
extern Option MixGrottos;

View File

@ -307,35 +307,36 @@ static void WriteLocation(
static void WriteShuffledEntrance(std::string sphereString, Entrance* entrance) {
int16_t originalIndex = entrance->GetIndex();
int16_t destinationIndex = -1;
int16_t originalBlueWarp = entrance->GetBlueWarp();
int16_t replacementBlueWarp = -1;
int16_t replacementIndex = entrance->GetReplacement()->GetIndex();
int16_t replacementDestinationIndex = -1;
std::string name = entrance->GetName();
std::string text = entrance->GetConnectedRegion()->regionName + " from " + entrance->GetReplacement()->GetParentRegion()->regionName;
if (entrance->GetReverse() != nullptr && !entrance->IsDecoupled()) {
// Track the reverse destination, useful for savewarp handling
if (entrance->GetReverse() != nullptr) {
destinationIndex = entrance->GetReverse()->GetIndex();
replacementDestinationIndex = entrance->GetReplacement()->GetReverse()->GetIndex();
replacementBlueWarp = entrance->GetReplacement()->GetReverse()->GetBlueWarp();
// When decouple is off we track the replacement's reverse destination, useful for recording visited entrances
if (!entrance->IsDecoupled()) {
replacementDestinationIndex = entrance->GetReplacement()->GetReverse()->GetIndex();
}
}
json entranceJson = json::object({
{"type", entrance->GetType()},
{"index", originalIndex},
{"destination", destinationIndex},
{"blueWarp", originalBlueWarp},
{"override", replacementIndex},
{"overrideDestination", replacementDestinationIndex},
});
jsonData["entrances"].push_back(entranceJson);
// When decoupled entrances is off, handle saving reverse entrances with blue warps
// When decoupled entrances is off, handle saving reverse entrances
if (entrance->GetReverse() != nullptr && !entrance->IsDecoupled()) {
json reverseEntranceJson = json::object({
{"type", entrance->GetReverse()->GetType()},
{"index", replacementDestinationIndex},
{"destination", replacementIndex},
{"blueWarp", replacementBlueWarp},
{"override", destinationIndex},
{"overrideDestination", originalIndex},
});

View File

@ -315,6 +315,7 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "World Settings:Overworld Spawns", RSK_SHUFFLE_OVERWORLD_SPAWNS },
{ "World Settings:Mixed Entrance Pools", RSK_MIXED_ENTRANCE_POOLS },
{ "World Settings:Mix Dungeons", RSK_MIX_DUNGEON_ENTRANCES },
{ "World Settings:Mix Bosses", RSK_MIX_BOSS_ENTRANCES },
{ "World Settings:Mix Overworld", RSK_MIX_OVERWORLD_ENTRANCES },
{ "World Settings:Mix Interiors", RSK_MIX_INTERIOR_ENTRANCES },
{ "World Settings:Mix Grottos", RSK_MIX_GROTTO_ENTRANCES },
@ -899,6 +900,7 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
case RSK_SHUFFLE_OVERWORLD_SPAWNS:
case RSK_MIXED_ENTRANCE_POOLS:
case RSK_MIX_DUNGEON_ENTRANCES:
case RSK_MIX_BOSS_ENTRANCES:
case RSK_MIX_OVERWORLD_ENTRANCES:
case RSK_MIX_INTERIOR_ENTRANCES:
case RSK_MIX_GROTTO_ENTRANCES:
@ -1562,9 +1564,9 @@ void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent)
// set all the entrances to be 0 to indicate an unshuffled entrance
for (auto &entranceOveride : gSaveContext.entranceOverrides) {
entranceOveride.type = 0;
entranceOveride.index = 0;
entranceOveride.destination = 0;
entranceOveride.blueWarp = 0;
entranceOveride.override = 0;
entranceOveride.overrideDestination = 0;
}
@ -1579,12 +1581,12 @@ void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent)
json entranceJson = *it;
for (auto entranceIt = entranceJson.begin(); entranceIt != entranceJson.end(); ++entranceIt) {
if (entranceIt.key() == "index") {
if (entranceIt.key() == "type") {
gSaveContext.entranceOverrides[i].type = entranceIt.value();
} else if (entranceIt.key() == "index") {
gSaveContext.entranceOverrides[i].index = entranceIt.value();
} else if (entranceIt.key() == "destination") {
gSaveContext.entranceOverrides[i].destination = entranceIt.value();
} else if (entranceIt.key() == "blueWarp") {
gSaveContext.entranceOverrides[i].blueWarp = entranceIt.value();
} else if (entranceIt.key() == "override") {
gSaveContext.entranceOverrides[i].override = entranceIt.value();
} else if (entranceIt.key() == "overrideDestination") {
@ -3008,6 +3010,7 @@ void GenerateRandomizerImgui(std::string seed = "") {
cvarSettings[RSK_SHUFFLE_OVERWORLD_SPAWNS] = CVarGetInteger("gRandomizeShuffleOverworldSpawns", RO_GENERIC_OFF);
cvarSettings[RSK_MIXED_ENTRANCE_POOLS] = CVarGetInteger("gRandomizeMixedEntrances", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_DUNGEON_ENTRANCES] = CVarGetInteger("gRandomizeMixDungeons", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_BOSS_ENTRANCES] = CVarGetInteger("gRandomizeMixBosses", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_OVERWORLD_ENTRANCES] = CVarGetInteger("gRandomizeMixOverworld", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_INTERIOR_ENTRANCES] = CVarGetInteger("gRandomizeMixInteriors", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_GROTTO_ENTRANCES] = CVarGetInteger("gRandomizeMixGrottos", RO_GENERIC_OFF);
@ -3706,7 +3709,8 @@ void RandomizerSettingsWindow::DrawElement() {
// Mixed Entrance Pools
UIWidgets::EnhancementCheckbox("Mixed Entrance Pools", "gRandomizeMixedEntrances");
UIWidgets::InsertHelpHoverText(
"Shuffle entrances into a mixed pool instead of separate ones.\n"
"Shuffle entrances into a mixed pool instead of separate ones. Has no affect on pools whose "
"entrances aren't shuffled, and \"Shuffle Boss Entrances\" must be set to \"Full\" to include them.\n"
"\n"
"For example, enabling the settings to shuffle grotto, dungeon, and overworld entrances and "
"selecting grotto and dungeon entrances here will allow a dungeon to be inside a grotto or "
@ -3720,6 +3724,13 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::EnhancementCheckbox("Mix Dungeons", "gRandomizeMixDungeons");
UIWidgets::InsertHelpHoverText("Dungeon entrances will be part of the mixed pool");
}
if (CVarGetInteger("gRandomizeShuffleBossEntrances", RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) ==
RO_BOSS_ROOM_ENTRANCE_SHUFFLE_FULL) {
UIWidgets::Spacer(0);
ImGui::SetCursorPosX(20);
UIWidgets::EnhancementCheckbox("Mix Bosses", "gRandomizeMixBosses");
UIWidgets::InsertHelpHoverText("Boss entrances will be part of the mixed pool");
}
if (CVarGetInteger("gRandomizeShuffleOverworldEntrances", RO_GENERIC_OFF)) {
UIWidgets::Spacer(0);
ImGui::SetCursorPosX(20);

View File

@ -1467,6 +1467,7 @@ typedef enum {
RSK_SHUFFLE_OVERWORLD_SPAWNS,
RSK_MIXED_ENTRANCE_POOLS,
RSK_MIX_DUNGEON_ENTRANCES,
RSK_MIX_BOSS_ENTRANCES,
RSK_MIX_OVERWORLD_ENTRANCES,
RSK_MIX_INTERIOR_ENTRANCES,
RSK_MIX_GROTTO_ENTRANCES,

View File

@ -39,17 +39,12 @@ s16 dynamicExitList[] = {
// Owl Flights : 0x492064 and 0x492080
static s16 entranceOverrideTable[ENTRANCE_TABLE_SIZE] = {0};
// Boss scenes (normalize boss scene range to 0 on lookup) to the replaced dungeon scene it is connected to
static s16 dungeonBossSceneOverrides[SHUFFLEABLE_BOSS_COUNT] = {0};
// Boss scenes (normalize boss scene range to 0 on lookup) mapped to save/death warp entrance
static s16 bossSceneSaveDeathWarps[SHUFFLEABLE_BOSS_COUNT] = {0};
static ActorEntry modifiedLinkActorEntry = {0};
EntranceInfo originalEntranceTable[ENTRANCE_TABLE_SIZE] = {0};
typedef struct {
s16 blueWarp;
s16 destination;
} BlueWarpReplacement;
typedef struct {
s16 entryway;
s16 exit;
@ -61,40 +56,25 @@ typedef struct {
} DungeonEntranceInfo;
static DungeonEntranceInfo dungeons[] = {
//entryway exit, boss, reverse, bluewarp, dungeon scene, boss scene
{ DEKU_TREE_ENTRANCE, ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, ENTR_KOKIRI_FOREST_11, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS },
{ DODONGOS_CAVERN_ENTRANCE, ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, ENTR_DEATH_MOUNTAIN_TRAIL_5, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS },
{ JABU_JABUS_BELLY_ENTRANCE, ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, ENTR_ZORAS_FOUNTAIN_0, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS },
{ FOREST_TEMPLE_ENTRANCE, ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, ENTR_SACRED_FOREST_MEADOW_3, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS },
{ FIRE_TEMPLE_ENTRANCE, ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, ENTR_DEATH_MOUNTAIN_CRATER_5, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS },
{ WATER_TEMPLE_ENTRANCE, ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, ENTR_LAKE_HYLIA_9, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS },
{ SPIRIT_TEMPLE_ENTRANCE, ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, ENTR_DESERT_COLOSSUS_8, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS },
{ SHADOW_TEMPLE_ENTRANCE, ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, ENTR_GRAVEYARD_8, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS },
//entryway exit, boss, reverse, bluewarp, dungeon scene, boss scene
{ ENTR_DEKU_TREE_0, ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, ENTR_KOKIRI_FOREST_11, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS },
{ ENTR_DODONGOS_CAVERN_0, ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, ENTR_DEATH_MOUNTAIN_TRAIL_5, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS },
{ ENTR_JABU_JABU_0, ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, ENTR_ZORAS_FOUNTAIN_0, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS },
{ ENTR_FOREST_TEMPLE_0, ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, ENTR_SACRED_FOREST_MEADOW_3, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS },
{ ENTR_FIRE_TEMPLE_0, ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, ENTR_DEATH_MOUNTAIN_CRATER_5, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS },
{ ENTR_WATER_TEMPLE_0, ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, ENTR_LAKE_HYLIA_9, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS },
{ ENTR_SPIRIT_TEMPLE_0, ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, ENTR_DESERT_COLOSSUS_8, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS },
{ ENTR_SHADOW_TEMPLE_0, ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, ENTR_GRAVEYARD_8, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS },
};
//These variables store the new entrance indices for dungeons so that
//savewarping and game overs respawn players at the proper entrance.
//By default, these will be their vanilla values.
static s16 newDekuTreeEntrance = DEKU_TREE_ENTRANCE;
static s16 newDodongosCavernEntrance = DODONGOS_CAVERN_ENTRANCE;
static s16 newJabuJabusBellyEntrance = JABU_JABUS_BELLY_ENTRANCE;
static s16 newForestTempleEntrance = FOREST_TEMPLE_ENTRANCE;
static s16 newFireTempleEntrance = FIRE_TEMPLE_ENTRANCE;
static s16 newWaterTempleEntrance = WATER_TEMPLE_ENTRANCE;
static s16 newSpiritTempleEntrance = SPIRIT_TEMPLE_ENTRANCE;
static s16 newShadowTempleEntrance = SHADOW_TEMPLE_ENTRANCE;
static s16 newBottomOfTheWellEntrance = BOTTOM_OF_THE_WELL_ENTRANCE;
static s16 newGerudoTrainingGroundsEntrance = GERUDO_TRAINING_GROUNDS_ENTRANCE;
static s16 newIceCavernEntrance = ICE_CAVERN_ENTRANCE;
static s8 hasCopiedEntranceTable = 0;
static s8 hasModifiedEntranceTable = 0;
void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance);
u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) {
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0
&& entranceOverride->override == 0 && entranceOverride->overrideDestination == 0;
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->override == 0 &&
entranceOverride->overrideDestination == 0;
}
static void Entrance_SeparateOGCFairyFountainExit(void) {
@ -113,6 +93,28 @@ static void Entrance_SeparateAdultSpawnAndPrelude() {
}
}
// Fix Adult dungeon blue warps as Child by assigning the child values for the warp pads
static void Entrance_ReplaceChildTempleWarps() {
if (Randomizer_GetSettingValue(RSK_SHUFFLE_DUNGEON_ENTRANCES) != RO_DUNGEON_ENTRANCE_SHUFFLE_OFF ||
Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) {
// Forest Temple
gEntranceTable[ENTR_SACRED_FOREST_MEADOW_3] = gEntranceTable[ENTR_SACRED_FOREST_MEADOW_2];
gEntranceTable[ENTR_SACRED_FOREST_MEADOW_3_1] = gEntranceTable[ENTR_SACRED_FOREST_MEADOW_2_1];
// Fire Temple
gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_5] = gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_4];
gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_5_1] = gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_4_1];
// Water Temple
gEntranceTable[ENTR_LAKE_HYLIA_9] = gEntranceTable[ENTR_LAKE_HYLIA_8];
gEntranceTable[ENTR_LAKE_HYLIA_9_1] = gEntranceTable[ENTR_LAKE_HYLIA_8_1];
// Shadow Temple
gEntranceTable[ENTR_GRAVEYARD_8] = gEntranceTable[ENTR_GRAVEYARD_7];
gEntranceTable[ENTR_GRAVEYARD_8_1] = gEntranceTable[ENTR_GRAVEYARD_7_1];
// Spirit Temple
gEntranceTable[ENTR_DESERT_COLOSSUS_8] = gEntranceTable[ENTR_DESERT_COLOSSUS_5];
gEntranceTable[ENTR_DESERT_COLOSSUS_8_1] = gEntranceTable[ENTR_DESERT_COLOSSUS_5_1];
}
}
void Entrance_CopyOriginalEntranceTable(void) {
if (!hasCopiedEntranceTable) {
memcpy(originalEntranceTable, gEntranceTable, sizeof(EntranceInfo) * ENTRANCE_TABLE_SIZE);
@ -130,9 +132,6 @@ void Entrance_ResetEntranceTable(void) {
void Entrance_Init(void) {
s32 index;
size_t blueWarpRemapIdx = 0;
BlueWarpReplacement bluewarps[SHUFFLEABLE_BOSS_COUNT] = {0};
Entrance_CopyOriginalEntranceTable();
// Skip Child Stealth if given by settings
@ -149,6 +148,7 @@ void Entrance_Init(void) {
Entrance_SeparateOGCFairyFountainExit();
Entrance_SeparateAdultSpawnAndPrelude();
Entrance_ReplaceChildTempleWarps();
// Initialize the entrance override table with each index leading to itself. An
// index referring to itself means that the entrance is not currently shuffled.
@ -156,9 +156,9 @@ void Entrance_Init(void) {
entranceOverrideTable[i] = i;
}
// Initialize all boss rooms connected to their vanilla dungeon
// Initialize all boss room save/death warps with their vanilla dungeon entryway
for (s16 i = 1; i < SHUFFLEABLE_BOSS_COUNT; i++) {
dungeonBossSceneOverrides[i] = i;
bossSceneSaveDeathWarps[i] = dungeons[i].entryway;
}
// Initialize the grotto exit and load lists
@ -172,9 +172,30 @@ void Entrance_Init(void) {
}
s16 originalIndex = gSaveContext.entranceOverrides[i].index;
s16 blueWarpIndex = gSaveContext.entranceOverrides[i].blueWarp;
s16 originalDestination = gSaveContext.entranceOverrides[i].destination;
s16 overrideIndex = gSaveContext.entranceOverrides[i].override;
int16_t bossScene = -1;
int16_t saveWarpEntrance = originalDestination; // Default save warp to the original return entrance
// Search for boss room overrides and look for the matching save/death warp value to use
// If the boss room is in a dungeon, use the dungeons entryway as the save warp
// Otherwise use the "exit" value for the entrance that lead to the boss room
for (int j = 0; j <= SHUFFLEABLE_BOSS_COUNT; j++) {
if (overrideIndex == dungeons[j].bossDoor) {
bossScene = dungeons[j].bossScene;
}
if (index == dungeons[j].bossDoor) {
saveWarpEntrance = dungeons[j].entryway;
}
}
// Found a boss scene and a valid save/death warp value
if (bossScene != -1 && saveWarpEntrance != -1) {
bossSceneSaveDeathWarps[bossScene - SCENE_DEKU_TREE_BOSS] = saveWarpEntrance;
}
//Overwrite grotto related indices
if (originalIndex >= ENTRANCE_RANDO_GROTTO_EXIT_START) {
Grotto_SetExitOverride(originalIndex, overrideIndex);
@ -189,36 +210,6 @@ void Entrance_Init(void) {
// Overwrite the indices which we want to shuffle, leaving the rest as they are
entranceOverrideTable[originalIndex] = overrideIndex;
if (blueWarpIndex != 0) {
// When boss shuffle is enabled, we need to know what dungeon the boss room is connected to for
// death/save warping, and for the blue warp
if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) {
s16 bossScene = -1;
s16 replacedDungeonScene = -1;
s16 replacedDungeonExit = -1;
// Search for the boss scene and replaced blue warp exits
for (s16 j = 0; j <= SHUFFLEABLE_BOSS_COUNT; j++) {
if (blueWarpIndex == dungeons[j].blueWarp) {
bossScene = dungeons[j].bossScene;
}
if (overrideIndex == dungeons[j].bossDoorReverse) {
replacedDungeonScene = dungeons[j].scene;
replacedDungeonExit = dungeons[j].exit;
}
}
// assign the boss scene override
if (bossScene != -1 && replacedDungeonScene != -1 && replacedDungeonExit != -1) {
dungeonBossSceneOverrides[bossScene - SCENE_DEKU_TREE_BOSS] = replacedDungeonScene;
bluewarps[blueWarpRemapIdx].blueWarp = blueWarpIndex;
bluewarps[blueWarpRemapIdx].destination = replacedDungeonExit;
blueWarpRemapIdx++;
}
} else {
entranceOverrideTable[blueWarpIndex] = overrideIndex;
}
}
//Override both land and water entrances for Hyrule Field -> ZR Front and vice versa
if (originalIndex == ENTR_ZORAS_RIVER_0) { //Hyrule Field -> ZR Front land entrance
entranceOverrideTable[ENTR_ZORAS_RIVER_3] = overrideIndex;
@ -227,14 +218,6 @@ void Entrance_Init(void) {
}
}
// If we have remapped blue warps from boss shuffle, handle setting those and grabbing the override for
// the replaced dungeons exit in the event that dungeon shuffle is also turned on
for (size_t i = 0; i < ARRAY_COUNT(bluewarps); i++) {
if (bluewarps[i].blueWarp != 0 && bluewarps[i].destination != 0) {
entranceOverrideTable[bluewarps[i].blueWarp] = Entrance_GetOverride(bluewarps[i].destination);
}
}
// Stop playing background music during shuffled entrance transitions
// so that we don't get duplicated or overlapping music tracks
if (Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) {
@ -328,40 +311,39 @@ void Entrance_SetGameOverEntrance(void) {
s16 scene = gPlayState->sceneNum;
// When in a boss room and boss shuffle is on, get the connected dungeon's original boss room entrance
// then run the normal game over overrides on it
// When in a boss room and boss shuffle is on, use the boss scene to find the death warp entrance
if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF &&
scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_SHADOW_TEMPLE_BOSS) {
// Normalize boss scene range to 0 on lookup
scene = dungeonBossSceneOverrides[scene - SCENE_DEKU_TREE_BOSS];
gSaveContext.entranceIndex = dungeons[scene].bossDoor;
// Normalize boss scene range to 0 on lookup and handle for grotto entrances
gSaveContext.entranceIndex = Grotto_OverrideSpecialEntrance(bossSceneSaveDeathWarps[scene - SCENE_DEKU_TREE_BOSS]);
return;
}
//Set the current entrance depending on which entrance the player last came through
switch (gSaveContext.entranceIndex) {
case ENTR_DEKU_TREE_BOSS_0 : //Deku Tree Boss Room
gSaveContext.entranceIndex = newDekuTreeEntrance;
gSaveContext.entranceIndex = ENTR_DEKU_TREE_0;
return;
case ENTR_DODONGOS_CAVERN_BOSS_0 : //Dodongos Cavern Boss Room
gSaveContext.entranceIndex = newDodongosCavernEntrance;
gSaveContext.entranceIndex = ENTR_DODONGOS_CAVERN_0;
return;
case ENTR_JABU_JABU_BOSS_0 : //Jabu Jabus Belly Boss Room
gSaveContext.entranceIndex = newJabuJabusBellyEntrance;
gSaveContext.entranceIndex = ENTR_JABU_JABU_0;
return;
case ENTR_FOREST_TEMPLE_BOSS_0 : //Forest Temple Boss Room
gSaveContext.entranceIndex = newForestTempleEntrance;
gSaveContext.entranceIndex = ENTR_FOREST_TEMPLE_0;
return;
case ENTR_FIRE_TEMPLE_BOSS_0 : //Fire Temple Boss Room
gSaveContext.entranceIndex = newFireTempleEntrance;
gSaveContext.entranceIndex = ENTR_FIRE_TEMPLE_0;
return;
case ENTR_WATER_TEMPLE_BOSS_0 : //Water Temple Boss Room
gSaveContext.entranceIndex = newWaterTempleEntrance;
gSaveContext.entranceIndex = ENTR_WATER_TEMPLE_0;
return;
case ENTR_SPIRIT_TEMPLE_BOSS_0 : //Spirit Temple Boss Room
gSaveContext.entranceIndex = newSpiritTempleEntrance;
gSaveContext.entranceIndex = ENTR_SPIRIT_TEMPLE_0;
return;
case ENTR_SHADOW_TEMPLE_BOSS_0 : //Shadow Temple Boss Room
gSaveContext.entranceIndex = newShadowTempleEntrance;
gSaveContext.entranceIndex = ENTR_SHADOW_TEMPLE_0;
return;
case ENTR_GANONDORF_BOSS_0 : //Ganondorf Boss Room
gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb
@ -374,46 +356,46 @@ void Entrance_SetSavewarpEntrance(void) {
s16 scene = gSaveContext.savedSceneNum;
// When in a boss room and boss shuffle is on, use the boss scene override to remap to its
// connected dungeon and use that for the final entrance
// When in a boss room and boss shuffle is on, use the boss scene to find the savewarp entrance
if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF &&
scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_SHADOW_TEMPLE_BOSS) {
// Normalize boss scene range to 0 on lookup
scene = dungeonBossSceneOverrides[scene - SCENE_DEKU_TREE_BOSS];
// Normalize boss scene range to 0 on lookup and handle for grotto entrances
gSaveContext.entranceIndex = Grotto_OverrideSpecialEntrance(bossSceneSaveDeathWarps[scene - SCENE_DEKU_TREE_BOSS]);
return;
}
if (scene == SCENE_DEKU_TREE || scene == SCENE_DEKU_TREE_BOSS) {
gSaveContext.entranceIndex = newDekuTreeEntrance;
gSaveContext.entranceIndex = ENTR_DEKU_TREE_0;
} else if (scene == SCENE_DODONGOS_CAVERN || scene == SCENE_DODONGOS_CAVERN_BOSS) {
gSaveContext.entranceIndex = newDodongosCavernEntrance;
gSaveContext.entranceIndex = ENTR_DODONGOS_CAVERN_0;
} else if (scene == SCENE_JABU_JABU || scene == SCENE_JABU_JABU_BOSS) {
gSaveContext.entranceIndex = newJabuJabusBellyEntrance;
gSaveContext.entranceIndex = ENTR_JABU_JABU_0;
} else if (scene == SCENE_FOREST_TEMPLE || scene == SCENE_FOREST_TEMPLE_BOSS) { //Forest Temple Boss Room
gSaveContext.entranceIndex = newForestTempleEntrance;
gSaveContext.entranceIndex = ENTR_FOREST_TEMPLE_0;
} else if (scene == SCENE_FIRE_TEMPLE || scene == SCENE_FIRE_TEMPLE_BOSS) { //Fire Temple Boss Room
gSaveContext.entranceIndex = newFireTempleEntrance;
gSaveContext.entranceIndex = ENTR_FIRE_TEMPLE_0;
} else if (scene == SCENE_WATER_TEMPLE || scene == SCENE_WATER_TEMPLE_BOSS) { //Water Temple Boss Room
gSaveContext.entranceIndex = newWaterTempleEntrance;
gSaveContext.entranceIndex = ENTR_WATER_TEMPLE_0;
} else if (scene == SCENE_SPIRIT_TEMPLE || scene == SCENE_SPIRIT_TEMPLE_BOSS) { //Spirit Temple Boss Room
gSaveContext.entranceIndex = newSpiritTempleEntrance;
gSaveContext.entranceIndex = ENTR_SPIRIT_TEMPLE_0;
} else if (scene == SCENE_SHADOW_TEMPLE || scene == SCENE_SHADOW_TEMPLE_BOSS) { //Shadow Temple Boss Room
gSaveContext.entranceIndex = newShadowTempleEntrance;
gSaveContext.entranceIndex = ENTR_SHADOW_TEMPLE_0;
} else if (scene == SCENE_BOTTOM_OF_THE_WELL) { // BOTW
gSaveContext.entranceIndex = newBottomOfTheWellEntrance;
gSaveContext.entranceIndex = ENTR_BOTTOM_OF_THE_WELL_0;
} else if (scene == SCENE_GERUDO_TRAINING_GROUND) { // GTG
gSaveContext.entranceIndex = newGerudoTrainingGroundsEntrance;
gSaveContext.entranceIndex = ENTR_GERUDO_TRAINING_GROUND_0;
} else if (scene == SCENE_ICE_CAVERN) { // Ice cavern
gSaveContext.entranceIndex = newIceCavernEntrance;
gSaveContext.entranceIndex = ENTR_ICE_CAVERN_0;
} else if (scene == SCENE_INSIDE_GANONS_CASTLE) {
gSaveContext.entranceIndex = GANONS_CASTLE_ENTRANCE;
gSaveContext.entranceIndex = ENTR_INSIDE_GANONS_CASTLE_0;
} else if (scene == SCENE_GANONS_TOWER || scene == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE || scene == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || scene == SCENE_GANON_BOSS || scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) {
gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb
} else if (scene == SCENE_THIEVES_HIDEOUT) { // Theives hideout
gSaveContext.entranceIndex = ENTR_THIEVES_HIDEOUT_0; // Gerudo Fortress -> Thieve's Hideout spawn 0
} else if (scene == SCENE_LINKS_HOUSE) {
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE);
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_LINKS_HOUSE_0);
} else if (LINK_IS_CHILD) {
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); // Child Overworld Spawn
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_LINKS_HOUSE_0); // Child Overworld Spawn
} else {
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_HYRULE_FIELD_10); // Adult Overworld Spawn (Normally 0x5F4 (ENTR_TEMPLE_OF_TIME_7), but 0x282 (ENTR_HYRULE_FIELD_10) has been repurposed to differentiate from Prelude which also uses 0x5F4)
}
@ -494,7 +476,7 @@ void Entrance_OverrideBlueWarp(void) {
void Entrance_OverrideCutsceneEntrance(u16 cutsceneCmd) {
switch (cutsceneCmd) {
case 24: // Dropping a fish for Jabu Jabu
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(newJabuJabusBellyEntrance);
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_JABU_JABU_0);
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
// In case Jabu's mouth leads to a grotto return

View File

@ -8,20 +8,6 @@
#define ENTRANCE_TABLE_SIZE ENTR_MAX
#define DEKU_TREE_ENTRANCE ENTR_DEKU_TREE_0
#define DODONGOS_CAVERN_ENTRANCE ENTR_DODONGOS_CAVERN_0
#define JABU_JABUS_BELLY_ENTRANCE ENTR_JABU_JABU_0
#define FOREST_TEMPLE_ENTRANCE ENTR_FOREST_TEMPLE_0
#define FIRE_TEMPLE_ENTRANCE ENTR_FIRE_TEMPLE_0
#define WATER_TEMPLE_ENTRANCE ENTR_WATER_TEMPLE_0
#define SPIRIT_TEMPLE_ENTRANCE ENTR_SPIRIT_TEMPLE_0
#define SHADOW_TEMPLE_ENTRANCE ENTR_SHADOW_TEMPLE_0
#define BOTTOM_OF_THE_WELL_ENTRANCE ENTR_BOTTOM_OF_THE_WELL_0
#define GERUDO_TRAINING_GROUNDS_ENTRANCE ENTR_GERUDO_TRAINING_GROUND_0
#define ICE_CAVERN_ENTRANCE ENTR_ICE_CAVERN_0
#define GANONS_CASTLE_ENTRANCE ENTR_INSIDE_GANONS_CASTLE_0
#define LINK_HOUSE_SAVEWARP_ENTRANCE ENTR_LINKS_HOUSE_0
#define ENTRANCE_RANDO_GROTTO_LOAD_START 0x0700
#define ENTRANCE_RANDO_GROTTO_EXIT_START 0x0800
#define MAX_ENTRANCE_RANDO_USED_INDEX 0x0820
@ -66,7 +52,7 @@ typedef enum {
#define ENTRANCE_RANDO_GROTTO_LOAD(index) ENTRANCE_RANDO_GROTTO_LOAD_START + index
#define ENTRANCE_RANDO_GROTTO_EXIT(index) ENTRANCE_RANDO_GROTTO_EXIT_START + index
#define ENTRANCE_OVERRIDES_MAX_COUNT 259 // 11 one-way entrances + 124 two-way entrances (x2)
#define ENTRANCE_OVERRIDES_MAX_COUNT 267 // 19 one-way entrances + 124 two-way entrances (x2)
#define SHUFFLEABLE_BOSS_COUNT 8
#define SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT 66 // Max entrance rando index is 0x0820, (2080 / 32 == 65) + 1
@ -79,9 +65,9 @@ typedef enum {
(((startTransType) << ENTRANCE_INFO_START_TRANS_TYPE_SHIFT) & ENTRANCE_INFO_START_TRANS_TYPE_MASK))
typedef struct {
uint16_t type;
int16_t index;
int16_t destination;
int16_t blueWarp;
int16_t override;
int16_t overrideDestination;
} EntranceOverride;

View File

@ -104,6 +104,7 @@ const EntranceData entranceData[] = {
{ ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_0, SINGLE_SCENE_INFO(SCENE_DEKU_TREE), "Deku Tree", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, ""},
{ ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, SINGLE_SCENE_INFO(SCENE_DEKU_TREE), "Deku Tree Boss Door", "Gohma", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_DEKU_TREE_1, ENTR_DEKU_TREE_BOSS_0, SINGLE_SCENE_INFO(SCENE_DEKU_TREE_BOSS), "Gohma", "Deku Tree Boss Door", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_KOKIRI_FOREST_11, -1, SINGLE_SCENE_INFO(SCENE_DEKU_TREE_BOSS), "Gohma", "Deku Tree Blue Warp", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Lost Woods
{ ENTR_KOKIRI_FOREST_2, ENTR_LOST_WOODS_9, SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods Bridge", "KF", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_OVERWORLD, "lw"},
@ -131,6 +132,7 @@ const EntranceData entranceData[] = {
{ ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE), "Forest Temple", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON},
{ ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE), "Forest Temple Boss Door", "Phantom Ganon", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_FOREST_TEMPLE_1, ENTR_FOREST_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE_BOSS), "Phantom Ganon", "Forest Temple Boss Door", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_SACRED_FOREST_MEADOW_3, -1, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE_BOSS), "Phantom Ganon", "Forest Temple Blue Warp", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Kakariko Village
{ ENTR_HYRULE_FIELD_1, ENTR_KAKARIKO_VILLAGE_0, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Hyrule Field", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
@ -179,6 +181,7 @@ const EntranceData entranceData[] = {
{ ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE), "Shadow Temple", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON},
{ ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE), "Shadow Temple Boss Door", "Bongo-Bongo", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_SHADOW_TEMPLE_1, ENTR_SHADOW_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE_BOSS), "Bongo-Bongo", "Shadow Temple Boss Door", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_GRAVEYARD_8, -1, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE_BOSS), "Bongo-Bongo", "Shadow Temple Blue Warp", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Death Mountain Trail
{ ENTR_GORON_CITY_0, ENTR_DEATH_MOUNTAIN_TRAIL_1, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"},
@ -194,6 +197,7 @@ const EntranceData entranceData[] = {
{ ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_0, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN), "Dodongo's Cavern", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc"},
{ ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN), "Dodongo's Cavern Boss Door", "King Dodongo", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ ENTR_DODONGOS_CAVERN_1, ENTR_DODONGOS_CAVERN_BOSS_0, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN_BOSS), "King Dodongo", "Dodongo's Cavern Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ ENTR_DEATH_MOUNTAIN_TRAIL_5, -1, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN_BOSS), "King Dodongo", "Dodongo's Cavern Blue Warp", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_ONE_WAY, "dc,bw", 1},
// Death Mountain Crater
{ ENTR_GORON_CITY_1, ENTR_DEATH_MOUNTAIN_CRATER_1, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_CRATER), "DMC", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"},
@ -208,6 +212,7 @@ const EntranceData entranceData[] = {
{ ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE), "Fire Temple", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON},
{ ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE), "Fire Temple Boss Door", "Volvagia", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_FIRE_TEMPLE_1, ENTR_FIRE_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE_BOSS), "Volvagia", "Fire Temple Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_DEATH_MOUNTAIN_CRATER_5, -1, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE_BOSS), "Volvagia", "Fire Temple Blue Warp", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Goron City
{ ENTR_DEATH_MOUNTAIN_TRAIL_1, ENTR_GORON_CITY_0, SINGLE_SCENE_INFO(SCENE_GORON_CITY), "Goron City", "DMT", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD, "gc"},
@ -247,6 +252,7 @@ const EntranceData entranceData[] = {
{ ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_0, SINGLE_SCENE_INFO(SCENE_JABU_JABU), "Jabu Jabu's Belly", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON},
{ ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, SINGLE_SCENE_INFO(SCENE_JABU_JABU), "Jabu Jabu's Belly Boss Door", "Barinade", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_JABU_JABU_1, ENTR_JABU_JABU_BOSS_0, SINGLE_SCENE_INFO(SCENE_JABU_JABU_BOSS), "Barinade", "Jabu Jabu's Belly Boss Door", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_ZORAS_FOUNTAIN_0, -1, SINGLE_SCENE_INFO(SCENE_JABU_JABU_BOSS), "Barinade", "Jabu Jabu's Belly Blue Warp", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
{ ENTR_ZORAS_FOUNTAIN_3, ENTR_ICE_CAVERN_0, SINGLE_SCENE_INFO(SCENE_ICE_CAVERN), "Ice Cavern", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON},
// Hyrule Field
@ -298,6 +304,7 @@ const EntranceData entranceData[] = {
{ ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE), "Water Temple", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh"},
{ ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE), "Water Temple Boss Door", "Morpha", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1},
{ ENTR_WATER_TEMPLE_1, ENTR_WATER_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE_BOSS), "Morpha", "Water Temple Boss Door", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1},
{ ENTR_LAKE_HYLIA_9, -1, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE_BOSS), "Morpha", "Water Temple Blue Warp", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_ONE_WAY, "lh,bw", 1},
// Gerudo Area
{ ENTR_HYRULE_FIELD_5, ENTR_GERUDO_VALLEY_0, SINGLE_SCENE_INFO(SCENE_GERUDO_VALLEY), "GV", "Hyrule Field", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
@ -328,6 +335,7 @@ const EntranceData entranceData[] = {
{ ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE), "Spirit Temple", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc"},
{ ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE), "Spirit Temple Boss Door", "Twinrova", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_SPIRIT_TEMPLE_1, ENTR_SPIRIT_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE_BOSS), "Twinrova", "Spirit Temple Boss Door", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_DESERT_COLOSSUS_8, -1, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE_BOSS), "Twinrova", "Spirit Temple Blue Warp", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Market
{ ENTR_HYRULE_FIELD_7, ENTR_MARKET_ENTRANCE_DAY_1, {SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_DAY), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_RUINS)}, "Market Entrance", "Hyrule Field", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
@ -797,6 +805,11 @@ void EntranceTrackerWindow::DrawElement() {
continue;
}
// RANDOTODO: Only show blue warps if bluewarp shuffle is on
if (original->metaTag.ends_with("bw") || override->metaTag.ends_with("bw")) {
continue;
}
bool isDiscovered = IsEntranceDiscovered(entrance.index);
bool showOriginal = (!destToggle ? CVarGetInteger("gEntranceTrackerShowTo", 0) : CVarGetInteger("gEntranceTrackerShowFrom", 0)) || isDiscovered;

View File

@ -184,9 +184,9 @@ void SaveManager::LoadRandomizerVersion2() {
SaveManager::Instance->LoadArray("entrances", ARRAY_COUNT(gSaveContext.entranceOverrides), [&](size_t i) {
SaveManager::Instance->LoadStruct("", [&]() {
SaveManager::Instance->LoadData("type", gSaveContext.entranceOverrides[i].type);
SaveManager::Instance->LoadData("index", gSaveContext.entranceOverrides[i].index);
SaveManager::Instance->LoadData("destination", gSaveContext.entranceOverrides[i].destination);
SaveManager::Instance->LoadData("blueWarp", gSaveContext.entranceOverrides[i].blueWarp);
SaveManager::Instance->LoadData("override", gSaveContext.entranceOverrides[i].override);
SaveManager::Instance->LoadData("overrideDestination", gSaveContext.entranceOverrides[i].overrideDestination);
});
@ -292,9 +292,9 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f
SaveManager::Instance->SaveArray("entrances", ARRAY_COUNT(saveContext->entranceOverrides), [&](size_t i) {
SaveManager::Instance->SaveStruct("", [&]() {
SaveManager::Instance->SaveData("type", saveContext->entranceOverrides[i].type);
SaveManager::Instance->SaveData("index", saveContext->entranceOverrides[i].index);
SaveManager::Instance->SaveData("destination", saveContext->entranceOverrides[i].destination);
SaveManager::Instance->SaveData("blueWarp", saveContext->entranceOverrides[i].blueWarp);
SaveManager::Instance->SaveData("override", saveContext->entranceOverrides[i].override);
SaveManager::Instance->SaveData("overrideDestination", saveContext->entranceOverrides[i].overrideDestination);
});

View File

@ -575,6 +575,13 @@ u32 func_80096FE8(PlayState* play, RoomContext* roomCtx) {
frontRoom = gSaveContext.respawnFlag > 0 ? ((void)0, gSaveContext.respawn[gSaveContext.respawnFlag - 1].roomIndex)
: play->setupEntranceList[play->curSpawn].room;
// In ER, override roomNum to load based on scene and spawn during scene init
if (IS_RANDO && gSaveContext.respawnFlag <= 0 &&
Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) {
frontRoom = Entrance_OverrideSpawnSceneRoom(play->sceneNum, play->curSpawn, frontRoom);
}
func_8009728C(play, roomCtx, frontRoom);
return maxRoomSize;
@ -583,12 +590,6 @@ u32 func_80096FE8(PlayState* play, RoomContext* roomCtx) {
s32 func_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomNum) {
size_t size;
// In ER, override roomNum to load based on scene and spawn
if (IS_RANDO && gSaveContext.respawnFlag <= 0 &&
Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) {
roomNum = Entrance_OverrideSpawnSceneRoom(play->sceneNum, play->curSpawn, roomNum);
}
return OTRfunc_8009728C(play, roomCtx, roomNum);
if (roomCtx->status == 0) {

View File

@ -4538,7 +4538,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol
Scene_SetTransitionForNextEntrance(play);
} else {
if (SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2) {
// In Entrance rando, if our respawnFlag is set for a grotto return, we don't want the void out to happen
if (SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2 &&
(!IS_RANDO || (Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES) && gSaveContext.respawnFlag != 2))) {
gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = play->nextEntranceIndex;
Play_TriggerVoidOut(play);
gSaveContext.respawnFlag = -2;