RandoV3 fixes for mix/decouple boss entrances

This commit is contained in:
Adam Bird 2024-02-20 16:52:42 -05:00
parent 37b960ab0e
commit 8b178f9386
7 changed files with 184 additions and 69 deletions

View File

@ -502,7 +502,7 @@ Area* AreaTable(const RandomizerRegion areaKey) {
std::vector<Rando::Entrance*> GetShuffleableEntrances(Rando::EntranceType type, bool onlyPrimary /*= true*/) {
std::vector<Rando::Entrance*> entrancesToShuffle = {};
for (RandomizerRegion area : Areas::GetAllAreas()) {
for (auto& exit: AreaTable(area)->exits) {
for (auto& exit : AreaTable(area)->exits) {
if ((exit.GetType() == type || type == Rando::EntranceType::All) && (exit.IsPrimary() || !onlyPrimary) && exit.GetType() != Rando::EntranceType::None) {
entrancesToShuffle.push_back(&exit);
}

View File

@ -10,6 +10,7 @@ EntranceLinkInfo NO_RETURN_ENTRANCE = { EntranceType::None, RR_NONE, RR_NONE, -1
Entrance::Entrance(RandomizerRegion 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];
@ -109,6 +110,10 @@ RandomizerRegion Entrance::GetConnectedRegionKey() const {
return connectedRegion;
}
RandomizerRegion Entrance::GetOriginalConnectedRegionKey() const {
return originalConnectedRegion;
}
Area* Entrance::GetConnectedRegion() const {
return AreaTable(connectedRegion);
}
@ -173,14 +178,6 @@ void Entrance::SetIndex(int16_t newIndex) {
index = newIndex;
}
int16_t Entrance::GetBlueWarp() const {
return blueWarp;
}
void Entrance::SetBlueWarp(int16_t newBlueWarp) {
blueWarp = newBlueWarp;
}
Entrance* Entrance::GetAssumed() const {
return assumed;
}
@ -226,7 +223,7 @@ Entrance* Entrance::GetNewTarget() {
AreaTable(RR_ROOT)->AddExit(RR_ROOT, connectedRegion, [] { return true; });
Entrance* targetEntrance = AreaTable(RR_ROOT)->GetExit(connectedRegion);
targetEntrance->SetReplacement(this);
targetEntrance->SetName(GetParentRegion()->regionName + " -> " + GetConnectedRegion()->regionName);
targetEntrance->SetName(AreaTable(RR_ROOT)->regionName + " -> " + GetConnectedRegion()->regionName);
return targetEntrance;
}
@ -246,6 +243,11 @@ void EntranceShuffler::SetNoRandomEntrances(bool noRandomEntrances) {
mNoRandomEntrances = noRandomEntrances;
}
// Construct entrance name from parent and connected region keys
std::string EntranceNameByRegions(RandomizerRegion parentRegion, RandomizerRegion connectedRegion) {
return AreaTable(parentRegion)->regionName + " -> " + AreaTable(connectedRegion)->regionName;
}
void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
auto ctx = Rando::Context::GetInstance();
for (auto& entrancePair : entranceShuffleTable) {
@ -256,26 +258,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 (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) && forwardEntry.type != EntranceType::ChildBoss &&
forwardEntry.type != EntranceType::AdultBoss) {
// When decouple entrances is on, mark the forward entrance
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
forwardEntrance->SetDecoupled();
}
if (returnEntry.parentRegion != RR_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 (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) && returnEntry.type != EntranceType::ChildBoss &&
returnEntry.type != EntranceType::AdultBoss) {
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
returnEntrance->SetDecoupled();
}
}
@ -925,24 +923,23 @@ int EntranceShuffler::ShuffleAllEntrances() {
mCurNumRandomizedEntrances = 0;
std::vector<EntranceInfoPair> entranceShuffleTable = {
// Parent Region Connected Region index blue warp
// Type Parent Region Connected Region Index
{ { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, 0x0000 },
{ EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, 0x0209, 0x0457 } },
{ EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, 0x0209 } },
{ { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, 0x0004 },
{ EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, 0x0242, 0x047A } },
{ EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, 0x0242 } },
{ { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, 0x0028 },
{ EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, 0x0221, 0x010E } },
{ EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, 0x0221 } },
{ { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, 0x0169 },
{ EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, 0x0215, 0x0608 } },
{ EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, 0x0215 } },
{ { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, 0x0165 },
{ EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, 0x024A, 0x0564 } },
{ EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, 0x024A } },
{ { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, 0x0010 },
{ EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, 0x021D, 0x060C } },
{ EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, 0x021D } },
{ { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, 0x0082 },
{ EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY, 0x01E1,
0x0610 } },
{ EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY, 0x01E1 } },
{ { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, 0x0037 },
{ EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, 0x0205, 0x0580 } },
{ EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, 0x0205 } },
{ { EntranceType::Dungeon, RR_KAKARIKO_VILLAGE, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, 0x0098 },
{ EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAKARIKO_VILLAGE, 0x02A6 } },
{ { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_ICE_CAVERN_ENTRYWAY, 0x0088 },
@ -1190,22 +1187,32 @@ int EntranceShuffler::ShuffleAllEntrances() {
{ { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, 0x05F4 }, NO_RETURN_ENTRANCE },
{ { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, 0x040F },
{ EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, 0x0252, 0x0457 } },
{ EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, 0x0252 } },
{ { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, 0x040B },
{ EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, 0x00C5, 0x047A } },
{ EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, 0x00C5 } },
{ { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, 0x0301 },
{ EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, 0x0407,
0x010E } },
{ EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, 0x0407 } },
{ { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, 0x000C },
{ EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, 0x024E, 0x0608 } },
{ EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, 0x024E } },
{ { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, 0x0305 },
{ EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, 0x0175, 0x0564 } },
{ EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, 0x0175 } },
{ { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, 0x0417 },
{ EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, 0x0423, 0x060C } },
{ EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, 0x0423 } },
{ { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, 0x008D },
{ EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, 0x02F5, 0x0610 } },
{ EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, 0x02F5 } },
{ { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, 0x0413 },
{ EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, 0x02B2, 0x0580 } },
{ EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, 0x02B2 } },
{ { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, 0x0457 }, NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, 0x047A },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, 0x010E }, NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, 0x0608 }, NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, 0x0564 }, NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, 0x060C }, NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, 0x0610 }, NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, 0x0580 },
NO_RETURN_ENTRANCE },
};
std::map<std::string, PriorityEntrance> priorityEntranceTable = {
@ -1262,6 +1269,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
entrance->GetConnectedRegionKey() == RR_DEKU_TREE_BOSS_ROOM;
});
}
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
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);
@ -1273,6 +1285,14 @@ int EntranceShuffler::ShuffleAllEntrances() {
entrance->GetConnectedRegionKey() == RR_DEKU_TREE_BOSS_ROOM;
});
}
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
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());
}
}
}
}
@ -1346,11 +1366,13 @@ int EntranceShuffler::ShuffleAllEntrances() {
// combine entrance pools if mixing pools. Only continue if more than one pool is selected.
int totalMixedPools =
(ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) ? 1 : 0) +
(ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES) ? 1 : 0);
(ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) ? 1 : 0) +
(ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES) ? 1 : 0) +
(ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES) ? 1 : 0);
if (totalMixedPools < 2) {
ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS).SetSelectedIndex(RO_GENERIC_OFF);
ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
ctx->GetOption(RSK_MIX_BOSS_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
@ -1364,6 +1386,12 @@ int EntranceShuffler::ShuffleAllEntrances() {
poolsToMix.insert(EntranceType::DungeonReverse);
}
}
if (ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES)) {
poolsToMix.insert(EntranceType::Boss);
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
poolsToMix.insert(EntranceType::BossReverse);
}
}
if (ctx->GetOption(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) {
poolsToMix.insert(EntranceType::Overworld);
}
@ -1503,6 +1531,94 @@ int EntranceShuffler::ShuffleAllEntrances() {
}
}
// Determine blue warp targets
// RANDOTODO: add bluewarp shuffle
if (true /* ctx->GetOption(RSK_SHUFFLE_BLUEWARP_ENTRANCES).Is(RO_BLUEWARP_ENTRANCE_SHUFFLE_DUNGEON) */) {
// 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(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE)) },
{ EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL)) },
{ EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN)) },
{ EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW)) },
{ EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL)) },
{ EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA)) },
{ EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY)) },
{ EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_ENTRYWAY, RR_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(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE),
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)) },
{ EntranceNameByRegions(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL),
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)) },
{ EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN),
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)) },
{ EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW),
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)) },
{ EntranceNameByRegions(RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL),
GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)) },
{ EntranceNameByRegions(RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA),
GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA)) },
{ EntranceNameByRegions(RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS)) },
{ EntranceNameByRegions(RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION),
GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION)) },
};
// Pair <BlueWarp exit, BossRoom reverse exit>
std::vector<EntrancePair> bossRoomExitPairs = {
{ GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)),
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)),
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)),
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)),
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)),
GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA)),
GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS)),
GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION)),
GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY)) },
};
for (EntrancePair pair : bossRoomExitPairs) {
Entrance* target = pair.second->GetReplacement() != nullptr ? pair.second->GetReplacement() : pair.second;
if (!ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
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;
}
return ENTRANCE_SHUFFLE_SUCCESS;
}
@ -1518,16 +1634,21 @@ void EntranceShuffler::CreateEntranceOverrides() {
int i = 0;
for (Entrance* entrance : allShuffleableEntrances) {
// Include blue warps when dungeons or bosses are shuffled
bool includeBluewarps =
entrance->GetType() == Rando::EntranceType::BlueWarp &&
(ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES) || ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES));
// Double-check to make sure the entrance is actually shuffled
if (!entrance->IsShuffled()) {
if (!entrance->IsShuffled() && !includeBluewarps) {
continue;
}
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;
@ -1541,9 +1662,9 @@ void EntranceShuffler::CreateEntranceOverrides() {
}
entranceOverrides[i] = {
.type = type,
.index = originalIndex,
.destination = destinationIndex,
.blueWarp = originalBlueWarp,
.override = replacementIndex,
.overrideDestination = replacementDestinationIndex,
};
@ -1559,9 +1680,9 @@ void EntranceShuffler::CreateEntranceOverrides() {
/// @brief set all the entrances to be 0 to indicate an unshuffled entrance
void EntranceShuffler::UnshuffleAllEntrances() {
for (auto& entranceOveride : entranceOverrides) {
entranceOveride.type = 0;
entranceOveride.index = 0;
entranceOveride.destination = 0;
entranceOveride.blueWarp = 0;
entranceOveride.override = 0;
entranceOveride.overrideDestination = 0;
}
@ -1575,12 +1696,12 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) {
for (auto it = entrancesJson.begin(); it != entrancesJson.end(); ++it, i++) {
nlohmann::json entranceJson = *it;
for (auto entranceIt = entranceJson.begin(); entranceIt != entranceJson.end(); ++entranceIt) {
if (entranceIt.key() == "index") {
if (entranceIt.key() == "type") {
entranceOverrides[i].type = entranceIt.value();
} else if (entranceIt.key() == "index") {
entranceOverrides[i].index = entranceIt.value();
} else if (entranceIt.key() == "destination") {
entranceOverrides[i].destination = entranceIt.value();
} else if (entranceIt.key() == "blueWarp") {
entranceOverrides[i].blueWarp = entranceIt.value();
} else if (entranceIt.key() == "override") {
entranceOverrides[i].override = entranceIt.value();
} else if (entranceIt.key() == "overrideDestination") {
@ -1593,5 +1714,5 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) {
} // namespace Rando
extern "C" EntranceOverride* Randomizer_GetEntranceOverrides() {
return Rando::Context::GetInstance()->GetEntranceShuffler()->entranceOverrides.data();
return Rando::Context::GetInstance()->GetEntranceShuffler()->entranceOverrides.data();
}

View File

@ -15,12 +15,16 @@ enum class EntranceType {
OwlDrop,
Spawn,
WarpSong,
BlueWarp,
Dungeon,
GanonDungeon,
DungeonReverse,
Boss,
BossReverse,
ChildBoss,
ChildBossReverse,
AdultBoss,
AdultBossReverse,
Interior,
InteriorReverse,
SpecialInterior,
@ -45,6 +49,7 @@ class Entrance {
uint32_t Getuint32_t() const;
bool CheckConditionAtAgeTime(bool& age, bool& time, bool passAnyway = false) const;
RandomizerRegion GetConnectedRegionKey() const;
RandomizerRegion GetOriginalConnectedRegionKey() const;
Area* GetConnectedRegion() const;
void SetParentRegion(RandomizerRegion newParent);
RandomizerRegion GetParentRegionKey() const;
@ -61,8 +66,6 @@ class Entrance {
void SetDecoupled();
int16_t GetIndex() const;
void SetIndex(int16_t newIndex);
int16_t GetBlueWarp() const;
void SetBlueWarp(int16_t newBlueWarp);
Entrance* GetAssumed() const;
void SetReplacement(Entrance* newReplacement);
Entrance* GetReplacement() const;
@ -78,6 +81,7 @@ class Entrance {
private:
RandomizerRegion parentRegion;
RandomizerRegion connectedRegion;
RandomizerRegion originalConnectedRegion;
std::vector<ConditionFn> conditions_met;
EntranceType type = EntranceType::None;
@ -86,7 +90,6 @@ class Entrance {
Entrance* assumed = nullptr;
Entrance* replacement = nullptr;
int16_t index = 0xFFFF;
int16_t blueWarp = 0;
bool shuffled = false;
bool primary = false;
bool addedToPool = false;
@ -99,7 +102,6 @@ typedef struct {
RandomizerRegion parentRegion;
RandomizerRegion connectedRegion;
int16_t index;
int16_t blueWarp;
} EntranceLinkInfo;
typedef struct {

View File

@ -170,12 +170,14 @@ void Settings::CreateOptionDescriptions() {
"This also adds the one-way entrance from Gerudo Valley to Lake Hylia in the pool of "
"overworld entrances when they are shuffled.";
mOptionDescriptions[RSK_MIXED_ENTRANCE_POOLS] =
"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 "
"vice versa, while overworld entrances are shuffled in their own separate pool and indoors stay vanilla.";
mOptionDescriptions[RSK_MIX_DUNGEON_ENTRANCES] = "Dungeon entrances will be part of the mixed pool";
mOptionDescriptions[RSK_MIX_BOSS_ENTRANCES] = "Boss entrances will be part of the mixed pool";
mOptionDescriptions[RSK_MIX_OVERWORLD_ENTRANCES] = "Overworld entrances will be part of the mixed pool";
mOptionDescriptions[RSK_MIX_INTERIOR_ENTRANCES] = "Interior entrances will be part of the mixed pool";
mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES] = "Grotto entrances will be part of the mixed pool";

View File

@ -236,23 +236,6 @@ std::unordered_map<s16, s16> getItemIdToItemId = {
{ GI_CLAIM_CHECK, ITEM_CLAIM_CHECK }
};
std::string sanitize(std::string stringValue) {
// Add backslashes.
for (auto i = stringValue.begin();;) {
auto const pos = std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
if (pos == stringValue.end()) {
break;
}
i = std::next(stringValue.insert(pos, '\\'), 2);
}
// Removes others.
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(), [](char const c) {
return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }), stringValue.end());
return stringValue;
}
#pragma optimize("", off)
#pragma GCC push_options
#pragma GCC optimize ("O0")

View File

@ -68,6 +68,7 @@ void Settings::CreateOptions() {
mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS] = Option::Bool("Overworld Spawns", "gRandomizeShuffleOverworldSpanws", mOptionDescriptions[RSK_SHUFFLE_OVERWORLD_SPAWNS]);
mOptions[RSK_MIXED_ENTRANCE_POOLS] = Option::Bool("Mixed Entrance Pools", "gRandomizeMixedEntrances", mOptionDescriptions[RSK_MIXED_ENTRANCE_POOLS]);
mOptions[RSK_MIX_DUNGEON_ENTRANCES] = Option::Bool("Mix Dungeons", "gRandomizeMixDungeons", mOptionDescriptions[RSK_MIX_DUNGEON_ENTRANCES], IMFLAG_NONE);
mOptions[RSK_MIX_BOSS_ENTRANCES] = Option::Bool("Mix Bosses", "gRandomizeMixBosses", mOptionDescriptions[RSK_MIX_BOSS_ENTRANCES], IMFLAG_NONE);
mOptions[RSK_MIX_OVERWORLD_ENTRANCES] = Option::Bool("Mix Overworld", "gRandomizeMixOverworld", mOptionDescriptions[RSK_MIX_OVERWORLD_ENTRANCES], IMFLAG_NONE);
mOptions[RSK_MIX_INTERIOR_ENTRANCES] = Option::Bool("Mix Interiors", "gRandomizeMixInteriors", mOptionDescriptions[RSK_MIX_INTERIOR_ENTRANCES], IMFLAG_NONE);
mOptions[RSK_MIX_GROTTO_ENTRANCES] = Option::Bool("Mix Grottos", "gRandomizeMixGrottos", mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES]);
@ -638,6 +639,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_DECOUPLED_ENTRANCES],
&mOptions[RSK_MIXED_ENTRANCE_POOLS],
&mOptions[RSK_MIX_DUNGEON_ENTRANCES],
&mOptions[RSK_MIX_BOSS_ENTRANCES],
&mOptions[RSK_MIX_OVERWORLD_ENTRANCES],
&mOptions[RSK_MIX_INTERIOR_ENTRANCES],
&mOptions[RSK_MIX_GROTTO_ENTRANCES]
@ -828,6 +830,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS],
&mOptions[RSK_MIXED_ENTRANCE_POOLS],
&mOptions[RSK_MIX_DUNGEON_ENTRANCES],
&mOptions[RSK_MIX_BOSS_ENTRANCES],
&mOptions[RSK_MIX_OVERWORLD_ENTRANCES],
&mOptions[RSK_MIX_INTERIOR_ENTRANCES],
&mOptions[RSK_MIX_GROTTO_ENTRANCES],
@ -1159,6 +1162,7 @@ void Settings::CreateOptions() {
{ "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 },
@ -1534,12 +1538,14 @@ void Settings::UpdateOptionProperties() {
if (CVarGetInteger("gRandomizeMixedEntrances", RO_GENERIC_OFF)) {
mOptions[RSK_MIXED_ENTRANCE_POOLS].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM);
mOptions[RSK_MIX_DUNGEON_ENTRANCES].Unhide();
mOptions[RSK_MIX_BOSS_ENTRANCES].Unhide();
mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Unhide();
mOptions[RSK_MIX_INTERIOR_ENTRANCES].Unhide();
mOptions[RSK_MIX_GROTTO_ENTRANCES].Unhide();
} else {
mOptions[RSK_MIXED_ENTRANCE_POOLS].AddFlag(IMFLAG_SEPARATOR_BOTTOM);
mOptions[RSK_MIX_DUNGEON_ENTRANCES].Hide();
mOptions[RSK_MIX_BOSS_ENTRANCES].Hide();
mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Hide();
mOptions[RSK_MIX_INTERIOR_ENTRANCES].Hide();
mOptions[RSK_MIX_GROTTO_ENTRANCES].Hide();
@ -2309,6 +2315,7 @@ void Settings::ParseJson(nlohmann::json spoilerFileJson) {
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:

View File

@ -357,9 +357,9 @@ void SaveManager::LoadRandomizerVersion3() {
auto entranceCtx = randoContext->GetEntranceShuffler();
SaveManager::Instance->LoadArray("entrances", ARRAY_COUNT(entranceCtx->entranceOverrides), [&](size_t i) {
SaveManager::Instance->LoadStruct("", [&]() {
SaveManager::Instance->LoadData("type", entranceCtx->entranceOverrides[i].type);
SaveManager::Instance->LoadData("index", entranceCtx->entranceOverrides[i].index);
SaveManager::Instance->LoadData("destination", entranceCtx->entranceOverrides[i].destination);
SaveManager::Instance->LoadData("blueWarp", entranceCtx->entranceOverrides[i].blueWarp);
SaveManager::Instance->LoadData("override", entranceCtx->entranceOverrides[i].override);
SaveManager::Instance->LoadData("overrideDestination",
entranceCtx->entranceOverrides[i].overrideDestination);