From 92cb7f159493e6d3afd3fdff3e8853b58cce4f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Tue, 10 Feb 2026 19:22:40 +0000 Subject: [PATCH] fix crash when generating decoupled entrances with boss shuffle (#6189) regression from ganon's tower shuffle --- .../Enhancements/randomizer/3drando/fill.cpp | 2 + .../randomizer/3drando/spoiler_log.cpp | 17 +-- soh/soh/Enhancements/randomizer/entrance.cpp | 116 +++++++----------- soh/soh/Enhancements/randomizer/entrance.h | 3 +- .../randomizer/location_access.cpp | 6 +- .../dungeons/ganons_castle.cpp | 10 +- .../randomizer/randomizer_entrance.c | 41 +++---- .../randomizer_entrance_tracker.cpp | 5 +- .../randomizer/randomizer_grotto.c | 3 - soh/src/code/z_play.c | 2 - 10 files changed, 83 insertions(+), 122 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index c961185df..522b37f34 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -211,6 +211,8 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer // Include bluewarps when unshuffled but dungeon or boss shuffle is on if ((exit.IsShuffled() || (exit.GetType() == Rando::EntranceType::BlueWarp && + (ctx->GetOption(RSK_SHUFFLE_GANONS_TOWER_ENTRANCE) || + exit.GetParentRegionKey() != RR_GANONS_TOWER_STAIRS_1) && (ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES) || ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES)))) && !exit.IsAddedToPool() && !ctx->GetEntranceShuffler()->HasNoRandomEntrances()) { gals.entranceSphere.push_back(&exit); diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp index c0e07b3c8..5e1b84ec2 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp @@ -8,6 +8,7 @@ #include "pool_functions.hpp" #include "soh/Enhancements/randomizer/randomizer_entrance_tracker.h" #include +#include #include #include @@ -223,12 +224,8 @@ static void WriteChosenOptions() { static void WritePlaythrough() { auto ctx = Rando::Context::GetInstance(); - for (uint32_t i = 0; i < ctx->playthroughLocations.size(); ++i) { - auto sphereNum = std::to_string(i); - std::string sphereString = "sphere "; - if (i < 10) - sphereString += "0"; - sphereString += sphereNum; + for (size_t i = 0; i < ctx->playthroughLocations.size(); i++) { + std::string sphereString = fmt::format("sphere {:0>2}", i); for (const RandomizerCheck key : ctx->playthroughLocations[i]) { if (!ctx->GetItemLocation(key)->IsHidden()) { WriteLocation(sphereString, key, true); @@ -240,12 +237,8 @@ static void WritePlaythrough() { // Write the randomized entrance playthrough to the spoiler log, if applicable static void WriteShuffledEntrances() { auto ctx = Rando::Context::GetInstance(); - for (uint32_t i = 0; i < ctx->GetEntranceShuffler()->playthroughEntrances.size(); ++i) { - auto sphereNum = std::to_string(i); - std::string sphereString = "sphere "; - if (i < 10) - sphereString += "0"; - sphereString += sphereNum; + for (size_t i = 0; i < ctx->GetEntranceShuffler()->playthroughEntrances.size(); i++) { + std::string sphereString = fmt::format("sphere {:0>2}", i); for (Entrance* entrance : ctx->GetEntranceShuffler()->playthroughEntrances[i]) { WriteShuffledEntrance(sphereString, entrance); } diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index f57c71eef..289d882df 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -589,14 +589,13 @@ void SetAllEntrancesData() { NO_RETURN_ENTRANCE }, { { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP }, NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_GANONS_TOWER_GANONDORF_LAIR, RR_GANONS_TOWER_STAIRS_1, ENTR_GANONS_TOWER_0 }, + { { EntranceType::BlueWarp, RR_GANONS_TOWER_STAIRS_1, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_OUTSIDE_GANONS_CASTLE_1_2 }, NO_RETURN_ENTRANCE }, // clang-format on }; auto ctx = Rando::Context::GetInstance(); for (auto& entrancePair : entranceShuffleTable) { - auto& forwardEntry = entrancePair.first; auto& returnEntry = entrancePair.second; @@ -695,7 +694,6 @@ std::vector EntranceShuffler::AssumeEntrancePool(std::vector& rollbacks) { - // Entrances shouldn't connect to their own scene, fail in this situation if ( // allow "special" areas to connect to eachother @@ -712,21 +710,19 @@ static bool AreEntrancesCompatible(Entrance* entrance, Entrance* target, std::ve target->GetConnectedRegion()->scene == SCENE_OUTSIDE_GANONS_CASTLE) || (entrance->GetParentRegion()->scene == SCENE_OUTSIDE_GANONS_CASTLE && target->GetConnectedRegion()->scene == SCENE_HYRULE_CASTLE))) { - auto message = "Entrance " + entrance->GetName() + " attempted to connect with own scene target " + - target->to_string() + ". Connection failed.\n"; - SPDLOG_DEBUG(message); + SPDLOG_DEBUG("Entrance {} attempted to connect with own scene target {}. Connection failed.", + entrance->GetName(), target->to_string()); return false; } // One way entrances shouldn't lead to the same scene as other already chosen one way entrances auto type = entrance->GetType(); - const std::vector oneWayTypes = { EntranceType::OwlDrop, EntranceType::Spawn, - EntranceType::WarpSong }; + const std::array oneWayTypes = { EntranceType::OwlDrop, EntranceType::Spawn, + EntranceType::WarpSong }; if (ElementInContainer(type, oneWayTypes)) { for (auto& rollback : rollbacks) { if (rollback.first->GetConnectedRegion()->scene == target->GetConnectedRegion()->scene) { - auto message = "A one way entrance already leads to " + target->to_string() + ". Connection failed.\n"; - SPDLOG_DEBUG(message); + SPDLOG_DEBUG("A one way entrance already leads to {}. Connection failed.", target->to_string()); return false; } } @@ -738,8 +734,7 @@ static bool AreEntrancesCompatible(Entrance* entrance, Entrance* target, std::ve // Change connections between an entrance and a target assumed entrance, in order to test the connections afterwards if // necessary static void ChangeConnections(Entrance* entrance, Entrance* targetEntrance) { - auto message = "Attempting to connect " + entrance->GetName() + " to " + targetEntrance->to_string() + "\n"; - SPDLOG_DEBUG(message); + SPDLOG_DEBUG("Attempting to connect {} to {}", entrance->GetName(), targetEntrance->to_string()); entrance->Connect(targetEntrance->Disconnect()); entrance->SetReplacement(targetEntrance->GetReplacement()); if (entrance->GetReverse() != nullptr && !entrance->IsDecoupled()) { @@ -791,7 +786,7 @@ static bool EntranceUnreachableAs(Entrance* entrance, uint8_t age, std::vectorHasAccess() && !RegionTable(RR_KAKARIKO_VILLAGE)->HasAccess()) { - SPDLOG_DEBUG("Invalid starting area\n"); + SPDLOG_DEBUG("Invalid starting area"); return false; } // Check that a region where time passes is always reachable as both ages without having collected any items if (!Regions::HasTimePassAccess(RO_AGE_CHILD) || !Regions::HasTimePassAccess(RO_AGE_ADULT)) { - SPDLOG_DEBUG("Time passing is not guaranteed as both ages\n"); + SPDLOG_DEBUG("Time passing is not guaranteed as both ages"); return false; } @@ -888,16 +879,16 @@ static bool ValidateWorld(Entrance* entrancePlaced) { // This is important to ensure that the player never loses access to the pedestal after going through time if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD) && !RegionTable(RR_TEMPLE_OF_TIME)->Adult()) { - SPDLOG_DEBUG("Path to Temple of Time as adult is not guaranteed\n"); + SPDLOG_DEBUG("Path to Temple of Time as adult is not guaranteed"); return false; } else if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT) && !RegionTable(RR_TEMPLE_OF_TIME)->Child()) { - SPDLOG_DEBUG("Path to Temple of Time as child is not guaranteed\n"); + SPDLOG_DEBUG("Path to Temple of Time as child is not guaranteed"); return false; } } - SPDLOG_DEBUG("All Locations NOT REACHABLE\n"); + SPDLOG_DEBUG("All Locations NOT REACHABLE"); return false; } return true; @@ -933,7 +924,6 @@ static void ConfirmReplacement(Entrance* entrance, Entrance* targetEntrance) { } bool EntranceShuffler::ReplaceEntrance(Entrance* entrance, Entrance* target, std::vector& rollbacks) { - if (!AreEntrancesCompatible(entrance, target, rollbacks)) { return false; } @@ -941,8 +931,7 @@ bool EntranceShuffler::ReplaceEntrance(Entrance* entrance, Entrance* target, std if (ValidateWorld(entrance)) { #ifdef ENABLE_DEBUG std::string ticks = std::to_string(svcGetSystemTick()); - auto message = "Dumping World Graph at " + ticks + "\n"; - // SPDLOG_DEBUG(message); + // SPDLOG_DEBUG("Dumping World Graph at {}", ticks); // Regions::DumpWorldGraph(ticks); #endif rollbacks.push_back(EntrancePair{ entrance, target }); @@ -951,8 +940,7 @@ bool EntranceShuffler::ReplaceEntrance(Entrance* entrance, Entrance* target, std } else { #ifdef ENABLE_DEBUG std::string ticks = std::to_string(svcGetSystemTick()); - auto message = "Dumping World Graph at " + ticks + "\n"; - // SPDLOG_DEBUG(message); + // SPDLOG_DEBUG("Dumping World Graph at {}", ticks); // Regions::DumpWorldGraph(ticks); #endif if (entrance->GetConnectedRegionKey() != RR_NONE) { @@ -1006,7 +994,7 @@ bool EntranceShuffler::PlaceOneWayPriorityEntrance( } } } - SPDLOG_DEBUG("ERROR: Unable to place priority one-way entrance for " + priorityName + "\n"); + SPDLOG_DEBUG("ERROR: Unable to place priority one-way entrance for {}", priorityName); assert(false); return false; } @@ -1044,7 +1032,7 @@ bool EntranceShuffler::ShuffleOneWayPriorityEntrances(std::map& entrancePool, if (retries != retryCount) { #ifdef ENABLE_DEBUG std::string ticks = std::to_string(svcGetSystemTick()); - auto message = "Failed to connect entrances. Retrying " + std::to_string(retries) + - " more times.\nDumping World Graph at " + ticks + "\n"; - SPDLOG_DEBUG(message); + SPDLOG_DEBUG("Failed to connect entrances. Retrying {} more times.", retries); + SPDLOG_DEBUG("Dumping World Graph at {}", ticks); // Regions::DumpWorldGraph(ticks); #endif } @@ -1248,23 +1235,23 @@ int EntranceShuffler::ShuffleAllEntrances() { if (ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_FULL)) { entrancePools[EntranceType::Boss] = GetShuffleableEntrances(EntranceType::ChildBoss); AddElementsToPool(entrancePools[EntranceType::Boss], GetShuffleableEntrances(EntranceType::AdultBoss)); + if (ctx->GetOption(RSK_SHUFFLE_GANONS_TOWER_ENTRANCE)) { + AddElementsToPool(entrancePools[EntranceType::Boss], GetShuffleableEntrances(EntranceType::GanonTower)); + } + if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { for (Entrance* entrance : entrancePools[EntranceType::Boss]) { entrancePools[EntranceType::BossReverse].push_back(entrance->GetReverse()); } } - - if (ctx->GetOption(RSK_SHUFFLE_GANONS_TOWER_ENTRANCE).IsNot(RO_GENERIC_OFF)) { - AddElementsToPool(entrancePools[EntranceType::Boss], GetShuffleableEntrances(EntranceType::GanonTower)); - if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { - for (Entrance* entrance : GetShuffleableEntrances(EntranceType::GanonTower)) { - entrancePools[EntranceType::BossReverse].push_back(entrance->GetReverse()); - } - } - } } else { entrancePools[EntranceType::ChildBoss] = GetShuffleableEntrances(EntranceType::ChildBoss); entrancePools[EntranceType::AdultBoss] = GetShuffleableEntrances(EntranceType::AdultBoss); + if (ctx->GetOption(RSK_SHUFFLE_GANONS_TOWER_ENTRANCE)) { + AddElementsToPool(entrancePools[EntranceType::AdultBoss], + GetShuffleableEntrances(EntranceType::GanonTower)); + } + if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { for (Entrance* entrance : entrancePools[EntranceType::ChildBoss]) { entrancePools[EntranceType::ChildBossReverse].push_back(entrance->GetReverse()); @@ -1273,16 +1260,6 @@ int EntranceShuffler::ShuffleAllEntrances() { entrancePools[EntranceType::AdultBossReverse].push_back(entrance->GetReverse()); } } - - if (ctx->GetOption(RSK_SHUFFLE_GANONS_TOWER_ENTRANCE).IsNot(RO_GENERIC_OFF)) { - AddElementsToPool(entrancePools[EntranceType::AdultBoss], - GetShuffleableEntrances(EntranceType::GanonTower)); - if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { - for (Entrance* entrance : GetShuffleableEntrances(EntranceType::GanonTower)) { - entrancePools[EntranceType::AdultBossReverse].push_back(entrance->GetReverse()); - } - } - } } } @@ -1376,7 +1353,6 @@ int EntranceShuffler::ShuffleAllEntrances() { std::set poolsToMix = {}; if (ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES)) { poolsToMix.insert(EntranceType::Dungeon); - // Insert reverse entrances when decoupled entrances is on if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { poolsToMix.insert(EntranceType::DungeonReverse); } @@ -1553,8 +1529,8 @@ int EntranceShuffler::ShuffleAllEntrances() { GetEntrance(RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE) }, { EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY), GetEntrance(RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION) }, - { EntranceNameByRegions(RR_GANONS_TOWER_GANONDORF_LAIR, RR_GANONS_TOWER_BEFORE_GANONDORF_LAIR), - GetEntrance(RR_GANONS_TOWER_ENTRYWAY, RR_GANONS_TOWER_STAIRS_1) }, + { EntranceNameByRegions(RR_GANONS_TOWER_STAIRS_1, RR_GANONS_TOWER_ENTRYWAY), + GetEntrance(RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE) } }; // If a boss room is inside a dungeon entrance (or inside a dungeon which is inside a dungeon entrance), make @@ -1576,8 +1552,8 @@ int EntranceShuffler::ShuffleAllEntrances() { GetEntrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS) }, { EntranceNameByRegions(RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION), GetEntrance(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION) }, - { EntranceNameByRegions(RR_GANONS_TOWER_ENTRYWAY, RR_GANONS_TOWER_STAIRS_1), - GetEntrance(RR_GANONS_TOWER_GANONDORF_LAIR, RR_GANONS_TOWER_STAIRS_1) }, + { EntranceNameByRegions(RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE), + GetEntrance(RR_GANONS_TOWER_STAIRS_1, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE) } }; // Pair @@ -1598,8 +1574,8 @@ int EntranceShuffler::ShuffleAllEntrances() { GetEntrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY) }, { GetEntrance(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION), GetEntrance(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY) }, - { GetEntrance(RR_GANONS_TOWER_GANONDORF_LAIR, RR_GANONS_TOWER_STAIRS_1), - GetEntrance(RR_GANONS_TOWER_GANONDORF_LAIR, RR_GANONS_TOWER_BEFORE_GANONDORF_LAIR) }, + { GetEntrance(RR_GANONS_TOWER_STAIRS_1, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE), + GetEntrance(RR_GANONS_TOWER_STAIRS_1, RR_GANONS_TOWER_ENTRYWAY) } }; for (EntrancePair pair : bossRoomExitPairs) { @@ -1635,7 +1611,7 @@ void EntranceShuffler::CreateEntranceOverrides() { if (mNoRandomEntrances) { return; } - SPDLOG_DEBUG("\nCREATING ENTRANCE OVERRIDES\n"); + SPDLOG_DEBUG("CREATING ENTRANCE OVERRIDES"); auto allShuffleableEntrances = GetShuffleableEntrances(EntranceType::All, false); int i = 0; @@ -1644,6 +1620,8 @@ void EntranceShuffler::CreateEntranceOverrides() { // Include blue warps when dungeons or bosses are shuffled bool includeBluewarps = entrance->GetType() == Rando::EntranceType::BlueWarp && + (ctx->GetOption(RSK_SHUFFLE_GANONS_TOWER_ENTRANCE) || + entrance->GetParentRegionKey() != RR_GANONS_TOWER_STAIRS_1) && (ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES) || ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES)); // Double-check to make sure the entrance is actually shuffled @@ -1651,8 +1629,7 @@ void EntranceShuffler::CreateEntranceOverrides() { continue; } - auto message = "Setting " + entrance->to_string() + "\n"; - SPDLOG_DEBUG(message); + SPDLOG_DEBUG("Setting {}", entrance->to_string()); uint8_t type = (uint8_t)entrance->GetType(); int16_t originalIndex = entrance->GetIndex(); @@ -1661,8 +1638,7 @@ void EntranceShuffler::CreateEntranceOverrides() { int16_t destinationIndex = -1; int16_t replacementDestinationIndex = -1; - // Only set destination indices for two way entrances and when decouple entrances - // is off + // Only set destination indices for two way entrances and when decouple entrances is off if (entrance->GetReverse() != nullptr && !ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { replacementDestinationIndex = entrance->GetReplacement()->GetReverse()->GetIndex(); destinationIndex = entrance->GetReverse()->GetIndex(); @@ -1676,10 +1652,8 @@ void EntranceShuffler::CreateEntranceOverrides() { .overrideDestination = replacementDestinationIndex, }; - message = "\tOriginal: " + std::to_string(originalIndex) + "\n"; - SPDLOG_DEBUG(message); - message = "\tReplacement " + std::to_string(replacementIndex) + "\n"; - SPDLOG_DEBUG(message); + SPDLOG_DEBUG("\tOriginal {}", originalIndex); + SPDLOG_DEBUG("\tReplacement {}", replacementIndex); i++; } } diff --git a/soh/soh/Enhancements/randomizer/entrance.h b/soh/soh/Enhancements/randomizer/entrance.h index 06481079e..32fefbb13 100644 --- a/soh/soh/Enhancements/randomizer/entrance.h +++ b/soh/soh/Enhancements/randomizer/entrance.h @@ -95,7 +95,6 @@ class Entrance { ConditionFn condition_function; EntranceType type = EntranceType::None; - Entrance* target = nullptr; Entrance* reverse = nullptr; Entrance* assumed = nullptr; Entrance* replacement = nullptr; @@ -105,10 +104,10 @@ class Entrance { bool addedToPool = false; bool decoupled = false; std::string name = ""; + std::string condition_str = ""; // If this is false, areas only spread to interiors through this entrance if there is no other choice // Set to false for owl drops, the windmill path between dampe's grave and windmill and blue warps bool spreadsAreasWithPriority = true; - std::string condition_str = ""; }; typedef struct { diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index d39e94f31..72338173b 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -1188,10 +1188,8 @@ void DumpWorldGraph(std::string str) { } // namespace Regions Region* RegionTable(const RandomizerRegion regionKey) { - if (regionKey > RR_MAX) { - printf("\x1b[1;1HERROR: AREAKEY TOO BIG"); - } - return &(areaTable[regionKey]); + assert(regionKey < RR_MAX); + return &areaTable[regionKey]; } // Retrieve all the shuffable entrances of a specific type diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp index 63bc50d91..75be53352 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp @@ -678,6 +678,8 @@ void RegionTable_Init_GanonsCastle() { //Exits ENTRANCE(RR_GANONS_TOWER_ENTRYWAY, true), ENTRANCE(RR_GANONS_TOWER_FLOOR_1, true), + // for imaginary blue warp + ENTRANCE(RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, false), }); areaTable[RR_GANONS_TOWER_FLOOR_1] = Region("Ganon's Tower Floor 1", SCENE_GANONS_TOWER, {}, {}, { @@ -686,7 +688,7 @@ void RegionTable_Init_GanonsCastle() { ENTRANCE(RR_GANONS_TOWER_STAIRS_2, AnyAgeTime([]{return logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2);})), }); - areaTable[RR_GANONS_TOWER_STAIRS_2] = Region("Ganon's Tower Stairs 1", SCENE_GANONS_TOWER, {}, {}, { + areaTable[RR_GANONS_TOWER_STAIRS_2] = Region("Ganon's Tower Stairs 2", SCENE_GANONS_TOWER, {}, {}, { //Exits ENTRANCE(RR_GANONS_TOWER_FLOOR_1, true), ENTRANCE(RR_GANONS_TOWER_FLOOR_2, true), @@ -701,7 +703,7 @@ void RegionTable_Init_GanonsCastle() { ENTRANCE(RR_GANONS_TOWER_STAIRS_3, AnyAgeTime([]{return logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2);})), }); - areaTable[RR_GANONS_TOWER_STAIRS_3] = Region("Ganon's Tower Stairs 1", SCENE_GANONS_TOWER, {}, {}, { + areaTable[RR_GANONS_TOWER_STAIRS_3] = Region("Ganon's Tower Stairs 3", SCENE_GANONS_TOWER, {}, {}, { //Exits ENTRANCE(RR_GANONS_TOWER_FLOOR_2, true), ENTRANCE(RR_GANONS_TOWER_FLOOR_3, true), @@ -713,7 +715,7 @@ void RegionTable_Init_GanonsCastle() { ENTRANCE(RR_GANONS_TOWER_STAIRS_4, AnyAgeTime([]{return logic->CanKillEnemy(RE_IRON_KNUCKLE, ED_CLOSE, true, 2);})), }); - areaTable[RR_GANONS_TOWER_STAIRS_4] = Region("Ganon's Tower Stairs 1", SCENE_GANONS_TOWER, {}, {}, { + areaTable[RR_GANONS_TOWER_STAIRS_4] = Region("Ganon's Tower Stairs 4", SCENE_GANONS_TOWER, {}, {}, { //Exits ENTRANCE(RR_GANONS_TOWER_FLOOR_3, true), ENTRANCE(RR_GANONS_TOWER_BEFORE_GANONDORF_LAIR, true), @@ -751,8 +753,6 @@ void RegionTable_Init_GanonsCastle() { }, { //Exits ENTRANCE(RR_GANONS_CASTLE_ESCAPE, logic->CanKillEnemy(RE_GANONDORF)), - ENTRANCE(RR_GANONS_TOWER_BEFORE_GANONDORF_LAIR, false), - ENTRANCE(RR_GANONS_TOWER_STAIRS_1, false), }); areaTable[RR_GANONS_CASTLE_ESCAPE] = Region("Ganon's Castle Escape", SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR, {}, { diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c index edb3a0e7b..010e40a23 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -59,23 +59,21 @@ typedef struct { s16 exit; s16 bossDoor; s16 bossDoorReverse; - s16 blueWarp; s16 scene; s16 bossScene; } DungeonEntranceInfo; static DungeonEntranceInfo dungeons[] = { // clang-format off - //entryway exit, boss, reverse, bluewarp, dungeon scene, boss scene - { ENTR_DEKU_TREE_ENTRANCE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE, ENTR_DEKU_TREE_BOSS_ENTRANCE, ENTR_DEKU_TREE_BOSS_DOOR, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS }, - { ENTR_DODONGOS_CAVERN_ENTRANCE, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE, ENTR_DODONGOS_CAVERN_BOSS_DOOR, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS }, - { ENTR_JABU_JABU_ENTRANCE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU, ENTR_JABU_JABU_BOSS_ENTRANCE, ENTR_JABU_JABU_BOSS_DOOR, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS }, - { ENTR_FOREST_TEMPLE_ENTRANCE, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE, ENTR_FOREST_TEMPLE_BOSS_DOOR, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS }, - { ENTR_FIRE_TEMPLE_ENTRANCE, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE, ENTR_FIRE_TEMPLE_BOSS_DOOR, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS }, - { ENTR_WATER_TEMPLE_ENTRANCE, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE, ENTR_WATER_TEMPLE_BOSS_ENTRANCE, ENTR_WATER_TEMPLE_BOSS_DOOR, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS }, - { ENTR_SPIRIT_TEMPLE_ENTRANCE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE, ENTR_SPIRIT_TEMPLE_BOSS_DOOR, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS }, - { ENTR_SHADOW_TEMPLE_ENTRANCE, ENTR_GRAVEYARD_OUTSIDE_TEMPLE, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE, ENTR_SHADOW_TEMPLE_BOSS_DOOR, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS }, - + //entryway exit, boss, reverse, dungeon scene, boss scene + { ENTR_DEKU_TREE_ENTRANCE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE, ENTR_DEKU_TREE_BOSS_ENTRANCE, ENTR_DEKU_TREE_BOSS_DOOR, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS }, + { ENTR_DODONGOS_CAVERN_ENTRANCE, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE, ENTR_DODONGOS_CAVERN_BOSS_DOOR, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS }, + { ENTR_JABU_JABU_ENTRANCE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU, ENTR_JABU_JABU_BOSS_ENTRANCE, ENTR_JABU_JABU_BOSS_DOOR, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS }, + { ENTR_FOREST_TEMPLE_ENTRANCE, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE, ENTR_FOREST_TEMPLE_BOSS_DOOR, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS }, + { ENTR_FIRE_TEMPLE_ENTRANCE, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE, ENTR_FIRE_TEMPLE_BOSS_DOOR, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS }, + { ENTR_WATER_TEMPLE_ENTRANCE, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE, ENTR_WATER_TEMPLE_BOSS_ENTRANCE, ENTR_WATER_TEMPLE_BOSS_DOOR, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS }, + { ENTR_SPIRIT_TEMPLE_ENTRANCE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE, ENTR_SPIRIT_TEMPLE_BOSS_DOOR, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS }, + { ENTR_SHADOW_TEMPLE_ENTRANCE, ENTR_GRAVEYARD_OUTSIDE_TEMPLE, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE, ENTR_SHADOW_TEMPLE_BOSS_DOOR, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS }, // clang-format on }; @@ -182,7 +180,6 @@ void Entrance_Init(void) { // Then overwrite the indices which are shuffled for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) { - if (Entrance_EntranceIsNull(&entranceOverrides[i])) { break; } @@ -265,7 +262,6 @@ void Entrance_Init(void) { } s16 Entrance_GetOverride(s16 index) { - // The game sometimes uses special indices from 0x7FF9 -> 0x7FFF for exiting // grottos and fairy fountains. These aren't handled here since the game // naturally handles them later. @@ -273,6 +269,11 @@ s16 Entrance_GetOverride(s16 index) { return index; } + // special index for fake ganon's castle blue warp + if (entranceOverrideTable[index] == ENTR_OUTSIDE_GANONS_CASTLE_1_2) { + return ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT; + } + return entranceOverrideTable[index]; } @@ -325,7 +326,6 @@ u32 Entrance_SceneAndSpawnAre(u8 scene, u8 spawn) { // Properly respawn the player after a game over, accounting for dungeon entrance randomizer void Entrance_SetGameOverEntrance(void) { - s16 scene = gPlayState->sceneNum; // When in a boss room and boss shuffle is on, use the boss scene to find the death warp entrance @@ -363,15 +363,14 @@ void Entrance_SetGameOverEntrance(void) { case ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE: // Shadow Temple Boss Room gSaveContext.entranceIndex = ENTR_SHADOW_TEMPLE_ENTRANCE; return; - case ENTR_GANONDORF_BOSS_0: // Ganondorf Boss Room - gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb + case ENTR_GANONDORF_BOSS_0: // Ganondorf Boss Room + gSaveContext.entranceIndex = ENTR_INSIDE_GANONS_CASTLE_ENTRANCE; return; } } // Properly savewarp the player accounting for dungeon entrance randomizer. void Entrance_SetSavewarpEntrance(void) { - s16 scene = gSaveContext.savedSceneNum; // When in a boss room and boss shuffle is on, use the boss scene to find the savewarp entrance @@ -723,14 +722,14 @@ void Entrance_OverrideSpawnScene(s32 sceneNum, s32 spawn) { } if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) { - // Repair the authentically bugged entrance when leaving Barniades boss room -> JabuJabu's belly + // Repair the authentically bugged entrance when leaving Barinade's boss room -> Jabu Jabu's belly // Link's position needs to be adjusted to prevent him from falling through the floor if (sceneNum == SCENE_JABU_JABU && spawn == 1) { modifiedLinkActorEntry.pos.z = 0xF7F4; gPlayState->linkActorEntry = &modifiedLinkActorEntry; } - // Repair the authentically bugged entrance when leaving Morpha's boass room -> Water Temple + // Repair the authentically bugged entrance when leaving Morpha's boss room -> Water Temple // Link's position was at the start of the Water Temple entrance // This updates it to place him in the hallway outside of Morpha's boss room. if (sceneNum == SCENE_WATER_TEMPLE && spawn == 1) { @@ -754,13 +753,13 @@ void Entrance_OverrideSpawnScene(s32 sceneNum, s32 spawn) { s32 Entrance_OverrideSpawnSceneRoom(s32 sceneNum, s32 spawn, s32 roomNum) { if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) { - // Repair the authentically bugged scene/spawn info for leaving Barinade's boss room -> JabuJabu's belly + // Repair the authentically bugged scene/spawn info for leaving Barinade's boss room -> Jabu Jabu's belly // to load the correct room outside Barniade's boss room if (sceneNum == SCENE_JABU_JABU && spawn == 1) { return 5; } - // Repair the authentically bugged scene/spawn info for leaving Morhpa's boss room -> Water Temple + // Repair the authentically bugged scene/spawn info for leaving Morpha's boss room -> Water Temple // to load the correct room for the hallway before Morpha's boss room if (sceneNum == SCENE_WATER_TEMPLE && spawn == 1) { return 11; diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp index 460b2831a..95cb71c2e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp @@ -414,8 +414,9 @@ const EntranceData entranceData[] = { { ENTR_INSIDE_GANONS_CASTLE_ENTRANCE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT, SINGLE_SCENE_INFO(SCENE_OUTSIDE_GANONS_CASTLE), "OGC Rainbow Bridge Exit", "Inside Ganon's Castle Entrance", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "outside ganon's castle,gc", 1}, { ENTR_POTION_SHOP_KAKARIKO_1, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD, {{ SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC, 0x02 }}, "OGC Great Fairy Fountain", "OGC Behind Pillar", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "outside ganon's castle"}, { ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE, SINGLE_SCENE_INFO(SCENE_INSIDE_GANONS_CASTLE), "Inside Ganon's Castle Entrance", "OGC Rainbow Bridge Exit", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "outside ganon's castle,gc"}, - { ENTR_INSIDE_GANONS_CASTLE_1, ENTR_GANONS_TOWER_0, SINGLE_SCENE_INFO(SCENE_GANONS_TOWER), "Ganon's Tower Entrance", "Inside Ganon's Castle Past Trials", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "gc"}, - { ENTR_GANONS_TOWER_0, ENTR_INSIDE_GANONS_CASTLE_1, SINGLE_SCENE_INFO(SCENE_INSIDE_GANONS_CASTLE), "Inside Ganon's Castle Past Trials", "Ganon's Tower Entrance", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "gc"}, + { ENTR_INSIDE_GANONS_CASTLE_1, ENTR_GANONS_TOWER_0, SINGLE_SCENE_INFO(SCENE_GANONS_TOWER), "Ganon's Tower Entrance", "Inside Ganon's Castle", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "gc"}, + { ENTR_GANONS_TOWER_0, ENTR_INSIDE_GANONS_CASTLE_1, SINGLE_SCENE_INFO(SCENE_INSIDE_GANONS_CASTLE), "Inside Ganon's Castle", "Ganon's Tower Entrance", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "gc"}, + { ENTR_OUTSIDE_GANONS_CASTLE_1_2, -1, SINGLE_SCENE_INFO(SCENE_OUTSIDE_GANONS_CASTLE), "Ganon's Blue Warp", "Ganon's Castle Blue Warp", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_ONE_WAY, "gc,bw", 1}, // clang-format on }; diff --git a/soh/soh/Enhancements/randomizer/randomizer_grotto.c b/soh/soh/Enhancements/randomizer/randomizer_grotto.c index 96105728a..5fe087527 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_grotto.c +++ b/soh/soh/Enhancements/randomizer/randomizer_grotto.c @@ -186,7 +186,6 @@ s16 Grotto_GetEntranceValueHandlingGrottoRando(s16 nextEntranceIndex) { // Translates and overrides the passed in entrance index if it corresponds to a // special grotto entrance (grotto load or return point) and updates player respawn data correctly. s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) { - // Don't change anything unless grotto shuffle has been enabled if (!Randomizer_GetSettingValue(RSK_SHUFFLE_GROTTO_ENTRANCES) && !Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_SPAWNS) && @@ -233,7 +232,6 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) { lastEntranceType = GROTTO_RETURN; // Grotto Loads } else if (nextEntranceIndex >= ENTRANCE_GROTTO_LOAD_START && nextEntranceIndex < ENTRANCE_GROTTO_EXIT_START) { - // Set the respawn data to load the correct grotto GrottoLoadInfo grotto = grottoLoadTable[grottoId]; gSaveContext.respawn[RESPAWN_MODE_RETURN].data = grotto.content; @@ -255,7 +253,6 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) { // Override the entrance index when entering into a grotto actor // thisx - pointer to the grotto actor void Grotto_OverrideActorEntrance(Actor* thisx) { - // Vanilla Behavior if there's no possibility of ending up in a grotto randomly if (!Randomizer_GetSettingValue(RSK_SHUFFLE_GROTTO_ENTRANCES) && !Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_SPAWNS) && diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index 55c1864fb..6ecafd91c 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -5,8 +5,6 @@ #include "soh/Enhancements/gameconsole.h" #include "soh/frame_interpolation.h" -#include "soh/Enhancements/debugconsole.h" -#include "soh/Enhancements/game-interactor/GameInteractor.h" #include #include #include "soh/Enhancements/enhancementTypes.h"