mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-25 19:02:19 -05:00
[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:
parent
3187564f5b
commit
13fce8258d
@ -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,23 +687,23 @@ 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},
|
||||
@ -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,
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -267,5 +267,6 @@ void AreaTable_Init_DekuTree() {
|
||||
{
|
||||
// Exits
|
||||
Entrance(DEKU_TREE_BOSS_ENTRYWAY, { [] { return true; } }),
|
||||
Entrance(KF_OUTSIDE_DEKU_TREE, { [] { return DekuTreeClear; } }),
|
||||
});
|
||||
}
|
||||
|
@ -309,5 +309,6 @@ void AreaTable_Init_DodongosCavern() {
|
||||
{
|
||||
// Exits
|
||||
Entrance(DODONGOS_CAVERN_BOSS_ENTRYWAY, { [] { return true; } }),
|
||||
Entrance(DEATH_MOUNTAIN_TRAIL, { [] { return DodongosCavernClear; } }),
|
||||
});
|
||||
}
|
||||
|
@ -420,5 +420,6 @@ void AreaTable_Init_FireTemple() {
|
||||
{
|
||||
// Exits
|
||||
Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
|
||||
Entrance(DMC_CENTRAL_LOCAL, { [] { return FireTempleClear; } }),
|
||||
});
|
||||
}
|
||||
|
@ -433,5 +433,6 @@ void AreaTable_Init_ForestTemple() {
|
||||
{
|
||||
// Exits
|
||||
Entrance(FOREST_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
|
||||
Entrance(SACRED_FOREST_MEADOW, { [] { return ForestTempleClear; } }),
|
||||
});
|
||||
}
|
||||
|
@ -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; } }),
|
||||
});
|
||||
}
|
||||
|
@ -209,5 +209,6 @@ void AreaTable_Init_ShadowTemple() {
|
||||
{
|
||||
// Exits
|
||||
Entrance(SHADOW_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
|
||||
Entrance(GRAVEYARD_WARP_PAD_REGION, { [] { return ShadowTempleClear; } }),
|
||||
});
|
||||
}
|
||||
|
@ -271,5 +271,6 @@ void AreaTable_Init_SpiritTemple() {
|
||||
{
|
||||
// Exits
|
||||
Entrance(SPIRIT_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
|
||||
Entrance(DESERT_COLOSSUS, { [] { return SpiritTempleClear; } }),
|
||||
});
|
||||
}
|
||||
|
@ -331,5 +331,6 @@ void AreaTable_Init_WaterTemple() {
|
||||
{
|
||||
// Exits
|
||||
Entrance(WATER_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
|
||||
Entrance(LAKE_HYLIA, { [] { return WaterTempleClear; } }),
|
||||
});
|
||||
}
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
// When decouple is off we track the replacement's reverse destination, useful for recording visited entrances
|
||||
if (!entrance->IsDecoupled()) {
|
||||
replacementDestinationIndex = entrance->GetReplacement()->GetReverse()->GetIndex();
|
||||
replacementBlueWarp = entrance->GetReplacement()->GetReverse()->GetBlueWarp();
|
||||
}
|
||||
}
|
||||
|
||||
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},
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
@ -62,39 +57,24 @@ typedef struct {
|
||||
|
||||
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 },
|
||||
{ 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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user