summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMalkierian <malkierian@live.com>2025-06-12 17:21:23 -0700
committerMalkierian <malkierian@live.com>2025-06-12 17:46:47 -0700
commit2b1322d7232cd033152edef2b6f5656c913ef186 (patch)
treed126377a03b34e5f7b6fbb4eed9358534e2d4949
parentabb0a93945b1888f3c2d322d5c9890fb6d19d33d (diff)
parentc7e3e08f8df387c5545f06d247856de8446c4520 (diff)
Merge branch 'develop-blair' of github.com:Malkierian/Shipwright into develop
-rw-r--r--soh/soh/Enhancements/debugconsole.cpp60
-rw-r--r--soh/soh/Enhancements/enemyrandomizer.cpp74
-rw-r--r--soh/soh/Enhancements/enemyrandomizer.h10
-rw-r--r--soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h32
-rw-r--r--soh/soh/Enhancements/mods.cpp7
-rw-r--r--soh/soh/Enhancements/randomizer/3drando/fill.cpp45
-rw-r--r--soh/soh/Enhancements/randomizer/3drando/fill.hpp4
-rw-r--r--soh/soh/Enhancements/randomizer/ShuffleCrates.cpp2
-rw-r--r--soh/soh/Enhancements/randomizer/context.cpp39
-rw-r--r--soh/soh/Enhancements/randomizer/context.h10
-rw-r--r--soh/soh/Enhancements/randomizer/entrance.cpp664
-rw-r--r--soh/soh/Enhancements/randomizer/entrance.h1
-rw-r--r--soh/soh/Enhancements/randomizer/item.cpp14
-rw-r--r--soh/soh/Enhancements/randomizer/location_access.h1
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp11
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp16
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp8
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp8
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp23
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp2
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp2
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp10
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp6
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp12
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp12
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp2
-rw-r--r--soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp2
-rw-r--r--soh/soh/Enhancements/randomizer/logic.cpp70
-rw-r--r--soh/soh/Enhancements/randomizer/logic.h5
-rw-r--r--soh/soh/Enhancements/randomizer/randomizer.cpp18
-rw-r--r--soh/soh/Enhancements/randomizer/randomizerTypes.h4
-rw-r--r--soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp48
-rw-r--r--soh/soh/Enhancements/randomizer/randomizer_check_tracker.h2
-rw-r--r--soh/soh/Enhancements/randomizer/randomizer_entrance.c5
-rw-r--r--soh/soh/Enhancements/randomizer/savefile.cpp4
-rw-r--r--soh/soh/Enhancements/randomizer/settings.cpp8
-rw-r--r--soh/soh/Enhancements/randomizer/settings.h8
-rw-r--r--soh/soh/OTRGlobals.cpp12
-rw-r--r--soh/soh/OTRGlobals.h1
-rw-r--r--soh/soh/SaveManager.cpp286
-rw-r--r--soh/soh/SaveManager.h6
-rw-r--r--soh/soh/SohGui/Menu.cpp54
-rw-r--r--soh/soh/util.cpp11
-rw-r--r--soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c6
-rw-r--r--soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c2
-rw-r--r--soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c2
-rw-r--r--soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c2
-rw-r--r--soh/src/overlays/actors/ovl_player_actor/z_player.c20
-rw-r--r--soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c16
49 files changed, 902 insertions, 765 deletions
diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp
index db5cc824f..d25c3cfff 100644
--- a/soh/soh/Enhancements/debugconsole.cpp
+++ b/soh/soh/Enhancements/debugconsole.cpp
@@ -11,6 +11,7 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/cosmetics/CosmeticsEditor.h"
#include "soh/Enhancements/audio/AudioEditor.h"
+#include "soh/Enhancements/randomizer/logic.h"
#define Path _Path
#define PATH_HACK
@@ -1450,6 +1451,55 @@ static bool SfxHandler(std::shared_ptr<Ship::Console> Console, const std::vector
return 0;
}
+static bool AvailableChecksProcessUndiscoveredExitsHandler(std::shared_ptr<Ship::Console> Console,
+ const std::vector<std::string>& args, std::string* output) {
+ const auto& logic = Rando::Context::GetInstance()->GetLogic();
+ bool enabled = false;
+
+ if (args.size() == 1) {
+ enabled = !logic->ACProcessUndiscoveredExits;
+ } else {
+ try {
+ enabled = std::stoi(args[1]);
+ } catch (std::invalid_argument const& ex) {
+ ERROR_MESSAGE("[SOH] Enable should be 0 or 1");
+ return 1;
+ }
+ }
+
+ logic->ACProcessUndiscoveredExits = enabled;
+ INFO_MESSAGE("[SOH] Available Checks - Process Undiscovered Exits %s",
+ logic->ACProcessUndiscoveredExits ? "enabled" : "disabled");
+
+ if (GameInteractor::IsSaveLoaded(true)) {
+ CheckTracker::RecalculateAvailableChecks();
+ }
+
+ return 0;
+}
+
+static bool AvailableChecksRecalculateHandler(std::shared_ptr<Ship::Console> Console,
+ const std::vector<std::string>& args, std::string* output) {
+ RandomizerRegion startingRegion = RR_ROOT;
+
+ if (args.size() > 1) {
+ try {
+ startingRegion = static_cast<RandomizerRegion>(std::stoi(args[1]));
+ } catch (std::invalid_argument const& ex) {
+ ERROR_MESSAGE("[SOH] Region should be a number");
+ return 1;
+ }
+
+ if (startingRegion <= RR_NONE || startingRegion >= RR_MAX) {
+ ERROR_MESSAGE("[SOH] Region should be between 1 and %d", RR_MAX - 1);
+ return 1;
+ }
+ }
+
+ CheckTracker::RecalculateAvailableChecks(startingRegion);
+ return 0;
+}
+
void DebugConsole_Init(void) {
// Console
CMD_REGISTER("file_select", { FileSelectHandler, "Returns to the file select." });
@@ -1708,5 +1758,15 @@ void DebugConsole_Init(void) {
{ "group_name", Ship::ArgumentType::TEXT, true },
} });
+ CMD_REGISTER("acpue", { AvailableChecksProcessUndiscoveredExitsHandler,
+ "Available Checks - Process Undiscovered Exits",
+ { { "enable", Ship::ArgumentType::NUMBER, true } } });
+
+ CMD_REGISTER("acr", { AvailableChecksRecalculateHandler,
+ "Available Checks - Recalculate",
+ {
+ { "starting_region", Ship::ArgumentType::NUMBER, true },
+ } });
+
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
}
diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp
index 5b0d97cad..2191563f5 100644
--- a/soh/soh/Enhancements/enemyrandomizer.cpp
+++ b/soh/soh/Enhancements/enemyrandomizer.cpp
@@ -11,17 +11,13 @@
extern "C" {
#include <z64.h>
+#include "src/overlays/actors/ovl_En_Rr/z_en_rr.h"
}
#define CVAR_ENEMY_RANDOMIZER_NAME CVAR_ENHANCEMENT("RandomizedEnemies")
#define CVAR_ENEMY_RANDOMIZER_DEFAULT ENEMY_RANDOMIZER_OFF
#define CVAR_ENEMY_RANDOMIZER_VALUE CVarGetInteger(CVAR_ENEMY_RANDOMIZER_NAME, CVAR_ENEMY_RANDOMIZER_DEFAULT)
-typedef struct EnemyEntry {
- int16_t id;
- int16_t params;
-} EnemyEntry;
-
bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX);
bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy);
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed);
@@ -298,15 +294,7 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* po
// Get randomized enemy ID and parameter.
uint32_t seed =
play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params;
- EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed);
-
- int8_t timesRandomized = 1;
-
- // While randomized enemy isn't allowed in certain situations, randomize again.
- while (!IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, randomEnemy)) {
- randomEnemy = GetRandomizedEnemyEntry(seed + timesRandomized);
- timesRandomized++;
- }
+ EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed, play);
*actorId = randomEnemy.id;
*params = randomEnemy.params;
@@ -362,19 +350,28 @@ void GetSelectedEnemies() {
}
}
-EnemyEntry GetRandomizedEnemyEntry(uint32_t seed) {
+EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play) {
+ std::vector<EnemyEntry> filteredEnemyList = {};
if (selectedEnemyList.size() == 0) {
GetSelectedEnemies();
}
- if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM_SEEDED) {
+ for (EnemyEntry enemy : selectedEnemyList) {
+ if (IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, enemy)) {
+ filteredEnemyList.push_back(enemy);
+ }
+ }
+ if (filteredEnemyList.size() == 0) {
+ filteredEnemyList = selectedEnemyList;
+ }
+ if (CVAR_ENEMY_RANDOMIZER_VALUE == ENEMY_RANDOMIZER_RANDOM_SEEDED) {
uint32_t finalSeed =
seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt);
Random_Init(finalSeed);
- uint32_t randomNumber = Random(0, selectedEnemyList.size());
- return selectedEnemyList[randomNumber];
+ uint32_t randomNumber = Random(0, filteredEnemyList.size());
+ return filteredEnemyList[randomNumber];
} else {
- uint32_t randomSelectedEnemy = Random(0, selectedEnemyList.size());
- return selectedEnemyList[randomSelectedEnemy];
+ uint32_t randomSelectedEnemy = Random(0, filteredEnemyList.size());
+ return filteredEnemyList[randomSelectedEnemy];
}
}
@@ -572,6 +569,43 @@ void FixClubMoblinScale(void* ptr) {
void RegisterEnemyRandomizer() {
COND_ID_HOOK(OnActorInit, ACTOR_EN_MB, CVAR_ENEMY_RANDOMIZER_VALUE, FixClubMoblinScale);
+ // prevent dark link from triggering a voidout
+ COND_VB_SHOULD(VB_TRIGGER_VOIDOUT, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, {
+ Actor* actor = va_arg(args, Actor*);
+
+ if (actor->category != ACTORCAT_PLAYER) {
+ *should = false;
+ Actor_Kill(actor);
+ }
+ });
+
+ // prevent dark link dealing fall damage to the player
+ COND_VB_SHOULD(VB_RECIEVE_FALL_DAMAGE, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, {
+ Actor* actor = va_arg(args, Actor*);
+
+ if (actor->category != ACTORCAT_PLAYER) {
+ *should = false;
+ }
+ });
+
+ // prevent dark link from interfering with HESS/recoil/etc when at more than 100 away from him
+ COND_VB_SHOULD(VB_TORCH2_HANDLE_CLANKING, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, {
+ Actor* darkLink = va_arg(args, Actor*);
+
+ if (darkLink->xzDistToPlayer > 100.0f) {
+ *should = false;
+ }
+ });
+
+ // prevent dark link from being grabbed by like likes and therefore grabbing the player
+ COND_VB_SHOULD(VB_LIKE_LIKE_GRAB_PLAYER, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, {
+ EnRr* likeLike = va_arg(args, EnRr*);
+
+ if (!(likeLike->collider1.base.oc != NULL && likeLike->collider1.base.oc->category == ACTORCAT_PLAYER) &&
+ !(likeLike->collider2.base.oc != NULL && likeLike->collider2.base.oc->category == ACTORCAT_PLAYER)) {
+ *should = false;
+ }
+ });
}
static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME }); \ No newline at end of file
diff --git a/soh/soh/Enhancements/enemyrandomizer.h b/soh/soh/Enhancements/enemyrandomizer.h
index d73930831..cf01eec03 100644
--- a/soh/soh/Enhancements/enemyrandomizer.h
+++ b/soh/soh/Enhancements/enemyrandomizer.h
@@ -1,9 +1,19 @@
#pragma once
#include <libultraship/libultra/types.h>
+#include "item-tables/ItemTableTypes.h"
+
+typedef struct EnemyEntry {
+ int16_t id;
+ int16_t params;
+} EnemyEntry;
#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 52
+bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX);
+bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy);
+EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play);
+
extern const char* enemyCVarList[];
extern const char* enemyNameList[];
extern void GetSelectedEnemies();
diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
index fea114198..cd8230df8 100644
--- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
+++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
@@ -2096,6 +2096,38 @@ typedef enum {
// #### `args`
// - `*EnWonderTalk2`
VB_WONDER_TALK,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
+ // - `*Actor`
+ VB_TRIGGER_VOIDOUT,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
+ // - `*Actor`
+ VB_TORCH2_HANDLE_CLANKING,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
+ // - `*Actor`
+ VB_RECIEVE_FALL_DAMAGE,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
+ // - `*EnRr`
+ VB_LIKE_LIKE_GRAB_PLAYER,
} GIVanillaBehavior;
#endif
diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp
index 64c4265d0..05e9d3d74 100644
--- a/soh/soh/Enhancements/mods.cpp
+++ b/soh/soh/Enhancements/mods.cpp
@@ -98,6 +98,13 @@ void SwitchAge() {
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK_FAST;
gPlayState->linkAgeOnLoad ^= 1;
+ // Discover adult/child spawns
+ if (gPlayState->linkAgeOnLoad == LINK_AGE_ADULT) {
+ Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false);
+ } else {
+ Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false);
+ }
+
static HOOK_ID hookId = 0;
hookId = REGISTER_VB_SHOULD(VB_INFLICT_VOID_DAMAGE, {
*should = false;
diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp
index 55fba1740..bc90ab270 100644
--- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp
@@ -201,6 +201,13 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer
bool stopOnBeatable = false, bool addToPlaythrough = false) {
auto ctx = Rando::Context::GetInstance();
for (auto& exit : region->exits) {
+ int16_t entranceIndex = exit.GetIndex();
+ if (!logic->ACProcessUndiscoveredExits && logic->CalculatingAvailableChecks &&
+ ctx->GetOption(RSK_SHUFFLE_ENTRANCES).Get() && exit.IsShuffled() && entranceIndex != -1 &&
+ !Entrance_GetIsEntranceDiscovered(entranceIndex)) {
+ continue;
+ }
+
Region* exitRegion = exit.GetConnectedRegion();
// Update Time of Day Access for the exit
if (UpdateToDAccess(&exit, exitRegion)) {
@@ -412,18 +419,13 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals
Rando::ItemLocation* location = ctx->GetItemLocation(loc);
RandomizerGet locItem = location->GetPlacedRandomizerGet();
- if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) {
- if (gals.calculatingAvailableChecks) {
- gals.accessibleLocations.push_back(loc);
- StopPerformanceTimer(PT_LOCATION_LOGIC);
- return false;
- }
-
+ if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, logic->CalculatingAvailableChecks)) {
location->AddToPool();
- if (locItem == RG_NONE) {
+ if (locItem == RG_NONE || logic->CalculatingAvailableChecks) {
gals.accessibleLocations.push_back(loc); // Empty location, consider for placement
- } else {
+ }
+ if (locItem != RG_NONE) {
// If ignore has a value, we want to check if the item location should be considered or not
// This is necessary due to the below preprocessing for playthrough generation
if (ignore != RG_NONE) {
@@ -519,11 +521,32 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize
// Return any of the targetLocations that are accessible in logic
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& targetLocations,
RandomizerGet ignore /* = RG_NONE*/,
- bool calculatingAvailableChecks /* = false */) {
+ bool calculatingAvailableChecks /* = false */,
+ RandomizerRegion startingRegion /* = RR_ROOT */) {
auto ctx = Rando::Context::GetInstance();
GetAccessibleLocationsStruct gals(0);
- gals.calculatingAvailableChecks = calculatingAvailableChecks;
ResetLogic(ctx, gals, !calculatingAvailableChecks);
+ if (startingRegion != RR_ROOT) {
+ gals.regionPool.insert(gals.regionPool.begin(), startingRegion);
+
+ const auto& region = RegionTable(startingRegion);
+ if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) {
+ region->childDay = true;
+ } else {
+ region->adultDay = true;
+ }
+ if (region->timePass) {
+ if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) {
+ region->childNight = true;
+ } else {
+ region->adultNight = true;
+ }
+ }
+ }
+ if (calculatingAvailableChecks) {
+ logic->Reset(false);
+ logic->CalculatingAvailableChecks = true;
+ }
do {
gals.InitLoop();
for (size_t i = 0; i < gals.regionPool.size(); i++) {
diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp
index 484331104..75c378095 100644
--- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp
+++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp
@@ -58,6 +58,7 @@ struct GetAccessibleLocationsStruct {
void ClearProgress();
void VanillaFill();
int Fill();
+void SetAreas();
std::vector<RandomizerCheck> GetEmptyLocations(std::vector<RandomizerCheck> allowedLocations);
@@ -66,7 +67,8 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& allowedLocations,
RandomizerGet ignore = RG_NONE,
- bool calculatingAvailableChecks = false);
+ bool calculatingAvailableChecks = false,
+ RandomizerRegion startingRegion = RR_ROOT);
void GeneratePlaythrough();
diff --git a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp
index f49081c59..4c6ab0aa5 100644
--- a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp
+++ b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp
@@ -360,7 +360,7 @@ void Rando::StaticData::RegisterCrateLocations() {
locationTable[RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1] = Location::Crate(RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-389, 1518), "Near Impas House Adult Crate 1", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1));
locationTable[RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-389, 1470), "Near Impas House Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2));
locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-433, -401), "Near Bazaar Adult Crate 1", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_1));
- locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2`", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2));
+ locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2));
locationTable[RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE] = Location::Crate(RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-724, 871), "Behind GS House Adult Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_BEHIND_GS_HOUSE_ADULT_CRATE));
locationTable[RC_KAK_NEAR_GY_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_GY_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1732, 1366), "Near Graveyard Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_GY_CHILD_CRATE));
locationTable[RC_KAK_NEAR_WINDMILL_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_WINDMILL_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1170, 601), "Near Windmill Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_WINDMILL_CHILD_CRATE));
diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp
index cdba9ef38..2ee39006d 100644
--- a/soh/soh/Enhancements/randomizer/context.cpp
+++ b/soh/soh/Enhancements/randomizer/context.cpp
@@ -11,6 +11,7 @@
#include "fishsanity.h"
#include "macros.h"
#include "3drando/hints.hpp"
+#include "soh/util.h"
#include "../kaleido.h"
#include <fstream>
@@ -370,25 +371,8 @@ GetItemEntry Context::GetFinalGIEntry(const RandomizerCheck rc, const bool check
return giEntry;
}
-std::string sanitize(std::string stringValue) {
- // Add backslashes.
- for (auto i = stringValue.begin();;) {
- auto const pos =
- std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
- if (pos == stringValue.end()) {
- break;
- }
- i = std::next(stringValue.insert(pos, '\\'), 2);
- }
-
- // Removes others.
- std::erase_if(stringValue, [](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; });
-
- return stringValue;
-}
-
void Context::ParseSpoiler(const char* spoilerFileName) {
- std::ifstream spoilerFileStream(sanitize(spoilerFileName));
+ std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return;
}
@@ -397,11 +381,13 @@ void Context::ParseSpoiler(const char* spoilerFileName) {
try {
nlohmann::json spoilerFileJson;
spoilerFileStream >> spoilerFileJson;
+ spoilerFileStream.close();
ParseHashIconIndexesJson(spoilerFileJson);
Rando::Settings::GetInstance()->ParseJson(spoilerFileJson);
ParseItemLocationsJson(spoilerFileJson);
- ParseHintJson(spoilerFileJson);
+ ParseTricksJson(spoilerFileJson);
mEntranceShuffler->ParseJson(spoilerFileJson);
+ ParseHintJson(spoilerFileJson);
mDungeons->ParseJson(spoilerFileJson);
mTrials->ParseJson(spoilerFileJson);
mSpoilerLoaded = true;
@@ -469,6 +455,17 @@ void Context::ParseHintJson(nlohmann::json spoilerFileJson) {
CreateStaticHints();
}
+void Context::ParseTricksJson(nlohmann::json spoilerFileJson) {
+ nlohmann::json enabledTricksJson = spoilerFileJson["enabledTricks"];
+ const auto& settings = Rando::Settings::GetInstance();
+ for (auto it : enabledTricksJson) {
+ int rt = settings->GetRandomizerTrickByName(it);
+ if (rt != -1) {
+ mTrickOptions[rt].Set(RO_GENERIC_ON);
+ }
+ }
+}
+
std::shared_ptr<EntranceShuffler> Context::GetEntranceShuffler() {
return mEntranceShuffler;
}
@@ -524,6 +521,10 @@ RandoOptionLACSCondition Context::LACSCondition() const {
return mLACSCondition;
}
+void Context::LACSCondition(RandoOptionLACSCondition lacsCondition) {
+ mLACSCondition = lacsCondition;
+}
+
std::shared_ptr<Kaleido> Context::GetKaleido() {
if (mKaleido == nullptr) {
mKaleido = std::make_shared<Kaleido>();
diff --git a/soh/soh/Enhancements/randomizer/context.h b/soh/soh/Enhancements/randomizer/context.h
index 8e9346069..968947756 100644
--- a/soh/soh/Enhancements/randomizer/context.h
+++ b/soh/soh/Enhancements/randomizer/context.h
@@ -106,12 +106,22 @@ class Context {
* @return RandoOptionLACSCondition
*/
RandoOptionLACSCondition LACSCondition() const;
+
+ /**
+ * @brief Sets the resolved Light Arrow CutScene check condition.
+ * There is no direct option for this, it is inferred based on the value of a few other options.
+ *
+ * @param lacsCondition
+ */
+ void LACSCondition(RandoOptionLACSCondition lacsCondition);
+
GetItemEntry GetFinalGIEntry(RandomizerCheck rc, bool checkObtainability = true, GetItemID ogItemId = GI_NONE);
void ParseSpoiler(const char* spoilerFileName);
void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson);
void ParseItemLocationsJson(nlohmann::json spoilerFileJson);
void WriteHintJson(nlohmann::ordered_json& spoilerFileJson);
void ParseHintJson(nlohmann::json spoilerFileJson);
+ void ParseTricksJson(nlohmann::json spoilerFileJson);
std::map<RandomizerCheck, ItemOverride> overrides = {};
std::vector<std::vector<RandomizerCheck>> playthroughLocations = {};
std::vector<RandomizerCheck> everyPossibleLocation = {};
diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp
index 5a19339ce..153bb6df4 100644
--- a/soh/soh/Enhancements/randomizer/entrance.cpp
+++ b/soh/soh/Enhancements/randomizer/entrance.cpp
@@ -250,7 +250,319 @@ std::string EntranceNameByRegions(RandomizerRegion parentRegion, RandomizerRegio
return RegionTable(parentRegion)->regionName + " -> " + RegionTable(connectedRegion)->regionName;
}
-void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
+std::unordered_map<int16_t, Entrance*> entranceMap;
+
+void SetAllEntrancesData() {
+ std::vector<EntranceInfoPair> entranceShuffleTable = {
+ // clang-format off
+ // Type Parent Region Connected Region Index
+ { { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE },
+ { EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } },
+ { { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE },
+ { EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } },
+ { { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE },
+ { EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } },
+ { { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE },
+ { EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } },
+ { { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE },
+ { EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } },
+ { { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE },
+ { EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } },
+ { { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE },
+ { EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } },
+ { { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE },
+ { EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } },
+ { { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE },
+ { EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } },
+ { { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE },
+ { EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } },
+ { { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE },
+ { EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } },
+ { { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE },
+ { EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } },
+
+ { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 },
+ { EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } },
+ { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 },
+ { EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } },
+ { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 },
+ { EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } },
+ { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 },
+ { EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } },
+ { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 },
+ { EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } },
+ { { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 },
+ { EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } },
+ { { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 },
+ { EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } },
+ { { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 },
+ { EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } },
+ { { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 },
+ { EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } },
+ { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 },
+ { EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } },
+ { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 },
+ { EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } },
+ { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 },
+ { EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } },
+ { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 },
+ { EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } },
+ { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 },
+ { EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } },
+ { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE },
+ { EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } },
+ { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 },
+ { EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } },
+ { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 },
+ { EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } },
+ { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT },
+ { EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } },
+ { { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK },
+ { EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } },
+ { { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 },
+ { EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } },
+ { { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 },
+ { EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } },
+ { { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 },
+ { EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } },
+ { { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 },
+ { EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } },
+ { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE },
+ { EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } },
+ { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 },
+ { EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } },
+ { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER },
+ { EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } },
+ { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 },
+ { EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } },
+ { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 },
+ { EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } },
+ { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 },
+ { EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } },
+ { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 },
+ { EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } },
+ { { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS },
+ { EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } },
+ { { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC },
+ { EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } },
+ { { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD },
+ // 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy
+ // fountain exits (normally they both use 0x340)
+ { EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } },
+ { { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC },
+ { EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } },
+ { { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT },
+ { EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } },
+ { { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF },
+ { EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } },
+
+ { { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 },
+ { EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } },
+ { { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE },
+ { EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } },
+ { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL },
+ { EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } },
+ { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT },
+ { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } },
+ { { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK },
+ { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } },
+
+ // Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the
+ // grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
+ // Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the
+ // grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
+ { { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) },
+ { EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) },
+ { EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) },
+ { EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) },
+ { EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) },
+ { EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) },
+ { EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) },
+ { EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) },
+ { EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) },
+ { EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) },
+ { EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) },
+ { EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) },
+ { EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) },
+ { EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) },
+ { EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) },
+ { EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) },
+ { EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) },
+ { EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) },
+ { EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) },
+ { EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) },
+ { EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) },
+ { EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } },
+ { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) },
+ { EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } },
+
+ // Graves have their own specified entrance indices
+ { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 },
+ { EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } },
+ { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 },
+ { EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } },
+ { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 },
+ { EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } },
+ { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE },
+ { EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } },
+
+ { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT },
+ { EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } },
+ { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT },
+ { EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } },
+ { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT },
+ { EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } },
+ { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT },
+ { EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } },
+ { { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT },
+ { EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } },
+ { { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT },
+ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } },
+ { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT },
+ { EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } },
+ { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT },
+ { EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } },
+ { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT },
+ { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } },
+ { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE },
+ { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } },
+ { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT },
+ { EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } },
+ { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE },
+ { EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } },
+ { { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT },
+ { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } },
+ { { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT },
+ { EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } },
+ { { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT },
+ { EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } },
+ { { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT },
+ { EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } },
+ { { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT },
+ { EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } },
+ { { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT },
+ { EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } },
+ { { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT },
+ { EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } },
+ { { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE },
+ { EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } },
+ { { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT },
+ { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } },
+ { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT },
+ { EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } },
+ { { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT },
+ { EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } },
+ { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT },
+ { EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } },
+ { { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE },
+ { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } },
+ { { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT },
+ { EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } },
+
+ { { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT },
+ NO_RETURN_ENTRANCE },
+
+ { { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP },
+ NO_RETURN_ENTRANCE },
+
+ { { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 },
+ NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between
+ // Adult Spawn and prelude of light (normally they both use 0x5F4)
+ { { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD },
+ NO_RETURN_ENTRANCE },
+
+ { { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE },
+ { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT, ENTR_DEKU_TREE_BOSS_DOOR } },
+ { { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE },
+ { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT, ENTR_DODONGOS_CAVERN_BOSS_DOOR } },
+ { { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE },
+ { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT, ENTR_JABU_JABU_BOSS_DOOR } },
+ { { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE },
+ { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } },
+ { { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE },
+ { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } },
+ { { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE },
+ { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } },
+ { { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE },
+ { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } },
+ { { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE },
+ { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } },
+
+ { { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP },
+ NO_RETURN_ENTRANCE },
+ { { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP },
+ NO_RETURN_ENTRANCE },
+ // clang-format on
+ };
+
auto ctx = Rando::Context::GetInstance();
for (auto& entrancePair : entranceShuffleTable) {
@@ -262,6 +574,7 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
forwardEntrance->SetIndex(forwardEntry.index);
forwardEntrance->SetType(forwardEntry.type);
forwardEntrance->SetAsPrimary();
+ entranceMap[forwardEntry.index] = forwardEntrance;
// When decouple entrances is on, mark the forward entrance
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
@@ -273,6 +586,7 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
returnEntrance->SetIndex(returnEntry.index);
returnEntrance->SetType(returnEntry.type);
forwardEntrance->BindTwoWay(returnEntrance);
+ entranceMap[returnEntry.index] = returnEntrance;
// Mark reverse entrance as decoupled
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
@@ -842,316 +1156,6 @@ int EntranceShuffler::ShuffleAllEntrances() {
mTotalRandomizableEntrances = 0;
mCurNumRandomizedEntrances = 0;
- std::vector<EntranceInfoPair> entranceShuffleTable = {
- // clang-format off
- // Type Parent Region Connected Region Index
- { { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE },
- { EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } },
- { { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE },
- { EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } },
- { { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE },
- { EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } },
- { { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE },
- { EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } },
- { { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE },
- { EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } },
- { { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE },
- { EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } },
- { { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE },
- { EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } },
- { { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE },
- { EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } },
- { { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE },
- { EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } },
- { { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE },
- { EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } },
- { { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE },
- { EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } },
- { { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE },
- { EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } },
-
- { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 },
- { EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } },
- { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 },
- { EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } },
- { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 },
- { EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } },
- { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 },
- { EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } },
- { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 },
- { EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } },
- { { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 },
- { EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } },
- { { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 },
- { EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } },
- { { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 },
- { EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } },
- { { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 },
- { EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } },
- { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 },
- { EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } },
- { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 },
- { EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } },
- { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 },
- { EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } },
- { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 },
- { EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } },
- { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 },
- { EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } },
- { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE },
- { EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } },
- { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 },
- { EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } },
- { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 },
- { EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } },
- { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT },
- { EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } },
- { { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK },
- { EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } },
- { { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 },
- { EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } },
- { { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 },
- { EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } },
- { { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 },
- { EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } },
- { { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 },
- { EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } },
- { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE },
- { EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } },
- { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 },
- { EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } },
- { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER },
- { EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } },
- { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 },
- { EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } },
- { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 },
- { EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } },
- { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 },
- { EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } },
- { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 },
- { EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } },
- { { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS },
- { EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } },
- { { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC },
- { EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } },
- { { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD },
- // 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy
- // fountain exits (normally they both use 0x340)
- { EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } },
- { { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC },
- { EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } },
- { { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT },
- { EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } },
- { { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF },
- { EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } },
-
- { { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 },
- { EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } },
- { { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE },
- { EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } },
- { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL },
- { EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } },
- { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT },
- { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } },
- { { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK },
- { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } },
-
- // Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the
- // grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
- // Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the
- // grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
- { { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) },
- { EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) },
- { EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) },
- { EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) },
- { EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) },
- { EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) },
- { EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) },
- { EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) },
- { EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) },
- { EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) },
- { EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) },
- { EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) },
- { EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) },
- { EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) },
- { EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) },
- { EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) },
- { EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) },
- { EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) },
- { EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) },
- { EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) },
- { EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) },
- { EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) },
- { EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) },
- { EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) },
- { EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) },
- { EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) },
- { EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) },
- { EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) },
- { EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) },
- { EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) },
- { EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) },
- { EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) },
- { EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } },
- { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) },
- { EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } },
-
- // Graves have their own specified entrance indices
- { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 },
- { EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } },
- { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 },
- { EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } },
- { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 },
- { EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } },
- { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE },
- { EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } },
-
- { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT },
- { EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } },
- { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT },
- { EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } },
- { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT },
- { EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } },
- { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT },
- { EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } },
- { { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT },
- { EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } },
- { { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT },
- { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } },
- { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT },
- { EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } },
- { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT },
- { EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } },
- { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT },
- { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } },
- { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE },
- { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } },
- { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT },
- { EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } },
- { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE },
- { EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } },
- { { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT },
- { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } },
- { { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT },
- { EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } },
- { { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT },
- { EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } },
- { { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT },
- { EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } },
- { { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT },
- { EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } },
- { { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT },
- { EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } },
- { { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT },
- { EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } },
- { { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE },
- { EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } },
- { { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT },
- { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } },
- { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT },
- { EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } },
- { { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT },
- { EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } },
- { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT },
- { EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } },
- { { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE },
- { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } },
- { { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT },
- { EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } },
-
- { { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT },
- NO_RETURN_ENTRANCE },
-
- { { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP },
- NO_RETURN_ENTRANCE },
- { { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP },
- NO_RETURN_ENTRANCE },
-
- { { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN },
- NO_RETURN_ENTRANCE },
- { { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 },
- NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between
- // Adult Spawn and prelude of light (normally they both use 0x5F4)
- { { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD },
- NO_RETURN_ENTRANCE },
- { { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD },
- NO_RETURN_ENTRANCE },
- { { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD },
- NO_RETURN_ENTRANCE },
- { { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD },
- NO_RETURN_ENTRANCE },
- { { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD },
- NO_RETURN_ENTRANCE },
- { { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD },
- NO_RETURN_ENTRANCE },
-
- { { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE },
- { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, ENTR_DEKU_TREE_BOSS_DOOR } },
- { { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE },
- { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, ENTR_DODONGOS_CAVERN_BOSS_DOOR } },
- { { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE },
- { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, ENTR_JABU_JABU_BOSS_DOOR } },
- { { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE },
- { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } },
- { { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE },
- { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } },
- { { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE },
- { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } },
- { { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE },
- { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } },
- { { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE },
- { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } },
-
- { { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP },
- NO_RETURN_ENTRANCE },
- { { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP },
- NO_RETURN_ENTRANCE },
- { { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP },
- NO_RETURN_ENTRANCE },
- { { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP },
- NO_RETURN_ENTRANCE },
- { { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP },
- NO_RETURN_ENTRANCE },
- { { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP },
- NO_RETURN_ENTRANCE },
- { { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP },
- NO_RETURN_ENTRANCE },
- { { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP },
- NO_RETURN_ENTRANCE },
- // clang-format on
- };
-
std::map<std::string, PriorityEntrance> priorityEntranceTable = {
{ "Bolero", { { RR_DMC_CENTRAL_LOCAL }, { EntranceType::OwlDrop, EntranceType::WarpSong } } },
{ "Nocturne",
@@ -1163,7 +1167,7 @@ int EntranceShuffler::ShuffleAllEntrances() {
};
mEntranceShuffleFailure = false;
- SetAllEntrancesData(entranceShuffleTable);
+ SetAllEntrancesData();
EntrancePools oneWayEntrancePools = {};
EntrancePools entrancePools = {};
@@ -1473,11 +1477,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
if (true /* ctx->GetOption(RSK_SHUFFLE_BLUEWARP_ENTRANCES).Is(RO_BLUEWARP_ENTRANCE_SHUFFLE_DUNGEON) */) {
// If a boss room is inside a boss door, make the blue warp go outside the dungeon's entrance
std::map<std::string, Entrance*> bossExits = {
- { EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY),
+ { EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT),
GetEntrance(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE) },
- { EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY),
+ { EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT),
GetEntrance(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL) },
- { EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY),
+ { EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT),
GetEntrance(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN) },
{ EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW) },
@@ -1515,11 +1519,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
// Pair <BlueWarp exit, BossRoom reverse exit>
std::vector<EntrancePair> bossRoomExitPairs = {
{ GetEntrance(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE),
- GetEntrance(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY) },
+ GetEntrance(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT) },
{ GetEntrance(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL),
- GetEntrance(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY) },
+ GetEntrance(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT) },
{ GetEntrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN),
- GetEntrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY) },
+ GetEntrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT) },
{ GetEntrance(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW),
GetEntrance(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY) },
{ GetEntrance(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL),
@@ -1647,6 +1651,30 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) {
}
}
} catch (const std::exception& e) { throw e; }
+ // We may need to reset more things here or elsewhere in spoiler loading
+ RegionTable_Init();
+ ApplyEntranceOverrides();
+ SetAreas();
+}
+
+void EntranceShuffler::ApplyEntranceOverrides() {
+ SetAllEntrancesData();
+
+ for (size_t i = 0; i < entranceOverrides.size(); i++) {
+ EntranceOverride entranceOverride = entranceOverrides[i];
+
+ if (entranceOverride.index == 0 && entranceOverride.destination == 0 && entranceOverride.override == 0 &&
+ entranceOverride.overrideDestination == 0) {
+ continue;
+ }
+
+ Entrance* entrance = entranceMap[entranceOverride.index];
+ Entrance* overrideEntrance = entranceMap[entranceOverride.override];
+
+ entrance->Disconnect();
+ entrance->Connect(overrideEntrance->GetOriginalConnectedRegionKey());
+ entrance->SetAsShuffled();
+ }
}
} // namespace Rando
diff --git a/soh/soh/Enhancements/randomizer/entrance.h b/soh/soh/Enhancements/randomizer/entrance.h
index 8fc192ca1..5b14611fe 100644
--- a/soh/soh/Enhancements/randomizer/entrance.h
+++ b/soh/soh/Enhancements/randomizer/entrance.h
@@ -128,6 +128,7 @@ class EntranceShuffler {
void CreateEntranceOverrides();
void UnshuffleAllEntrances();
void ParseJson(nlohmann::json spoilerFileJson);
+ void ApplyEntranceOverrides();
private:
std::vector<Entrance*> AssumeEntrancePool(std::vector<Entrance*>& entrancePool);
diff --git a/soh/soh/Enhancements/randomizer/item.cpp b/soh/soh/Enhancements/randomizer/item.cpp
index ee0dc9403..3b75a0c95 100644
--- a/soh/soh/Enhancements/randomizer/item.cpp
+++ b/soh/soh/Enhancements/randomizer/item.cpp
@@ -46,14 +46,20 @@ Item::~Item() = default;
void Item::ApplyEffect() const {
auto ctx = Rando::Context::GetInstance();
- ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true);
- ctx->GetLogic()->SetInLogic(logicVal, true);
+ auto logic = ctx->GetLogic();
+ if (!logic->CalculatingAvailableChecks) {
+ logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true);
+ }
+ logic->SetInLogic(logicVal, true);
}
void Item::UndoEffect() const {
auto ctx = Rando::Context::GetInstance();
- ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false);
- ctx->GetLogic()->SetInLogic(logicVal, false);
+ auto logic = ctx->GetLogic();
+ if (!logic->CalculatingAvailableChecks) {
+ logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false);
+ }
+ logic->SetInLogic(logicVal, false);
}
const Text& Item::GetName() const {
diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h
index dc09712a1..733623cb4 100644
--- a/soh/soh/Enhancements/randomizer/location_access.h
+++ b/soh/soh/Enhancements/randomizer/location_access.h
@@ -137,7 +137,6 @@ class Region {
bool adultDay = false;
bool adultNight = false;
bool addedToPool = false;
- ;
void ApplyTimePass();
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp
index 109799285..487e9678a 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp
@@ -436,11 +436,16 @@ void RegionTable_Init_DekuTree() {
#pragma endregion
// Boss Room
+ // RANDOTODO make it so entrance randomiser can properly handle more than 1 access to that entrance
areaTable[RR_DEKU_TREE_BOSS_ENTRYWAY] = Region("Deku Tree Boss Entryway", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
+ Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}),
+ });
+
+ areaTable[RR_DEKU_TREE_BOSS_EXIT] = Region("Deku Tree Boss Exit", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
+ // Exits
Entrance(RR_DEKU_TREE_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsVanilla();}),
Entrance(RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsMQ();}),
- Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}),
});
areaTable[RR_DEKU_TREE_BOSS_ROOM] = Region("Deku Tree Boss Room", "Deku Tree", {}, NO_DAY_NIGHT_CYCLE, {
@@ -460,8 +465,8 @@ void RegionTable_Init_DekuTree() {
LOCATION(RC_DEKU_TREE_QUEEN_GOHMA_GRASS_8, logic->CanCutShrubs()),
}, {
// Exits
- Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, []{return true;}),
- Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false),
+ Entrance(RR_DEKU_TREE_BOSS_EXIT, []{return true;}),
+ Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false),
});
// clang-format on
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp
index c268f4691..763a3398a 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp
@@ -268,6 +268,7 @@ void RegionTable_Init_DodongosCavern() {
LOCATION(RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT, logic->CanStunDeku()),
}, {
//Exits
+ Entrance(RR_DODONGOS_CAVERN_MQ_BEGINNING, []{return true;}),
Entrance(RR_DODONGOS_CAVERN_MQ_GOSSIP_STONE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET);});}),
Entrance(RR_DODONGOS_CAVERN_MQ_MOUTH_SIDE_BRIDGE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}),
Entrance(RR_DODONGOS_CAVERN_MQ_STAIRS_LOWER, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}),
@@ -386,7 +387,7 @@ void RegionTable_Init_DodongosCavern() {
Entrance(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanUse(RG_STICKS) && logic->HasItem(RG_GORONS_BRACELET);}), //Implies access to RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM from here
});
- areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Torch Puzzle Lower", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {
+ areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Big Block Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_1, logic->CanBreakPots()),
LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_2, logic->CanBreakPots()),
@@ -426,7 +427,7 @@ void RegionTable_Init_DodongosCavern() {
Entrance(RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM, []{return Here(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanKillEnemy(RE_LIZALFOS);});}),
});
- areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Before Upper Lizalfos", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {
+ areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Two Fires Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_1, logic->CanBreakPots()),
LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_2, logic->CanBreakPots()),
@@ -558,11 +559,16 @@ void RegionTable_Init_DodongosCavern() {
#pragma endregion
// Boss Room
+ // RANDOTODO make it so entrance randomiser can properly handle more than 1 access to that entrance
areaTable[RR_DODONGOS_CAVERN_BOSS_ENTRYWAY] = Region("Dodongos Cavern Boss Entryway", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
+ Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}),
+ });
+
+ areaTable[RR_DODONGOS_CAVERN_BOSS_EXIT] = Region("Dodongos Cavern Boss Exit", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {}, {
+ // Exits
Entrance(RR_DODONGOS_CAVERN_BOSS_AREA, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsVanilla();}),
Entrance(RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsMQ();}),
- Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}),
});
areaTable[RR_DODONGOS_CAVERN_BOSS_ROOM] = Region("Dodongos Cavern Boss Room", "Dodongos Cavern", {}, NO_DAY_NIGHT_CYCLE, {
@@ -575,8 +581,8 @@ void RegionTable_Init_DodongosCavern() {
LOCATION(RC_KING_DODONGO, logic->DodongosCavernClear),
}, {
// Exits
- Entrance(RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, []{return true;}),
- Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false),
+ Entrance(RR_DODONGOS_CAVERN_BOSS_EXIT, []{return true;}),
+ Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false),
});
// clang-format on
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp
index 764a78a1c..834e8a3c3 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp
@@ -38,7 +38,7 @@ void RegionTable_Init_FireTemple() {
}, {
//Exits
Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return true;}),
- Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}))) || logic->CanUse(RG_HOVER_BOOTS));}),
+ Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}) || logic->CanUse(RG_HOVER_BOOTS));}),
});
areaTable[RR_FIRE_TEMPLE_LOOP_ENEMIES] = Region("Fire Temple Loop Enemies", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
@@ -445,7 +445,7 @@ void RegionTable_Init_FireTemple() {
Entrance(RR_FIRE_TEMPLE_MQ_FIRST_ROOM_UPPER, []{return true;}),
//Child cannot make it to the north side torches without a hook without specifically bunny hood speed + hover boots
Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM_NORTH, []{return logic->FireTimer() > 32 && (logic->CanUse(RG_HOOKSHOT) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)));}),
- Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}),
+ Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}),
});
//This room assumes tunic logic is handled on entry.
@@ -701,7 +701,7 @@ void RegionTable_Init_FireTemple() {
Entrance(RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER, []{return true;}),
});
- areaTable[RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER] = Region("Fire Temple MQ North Fire Maze", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
+ areaTable[RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER] = Region("Fire Temple MQ Upper Flare Dancer", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_FIRE_TEMPLE_MQ_FREESTANDING_KEY, logic->CanKillEnemy(RE_FLARE_DANCER)),
}, {
@@ -740,7 +740,7 @@ void RegionTable_Init_FireTemple() {
// Exits
Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsMQ() && false;}),
- Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return true;}),
+ Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY);}),
});
areaTable[RR_FIRE_TEMPLE_BOSS_ROOM] = Region("Fire Temple Boss Room", "Fire Temple", {}, NO_DAY_NIGHT_CYCLE, {
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp
index a74642324..05413a2c9 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp
@@ -298,7 +298,7 @@ void RegionTable_Init_ForestTemple() {
}, {
//Exits
Entrance(RR_FOREST_TEMPLE_LOBBY, []{return true;}),
- Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}),
+ Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
#pragma endregion
@@ -481,7 +481,7 @@ void RegionTable_Init_ForestTemple() {
Entrance(RR_FOREST_TEMPLE_MQ_FALLING_ROOM, []{return logic->CanUse(RG_SONG_OF_TIME);}),
});
- areaTable[RR_FOREST_TEMPLE_MQ_JOELLE_ROOM] = Region("Forest Temple MQ Joelle room", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
+ areaTable[RR_FOREST_TEMPLE_MQ_JOELLE_ROOM] = Region("Forest Temple MQ Joelle Room", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->ForestTempleJoelle, []{return logic->CanUse(RG_FAIRY_BOW);}),
}, {
@@ -593,7 +593,7 @@ void RegionTable_Init_ForestTemple() {
areaTable[RR_FOREST_TEMPLE_MQ_BOSS_REGION] = Region("Forest Temple MQ Boss Region", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_FOREST_TEMPLE_MQ_BASEMENT, []{return logic->ForestOpenBossCorridor;}),
- Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}),
+ Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
#pragma endregion
@@ -603,7 +603,7 @@ void RegionTable_Init_ForestTemple() {
// Exits
Entrance(RR_FOREST_TEMPLE_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_FOREST_TEMPLE_MQ_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsMQ() && false;}),
- Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return true;}),
+ Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}),
});
areaTable[RR_FOREST_TEMPLE_BOSS_ROOM] = Region("Forest Temple Boss Room", "Forest Temple", {}, NO_DAY_NIGHT_CYCLE, {
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 040c10a90..cc0c9767d 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp
@@ -29,12 +29,7 @@ void RegionTable_Init_GanonsCastle() {
Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, []{return true;}),
Entrance(RR_GANONS_CASTLE_SPIRIT_TRIAL, []{return true;}),
Entrance(RR_GANONS_CASTLE_LIGHT_TRIAL, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}),
- Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
- (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
- (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
- (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
- (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
- (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}),
+ Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}),
Entrance(RR_GANONS_CASTLE_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON) || logic->CanUse(RG_LENS_OF_TRUTH);}),
});
@@ -162,13 +157,7 @@ void RegionTable_Init_GanonsCastle() {
Entrance(RR_GANONS_CASTLE_MQ_SHADOW_TRIAL_STARTING_LEDGE, []{return true;}),
Entrance(RR_GANONS_CASTLE_MQ_SPIRIT_TRIAL_CHAIRS_ROOM, []{return true;}),
Entrance(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_DINOLFOS_ROOM, []{return Here(RR_GANONS_CASTLE_MQ_MAIN, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);});}),
- //RANDOTODO could we just set these events automatically based on the setting?
- Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
- (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
- (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
- (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
- (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
- (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}),
+ Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}),
Entrance(RR_GANONS_CASTLE_MQ_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON_MQ) || logic->CanUse(RG_LENS_OF_TRUTH);}),
});
@@ -436,7 +425,13 @@ void RegionTable_Init_GanonsCastle() {
//Exits
Entrance(RR_GANONS_CASTLE_LOBBY, []{return ctx->GetDungeon(GANONS_CASTLE)->IsVanilla();}),
Entrance(RR_GANONS_CASTLE_MQ_MAIN, []{return ctx->GetDungeon(GANONS_CASTLE)->IsMQ();}),
- Entrance(RR_GANONS_TOWER_FLOOR_1, []{return true;}),
+ //RANDOTODO could we just set these events automatically based on the setting?
+ Entrance(RR_GANONS_TOWER_FLOOR_1, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
+ (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
+ (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
+ (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
+ (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
+ (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}),
});
areaTable[RR_GANONS_TOWER_FLOOR_1] = Region("Ganon's Tower Floor 1", "Ganons Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp
index 5466c598c..f1a0ebee7 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp
@@ -149,7 +149,7 @@ void RegionTable_Init_GerudoTrainingGround() {
Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER, []{return logic->SmallKeys(RR_GERUDO_TRAINING_GROUND, 3);}),
});
- areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, {
+ areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Maze Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->MQGTGMazeSwitch, []{return logic->CanUse(RG_MEGATON_HAMMER);}),
},
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp
index 7aa2066ca..20ff9b1e1 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp
@@ -84,7 +84,7 @@ void RegionTable_Init_IceCavern() {
//Exits
//the switch for the glass blocking the entrance is linked to the switch that controls the glass around the skulltulla in RR_ICE_CAVERN_MQ_SCARECROW_ROOM
//if you clear the ice, you can hit it with a pot from here.
- Entrance(RR_ICE_CAVERN_BEGINNING, []{return logic->BlueFire();}),
+ Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->BlueFire();}),
Entrance(RR_ICE_CAVERN_MQ_MAP_ROOM, []{return Here(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->CanKillEnemy(RE_WHITE_WOLFOS) && logic->CanKillEnemy(RE_FREEZARD);});}),
Entrance(RR_ICE_CAVERN_MQ_COMPASS_ROOM, []{return logic->IsAdult && logic->BlueFire();}),
Entrance(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, []{return logic->BlueFire();}),
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp
index bc90e5722..84d6ce7e5 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp
@@ -354,9 +354,13 @@ void RegionTable_Init_JabuJabusBelly() {
// Boss Room
areaTable[RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY] = Region("Jabu Jabus Belly Boss Entryway", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
+ Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}),
+ });
+
+ areaTable[RR_JABU_JABUS_BELLY_BOSS_EXIT] = Region("Jabu Jabus Belly Boss Exit", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
+ // Exits
Entrance(RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsVanilla();}),
Entrance(RR_JABU_JABUS_BELLY_MQ_EAST_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsMQ();}),
- Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}),
});
areaTable[RR_JABU_JABUS_BELLY_BOSS_ROOM] = Region("Jabu Jabus Belly Boss Room", "Jabu Jabus Belly", {}, NO_DAY_NIGHT_CYCLE, {
@@ -374,8 +378,8 @@ void RegionTable_Init_JabuJabusBelly() {
LOCATION(RC_BARINADE, logic->JabuJabusBellyClear),
}, {
// Exits
- Entrance(RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, []{return false;}),
- Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false),
+ Entrance(RR_JABU_JABUS_BELLY_BOSS_EXIT, []{return false;}),
+ Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false),
});
// clang-format on
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp
index e7ee515d1..c91e011ea 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp
@@ -112,7 +112,7 @@ void RegionTable_Init_ShadowTemple() {
LOCATION(RC_SHADOW_TEMPLE_AFTER_SHIP_LOWER_HEART, (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->CanUse(RG_SONG_OF_TIME) || (logic->CanUse(RG_DISTANT_SCARECROW) && logic->CanUse(RG_HOVER_BOOTS))),
}, {
//Exits
- Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS) && logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);})
+ Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS);})
});
#pragma endregion
@@ -370,7 +370,7 @@ void RegionTable_Init_ShadowTemple() {
}, {
//Exits
Entrance(RR_SHADOW_TEMPLE_MQ_ACROSS_CHASM, []{return logic->CanUse(RG_HOVER_BOOTS) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH));}),
- Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}),
+ Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
//Assumes lens is checked on entry
@@ -403,7 +403,7 @@ void RegionTable_Init_ShadowTemple() {
// Exits
Entrance(RR_SHADOW_TEMPLE_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_SHADOW_TEMPLE_MQ_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsMQ() && false;}),
- Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return true;}),
+ Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}),
});
areaTable[RR_SHADOW_TEMPLE_BOSS_ROOM] = Region("Shadow Temple Boss Room", "Shadow Temple", {}, NO_DAY_NIGHT_CYCLE, {
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp
index c1cf1856f..c78a6bb49 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp
@@ -145,7 +145,7 @@ void RegionTable_Init_SpiritTemple() {
areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return true;}),
- Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
+ Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
#pragma endregion
@@ -194,13 +194,13 @@ void RegionTable_Init_SpiritTemple() {
});
// Room to store the 2 pots in to handle glitch logic going backwards around the loop later
- areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
+ areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ 1F Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_1, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_2, logic->CanBreakPots()),
}, {});
- areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
+ areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple MQ Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
//For non-fairy pot items, you can also get them with rang without killing the stalfos
EventAccess(&logic->FairyPot, []{return Here(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}),
@@ -315,7 +315,7 @@ void RegionTable_Init_SpiritTemple() {
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
});
- areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
+ areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ West Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}),
@@ -541,7 +541,7 @@ void RegionTable_Init_SpiritTemple() {
areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}),
- Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
+ Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
#pragma endregion
@@ -551,7 +551,7 @@ void RegionTable_Init_SpiritTemple() {
// Exits
Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}),
- Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}),
+ Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
});
areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", "Spirit Temple", {}, NO_DAY_NIGHT_CYCLE, {
diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp
index 3acf5d74a..d490b1650 100644
--- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp
@@ -277,7 +277,7 @@ void RegionTable_Init_WaterTemple() {
}, {
//Exits
Entrance(RR_WATER_TEMPLE_LOBBY, []{return true;}),
- Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}),
+ Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
#pragma endregion
@@ -362,10 +362,10 @@ void RegionTable_Init_WaterTemple() {
Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_ICE_ARROWS) || logic->CanUse(RG_NAYRUS_LOVE);}),
});
- areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Main", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
+ areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Boss Door", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_WATER_TEMPLE_MQ_3F_NORTH_LEDGE, []{return logic->CanUse(RG_ICE_ARROWS) || logic->TakeDamage();}),
- Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}),
+ Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
areaTable[RR_WATER_TEMPLE_MQ_EAST_TOWER] = Region("Water Temple MQ East Tower", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
@@ -507,7 +507,7 @@ void RegionTable_Init_WaterTemple() {
Entrance(RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F, []{return logic->CanUse(RG_LONGSHOT);}),
});
- areaTable[RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F] = Region("Water Temple MQ Behind Blue Switch 2F", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
+ areaTable[RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F] = Region("Water Temple MQ Behind Blue Switch 3F", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_WATER_TEMPLE_MQ_GS_BEFORE_UPPER_WATER_SWITCH, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)),
}, {
@@ -552,7 +552,7 @@ void RegionTable_Init_WaterTemple() {
}, {});
//This room exists to hold the wonderitems that drop from the emblems here. Specifically this assumes you are standing on the final ledge
- areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple Waterfall", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
+ areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple MQ Waterfall", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_WATER_TEMPLE_MQ_3F_CENTRAL, []{return logic->SmallKeys(RR_WATER_TEMPLE, 1) && logic->CanUse(RG_LONGSHOT);}),
Entrance(RR_WATER_TEMPLE_MQ_STALFOS_PIT, []{return true;}),
@@ -843,7 +843,7 @@ void RegionTable_Init_WaterTemple() {
// Exits
Entrance(RR_WATER_TEMPLE_PRE_BOSS_ROOM, []{return ctx->GetDungeon(WATER_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return ctx->GetDungeon(WATER_TEMPLE)->IsMQ() && false;}),
- Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return true;}),
+ Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}),
});
areaTable[RR_WATER_TEMPLE_BOSS_ROOM] = Region("Water Temple Boss Room", "Water Temple", {}, NO_DAY_NIGHT_CYCLE, {
diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp
index eec9d9248..c5ac0c02c 100644
--- a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp
@@ -265,7 +265,7 @@ void RegionTable_Init_Kakariko() {
Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return true;}),
});
- areaTable[RR_KAK_WELL] = Region("Kak Behind Gate", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
+ areaTable[RR_KAK_WELL] = Region("Kak Well", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_KAKARIKO_VILLAGE, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->DrainWell;}),
Entrance(RR_BOTTOM_OF_THE_WELL_ENTRYWAY, []{return logic->IsChild || (logic->DrainWell && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).IsNot(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF));}),
diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp
index 364dc074b..96875975f 100644
--- a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp
+++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp
@@ -105,7 +105,7 @@ void RegionTable_Init_LakeHylia() {
areaTable[RR_LH_LAB] = Region("LH Lab", "LH Lab", {}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
- LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))),
+ LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_BRONZE_SCALE))),
LOCATION(RC_LH_TRADE_FROG, logic->IsAdult && logic->CanUse(RG_EYEBALL_FROG)),
LOCATION(RC_LH_GS_LAB_CRATE, logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->CanBreakCrates()),
LOCATION(RC_LH_LAB_FRONT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)),
diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp
index ca52009ac..da20315a2 100644
--- a/soh/soh/Enhancements/randomizer/logic.cpp
+++ b/soh/soh/Enhancements/randomizer/logic.cpp
@@ -2328,8 +2328,10 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) {
inLogic[logicVal] = value;
}
-void Logic::Reset() {
- NewSaveContext();
+void Logic::Reset(bool resetSaveContext /*= true*/) {
+ if (resetSaveContext) {
+ NewSaveContext();
+ }
StartPerformanceTimer(PT_LOGIC_RESET);
memset(inLogic, false, sizeof(inLogic));
// Settings-dependent variables
@@ -2368,37 +2370,39 @@ void Logic::Reset() {
ShadowTrialClear = false;
LightTrialClear = false;
- // Ocarina C Buttons
- bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true);
- SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle);
- SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle);
- SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle);
- SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle);
- SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle);
-
- // Progressive Items
- SetUpgrade(UPG_STICKS, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).Is(true) ? 0 : 1);
- SetUpgrade(UPG_NUTS, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG).Is(true) ? 0 : 1);
-
- // If we're not shuffling swim, we start with it
- if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) {
- SetRandoInf(RAND_INF_CAN_SWIM, true);
- }
+ if (resetSaveContext) {
+ // Ocarina C Buttons
+ bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true);
+ SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle);
+ SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle);
+ SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle);
+ SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle);
+ SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle);
+
+ // Progressive Items
+ SetUpgrade(UPG_STICKS, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).Is(true) ? 0 : 1);
+ SetUpgrade(UPG_NUTS, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG).Is(true) ? 0 : 1);
+
+ // If we're not shuffling swim, we start with it
+ if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) {
+ SetRandoInf(RAND_INF_CAN_SWIM, true);
+ }
- // If we're not shuffling child's wallet, we start with it
- if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) {
- SetRandoInf(RAND_INF_HAS_WALLET, true);
- }
+ // If we're not shuffling child's wallet, we start with it
+ if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) {
+ SetRandoInf(RAND_INF_HAS_WALLET, true);
+ }
- // If we're not shuffling fishing pole, we start with it
- if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) {
- SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true);
- }
+ // If we're not shuffling fishing pole, we start with it
+ if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) {
+ SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true);
+ }
- // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in vanilla
- // FiT
- if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) {
- SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1);
+ // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in
+ // vanilla FiT
+ if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) {
+ SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1);
+ }
}
// Bottle Count
@@ -2450,7 +2454,9 @@ void Logic::Reset() {
// Other
AtDay = false;
AtNight = false;
- GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get();
+ if (resetSaveContext) {
+ GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get();
+ }
// Events
ShowedMidoSwordAndShield = false;
@@ -2515,6 +2521,8 @@ void Logic::Reset() {
Spirit1FSilverRupees = false;
JabuRutoIn1F = false;
+ CalculatingAvailableChecks = false;
+
StopPerformanceTimer(PT_LOGIC_RESET);
}
} // namespace Rando
diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h
index 19d7e173e..9a5c87751 100644
--- a/soh/soh/Enhancements/randomizer/logic.h
+++ b/soh/soh/Enhancements/randomizer/logic.h
@@ -177,6 +177,9 @@ class Logic {
/* --- END OF HELPERS AND LOCATION ACCESS --- */
+ bool CalculatingAvailableChecks = false;
+ bool ACProcessUndiscoveredExits = false;
+
SaveContext* mSaveContext = nullptr;
Logic();
bool CanUse(RandomizerGet itemName);
@@ -248,7 +251,7 @@ class Logic {
bool CanUseProjectile();
bool CanBuildRainbowBridge();
bool CanTriggerLACS();
- void Reset();
+ void Reset(bool resetSaveContext = true);
void SetContext(std::shared_ptr<Context> _ctx);
bool GetInLogic(LogicVal logicVal);
void SetInLogic(LogicVal logicVal, bool remove);
diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp
index 1e05bc60b..623aa01f9 100644
--- a/soh/soh/Enhancements/randomizer/randomizer.cpp
+++ b/soh/soh/Enhancements/randomizer/randomizer.cpp
@@ -365,10 +365,20 @@ std::unordered_map<s16, s16> getItemIdToItemId = {
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
if (strcmp(spoilerFileName, "") != 0) {
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
- if (!spoilerFileStream) {
- return false;
- } else {
- return true;
+ if (spoilerFileStream) {
+ nlohmann::json contents;
+ spoilerFileStream >> contents;
+ spoilerFileStream.close();
+ if (contents.contains("version") &&
+ strcmp(std::string(contents["version"]).c_str(), (char*)gBuildVersion) == 0) {
+ return true;
+ } else {
+ SohGui::RegisterPopup(
+ "Old Spoiler Version",
+ "The spoiler file located at\n" + std::string(spoilerFileName) +
+ "\nwas made by a version that doesn't match the currently running version.\n" +
+ "Loading for this file has been cancelled.");
+ }
}
}
diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h
index e5d82b18b..a0dd973b7 100644
--- a/soh/soh/Enhancements/randomizer/randomizerTypes.h
+++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h
@@ -574,6 +574,7 @@ typedef enum {
RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM,
RR_DEKU_TREE_BOSS_ENTRYWAY,
+ RR_DEKU_TREE_BOSS_EXIT,
RR_DEKU_TREE_BOSS_ROOM,
RR_DODONGOS_CAVERN_BEGINNING,
@@ -621,7 +622,9 @@ typedef enum {
RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH,
RR_DODONGOS_CAVERN_MQ_BACK_BEHIND_FIRE,
RR_DODONGOS_CAVERN_MQ_BACK_SWITCH_GRAVE,
+
RR_DODONGOS_CAVERN_BOSS_ENTRYWAY,
+ RR_DODONGOS_CAVERN_BOSS_EXIT,
RR_DODONGOS_CAVERN_BOSS_ROOM,
RR_JABU_JABUS_BELLY_BEGINNING,
@@ -650,6 +653,7 @@ typedef enum {
RR_JABU_JABUS_BELLY_MQ_EAST_ROOM,
RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY,
+ RR_JABU_JABUS_BELLY_BOSS_EXIT,
RR_JABU_JABUS_BELLY_BOSS_ROOM,
RR_FOREST_TEMPLE_FIRST_ROOM,
diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
index de5e87289..adaf03cea 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
+++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
@@ -585,6 +585,13 @@ void CheckTrackerLoadGame(int32_t fileNum) {
UpdateAllOrdering();
UpdateInventoryChecks();
UpdateFilters();
+
+ RegionTable_Init();
+
+ if (Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_ENTRANCES).Get()) {
+ Rando::Context::GetInstance()->GetEntranceShuffler()->ApplyEntranceOverrides();
+ }
+
RecalculateAvailableChecks();
}
@@ -903,7 +910,6 @@ void LoadFile() {
SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled, (uint32_t)0);
UpdateAllOrdering();
UpdateAllAreas();
- RegionTable_Init();
}
void Teardown() {
@@ -1495,6 +1501,27 @@ void LoadSettings() {
showOverworldFreestanding = false;
showDungeonFreestanding = true;
}
+
+ switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY)) {
+ case RO_GANON_BOSS_KEY_LACS_STONES:
+ Rando::Context::GetInstance()->LACSCondition(RO_LACS_STONES);
+ break;
+ case RO_GANON_BOSS_KEY_LACS_MEDALLIONS:
+ Rando::Context::GetInstance()->LACSCondition(RO_LACS_MEDALLIONS);
+ break;
+ case RO_GANON_BOSS_KEY_LACS_REWARDS:
+ Rando::Context::GetInstance()->LACSCondition(RO_LACS_REWARDS);
+ break;
+ case RO_GANON_BOSS_KEY_LACS_DUNGEONS:
+ Rando::Context::GetInstance()->LACSCondition(RO_LACS_DUNGEONS);
+ break;
+ case RO_GANON_BOSS_KEY_LACS_TOKENS:
+ Rando::Context::GetInstance()->LACSCondition(RO_LACS_TOKENS);
+ break;
+ default:
+ Rando::Context::GetInstance()->LACSCondition(RO_LACS_VANILLA);
+ break;
+ }
}
bool IsCheckShuffled(RandomizerCheck rc) {
@@ -1835,7 +1862,7 @@ void DrawLocation(RandomizerCheck rc) {
case RCSHOW_IDENTIFIED:
case RCSHOW_SEEN:
if (IS_RANDO) {
- if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery && !itemLoc->IsAddedToPool()) {
+ if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery) {
if (status == RCSHOW_IDENTIFIED) {
txt = OTRGlobals::Instance->gRandoContext->overrides[rc].GetTrickName().GetForLanguage(
gSaveContext.language);
@@ -1845,11 +1872,10 @@ void DrawLocation(RandomizerCheck rc) {
.GetName()
.GetForLanguage(gSaveContext.language);
}
- } else if (!mystery && !itemLoc->IsAddedToPool()) {
+ } else if (!mystery) {
txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language);
}
- if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery &&
- !itemLoc->IsAddedToPool()) {
+ if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery) {
auto price = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetPrice();
if (price) {
txt += fmt::format(" - {}", price);
@@ -1973,7 +1999,7 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName,
UIWidgets::PopStyleCombobox();
}
-void RecalculateAvailableChecks() {
+void RecalculateAvailableChecks(RandomizerRegion startingRegion /* = RR_ROOT */) {
if (!enableAvailableChecks) {
return;
}
@@ -1981,20 +2007,22 @@ void RecalculateAvailableChecks() {
ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
+ const auto& ctx = Rando::Context::GetInstance();
+
std::vector<RandomizerCheck> targetLocations;
targetLocations.reserve(RC_MAX);
for (auto& location : Rando::StaticData::GetLocationTable()) {
RandomizerCheck rc = location.GetRandomizerCheck();
- Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
+ Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc);
itemLocation->SetAvailable(false);
if (!itemLocation->HasObtained()) {
targetLocations.emplace_back(rc);
}
}
- std::vector<RandomizerCheck> availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true);
+ std::vector<RandomizerCheck> availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true, startingRegion);
for (auto& rc : availableChecks) {
- const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
+ const auto& itemLocation = ctx->GetItemLocation(rc);
itemLocation->SetAvailable(true);
}
@@ -2002,7 +2030,7 @@ void RecalculateAvailableChecks() {
for (auto& [rcArea, vec] : checksByArea) {
areaChecksAvailable[rcArea] = 0;
for (auto& rc : vec) {
- Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
+ Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc);
if (itemLocation->IsAvailable() && IsVisibleInCheckTracker(rc) && !IsCheckHidden(rc)) {
areaChecksAvailable[rcArea]++;
}
diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h
index 695899bab..38c38a39b 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h
+++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h
@@ -61,6 +61,6 @@ void UpdateAllOrdering();
void UpdateAllAreas();
void RecalculateAllAreaTotals();
void SpoilAreaFromCheck(RandomizerCheck rc);
-void RecalculateAvailableChecks();
+void RecalculateAvailableChecks(RandomizerRegion startingRegion = RR_ROOT);
void CheckTracker_LoadFromPreset(nlohmann::json info);
} // namespace CheckTracker
diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c
index 11d17e621..e32223124 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c
+++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c
@@ -253,7 +253,9 @@ void Entrance_Init(void) {
for (s16 i = 0; i < 4; i++) {
// Zero out the bit in the field which tells the game to keep playing
// background music for all four scene setups at each index
- gEntranceTable[override + i].field &= ~ENTRANCE_INFO_CONTINUE_BGM_FLAG;
+ if (override + i < ENTRANCE_TABLE_SIZE) {
+ gEntranceTable[override + i].field &= ~ENTRANCE_INFO_CONTINUE_BGM_FLAG;
+ }
}
}
}
@@ -809,6 +811,7 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) {
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
gSaveContext.ship.stats.entrancesDiscovered[idx] |= entranceBit;
+ CheckTracker_RecalculateAvailableChecks();
// Set reverse entrance when not decoupled
if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) {
diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp
index 884ade150..05a10a2b3 100644
--- a/soh/soh/Enhancements/randomizer/savefile.cpp
+++ b/soh/soh/Enhancements/randomizer/savefile.cpp
@@ -232,6 +232,8 @@ extern "C" void Randomizer_InitSaveFile() {
// Reset triforce pieces collected.
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected = 0;
+ SetStartingItems();
+
// Set Cutscene flags and texts to skip them.
Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO);
Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA);
@@ -430,6 +432,4 @@ extern "C" void Randomizer_InitSaveFile() {
gSaveContext.itemGetInf[3] |= 0x800; // Bunny Hood related
gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth
}
-
- SetStartingItems();
}
diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp
index 1c81e2d58..cd545ac56 100644
--- a/soh/soh/Enhancements/randomizer/settings.cpp
+++ b/soh/soh/Enhancements/randomizer/settings.cpp
@@ -1744,6 +1744,14 @@ TrickOption& Settings::GetTrickOption(const RandomizerTrick key) {
return mTrickOptions[key];
}
+int Settings::GetRandomizerTrickByName(const std::string& name) {
+ const auto& it = mTrickNameToEnum.find(name);
+ if (it == mTrickNameToEnum.end()) {
+ return -1;
+ }
+ return it->second;
+}
+
void Context::ResetTrickOptions() {
for (int count = 0; count < RT_MAX; count++) {
mTrickOptions[count].Set(0); // RANDOTODO this can probably be done better
diff --git a/soh/soh/Enhancements/randomizer/settings.h b/soh/soh/Enhancements/randomizer/settings.h
index c820a9df9..ed214471a 100644
--- a/soh/soh/Enhancements/randomizer/settings.h
+++ b/soh/soh/Enhancements/randomizer/settings.h
@@ -51,6 +51,14 @@ class Settings {
TrickOption& GetTrickOption(RandomizerTrick key);
/**
+ * @brief Get the RandomizerTrick corresponding to the provided name.
+ *
+ * @param name
+ * @return int RandomizerTrick index or -1 if not found
+ */
+ int GetRandomizerTrickByName(const std::string& name);
+
+ /**
* @brief Returns a reference to the entire array of options.
*
* @return const std::array<Option, RSK_MAX>&
diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp
index a2135006a..b8d9f4065 100644
--- a/soh/soh/OTRGlobals.cpp
+++ b/soh/soh/OTRGlobals.cpp
@@ -1151,6 +1151,13 @@ extern "C" void InitOTR() {
"Error", "SoH does not have proper file permissions. Please move it to a folder that does and run again.");
exit(1);
}
+ if (ownPath.string().find("OneDrive") != std::string::npos) {
+ Extractor::ShowErrorBox(
+ "Error",
+ "SoH appears to be in a OneDrive folder, which will cause issues. "
+ "Please move it to a folder outside of OneDrive, like the root of a drive (e.g. \"C:\\Games\\SoH\").");
+ exit(1);
+ }
#endif
#if not defined(__SWITCH__) && not defined(__WIIU__)
@@ -2643,4 +2650,7 @@ void SoH_ProcessDroppedFiles(std::string filePath) {
return;
}
}
-// #endregion
+
+extern "C" void CheckTracker_RecalculateAvailableChecks() {
+ CheckTracker::RecalculateAvailableChecks();
+}
diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h
index 2d1464e47..21316d19d 100644
--- a/soh/soh/OTRGlobals.h
+++ b/soh/soh/OTRGlobals.h
@@ -168,6 +168,7 @@ void Gfx_UnregisterBlendedTexture(const char* name);
void Gfx_TextureCacheDelete(const uint8_t* addr);
void SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose();
+void CheckTracker_RecalculateAvailableChecks();
GetItemID RetrieveGetItemIDFromItemID(ItemID itemID);
RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID);
diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp
index 0b0421fbe..57904fc84 100644
--- a/soh/soh/SaveManager.cpp
+++ b/soh/soh/SaveManager.cpp
@@ -119,10 +119,8 @@ SaveManager::SaveManager() {
AddLoadFunction("base", 4, LoadBaseVersion4);
AddSaveFunction("base", 4, SaveBase, true, SECTION_PARENT_NONE);
- AddLoadFunction("randomizer", 1, LoadRandomizerVersion1);
- AddLoadFunction("randomizer", 2, LoadRandomizerVersion2);
- AddLoadFunction("randomizer", 3, LoadRandomizerVersion3);
- AddSaveFunction("randomizer", 3, SaveRandomizer, true, SECTION_PARENT_NONE);
+ AddLoadFunction("randomizer", 1, LoadRandomizer);
+ AddSaveFunction("randomizer", 1, SaveRandomizer, true, SECTION_PARENT_NONE);
AddInitFunction(InitFileImpl);
@@ -157,234 +155,7 @@ SaveManager::SaveManager() {
}
}
-// RANDOTODO should we just have dummy functions that raise warnings instead if these aren't supported?
-void SaveManager::LoadRandomizerVersion1() {
- auto randoContext = Rando::Context::GetInstance();
- RandomizerCheck location = RC_UNKNOWN_CHECK;
- for (int i = 0; i < RC_MAX; i++) {
- SaveManager::Instance->LoadData("check" + std::to_string(i), location);
- SaveManager::Instance->LoadStruct("get" + std::to_string(i), [&]() {
- SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(location)->RefPlacedItem());
- if (randoContext->GetItemLocation(location)->GetPlacedRandomizerGet() == RG_ICE_TRAP) {
- randoContext->overrides[location].SetLocation(location);
- SaveManager::Instance->LoadData("fakeRgID", randoContext->overrides[location].RefLooksLike());
- SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().english);
- SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().french);
- }
- });
- }
-
- for (uint32_t i = 0; i < randoContext->hashIconIndexes.size(); i++) {
- SaveManager::Instance->LoadData("seed" + std::to_string(i), randoContext->hashIconIndexes[i]);
- }
-
- for (int i = 0; i < RSK_MAX; i++) {
- int key, value;
- SaveManager::Instance->LoadData("sk" + std::to_string(i), key);
- SaveManager::Instance->LoadData("sv" + std::to_string(i), value);
- randoContext->GetOption(RandomizerSettingKey(key)).Set(value);
- }
-
- for (int i = 0; i < 50; i++) {
- RandomizerCheck check;
- char hintText[200];
- SaveManager::Instance->LoadData("hc" + std::to_string(i), check);
- for (int j = 0; j < ARRAY_COUNT(hintText); j++) {
- SaveManager::Instance->LoadData("ht" + std::to_string(i) + "-" + std::to_string(j), hintText[j]);
- }
- RandomizerHint stoneHint = Rando::StaticData::oldVerHintOrder[i - Rando::StaticData::oldVerGossipStoneStart];
- randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) }));
- }
-
- char childAltarText[250];
- for (int i = 0; i < ARRAY_COUNT(childAltarText); i++) {
- SaveManager::Instance->LoadData("cat" + std::to_string(i), childAltarText[i]);
- }
- randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) }));
-
- char adultAltarText[750];
- for (int i = 0; i < ARRAY_COUNT(adultAltarText); i++) {
- SaveManager::Instance->LoadData("aat" + std::to_string(i), adultAltarText[i]);
- }
- randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) }));
-
- char ganonHintText[150];
- for (int i = 0; i < ARRAY_COUNT(ganonHintText); i++) {
- SaveManager::Instance->LoadData("ght" + std::to_string(i), ganonHintText[i]);
- }
- randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) }));
-
- char ganonText[250];
- for (int i = 0; i < ARRAY_COUNT(ganonText); i++) {
- SaveManager::Instance->LoadData("gt" + std::to_string(i), ganonText[i]);
- }
- randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) }));
-
- SaveManager::Instance->LoadData("triforcePiecesCollected",
- gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected);
-
- SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount);
-
- size_t merchantPricesSize = 0;
- if (randoContext->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_OFF)) {
- merchantPricesSize += NUM_SCRUBS;
- }
- if (randoContext->GetOption(RSK_SHOPSANITY).Is(RO_SHOPSANITY_OFF)) {
- merchantPricesSize += NUM_SHOP_ITEMS;
- }
-
- SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) {
- SaveManager::Instance->LoadStruct("", [&]() {
- RandomizerCheck rc;
- SaveManager::Instance->LoadData("check", rc);
- uint32_t price;
- SaveManager::Instance->LoadData("price", price);
- randoContext->GetItemLocation(rc)->SetCustomPrice(price);
- });
- });
-}
-
-// RANDOTODO if we actually support this, be less lazy
-void SaveManager::LoadRandomizerVersion2() {
- auto randoContext = Rando::Context::GetInstance();
- SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) {
- SaveManager::Instance->LoadStruct("", [&]() {
- SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(i)->RefPlacedItem());
- RandomizerGet rg = RG_NONE;
- SaveManager::Instance->LoadData("fakeRgID", rg, RG_NONE);
- if (rg != RG_NONE) {
- randoContext->overrides[static_cast<RandomizerCheck>(i)] =
- Rando::ItemOverride(static_cast<RandomizerCheck>(i), rg);
- SaveManager::Instance->LoadData(
- "trickName", randoContext->overrides[static_cast<RandomizerCheck>(i)].GetTrickName().english);
- SaveManager::Instance->LoadData(
- "trickName", randoContext->overrides[static_cast<RandomizerCheck>(i)].GetTrickName().french);
- }
- });
- });
-
- auto entranceCtx = randoContext->GetEntranceShuffler();
- SaveManager::Instance->LoadArray("entrances", ARRAY_COUNT(entranceCtx->entranceOverrides), [&](size_t i) {
- SaveManager::Instance->LoadStruct("", [&]() {
- SaveManager::Instance->LoadData("type", entranceCtx->entranceOverrides[i].type);
- SaveManager::Instance->LoadData("index", entranceCtx->entranceOverrides[i].index);
- SaveManager::Instance->LoadData("destination", entranceCtx->entranceOverrides[i].destination);
- SaveManager::Instance->LoadData("override", entranceCtx->entranceOverrides[i].override);
- SaveManager::Instance->LoadData("overrideDestination",
- entranceCtx->entranceOverrides[i].overrideDestination);
- });
- });
-
- SaveManager::Instance->LoadArray("seed", randoContext->hashIconIndexes.size(), [&](size_t i) {
- SaveManager::Instance->LoadData("", randoContext->hashIconIndexes[i]);
- });
-
- std::string inputSeed;
- SaveManager::Instance->LoadData("inputSeed", inputSeed);
- randoContext->SetSeedString(inputSeed);
-
- uint32_t finalSeed;
- SaveManager::Instance->LoadData("finalSeed", finalSeed);
- randoContext->SetSeed(finalSeed);
-
- SaveManager::Instance->LoadArray("randoSettings", RSK_MAX, [&](size_t i) {
- int value = 0;
- SaveManager::Instance->LoadData("", value);
- randoContext->GetOption(RandomizerSettingKey(i)).Set(value);
- });
-
- SaveManager::Instance->LoadArray("hintLocations", RH_ZR_OPEN_GROTTO_GOSSIP_STONE + 1, [&](size_t i) {
- SaveManager::Instance->LoadStruct("", [&]() {
- RandomizerCheck rc = RC_UNKNOWN_CHECK;
- SaveManager::Instance->LoadData("check", rc);
- if (rc != RC_UNKNOWN_CHECK) {
- std::string hintText;
- SaveManager::Instance->LoadData("hintText", hintText);
- RandomizerHint stoneHint =
- Rando::StaticData::oldVerHintOrder[rc - Rando::StaticData::oldVerGossipStoneStart];
- randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) }));
- }
- });
- });
-
- std::string childAltarText;
- SaveManager::Instance->LoadData("childAltarText", childAltarText);
- randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) }));
- std::string adultAltarText;
- SaveManager::Instance->LoadData("adultAltarText", adultAltarText);
- randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) }));
- std::string ganonHintText;
- SaveManager::Instance->LoadData("ganonHintText", ganonHintText);
- randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) }));
- std::string ganonText;
- SaveManager::Instance->LoadData("ganonText", ganonText);
- randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) }));
- std::string dampeText;
- SaveManager::Instance->LoadData("dampeText", dampeText);
- randoContext->AddHint(RH_DAMPES_DIARY, Rando::Hint(RH_DAMPES_DIARY, { CustomMessage(dampeText) }));
- std::string gregHintText;
- SaveManager::Instance->LoadData("gregHintText", gregHintText);
- randoContext->AddHint(RH_GREG_RUPEE, Rando::Hint(RH_GREG_RUPEE, { CustomMessage(gregHintText) }));
- std::string sheikText;
- SaveManager::Instance->LoadData("sheikText", sheikText);
- randoContext->AddHint(RH_SHEIK_HINT, Rando::Hint(RH_SHEIK_HINT, { CustomMessage(sheikText) }));
- std::string sariaText;
- SaveManager::Instance->LoadData("sariaText", sariaText);
- randoContext->AddHint(RH_SARIA_HINT, Rando::Hint(RH_SARIA_HINT, { CustomMessage(sariaText) }));
- std::string fishingPoleText;
- SaveManager::Instance->LoadData("fishingPoleText", fishingPoleText);
- randoContext->AddHint(RH_FISHING_POLE, Rando::Hint(RH_FISHING_POLE, { CustomMessage(fishingPoleText) }));
- std::string warpMinuetText;
- SaveManager::Instance->LoadData("warpMinuetText", warpMinuetText);
- randoContext->AddHint(RH_MINUET_WARP_LOC, Rando::Hint(RH_MINUET_WARP_LOC, { CustomMessage(warpMinuetText) }));
- std::string warpBoleroText;
- SaveManager::Instance->LoadData("warpBoleroText", warpBoleroText);
- randoContext->AddHint(RH_BOLERO_WARP_LOC, Rando::Hint(RH_BOLERO_WARP_LOC, { CustomMessage(warpBoleroText) }));
- std::string warpSerenadeText;
- SaveManager::Instance->LoadData("warpSerenadeText", warpSerenadeText);
- randoContext->AddHint(RH_SERENADE_WARP_LOC, Rando::Hint(RH_SERENADE_WARP_LOC, { CustomMessage(warpSerenadeText) }));
- std::string warpRequiemText;
- SaveManager::Instance->LoadData("warpRequiemText", warpRequiemText);
- randoContext->AddHint(RH_REQUIEM_WARP_LOC, Rando::Hint(RH_REQUIEM_WARP_LOC, { CustomMessage(warpRequiemText) }));
- std::string warpNocturneText;
- SaveManager::Instance->LoadData("warpNocturneText", warpNocturneText);
- randoContext->AddHint(RH_NOCTURNE_WARP_LOC, Rando::Hint(RH_NOCTURNE_WARP_LOC, { CustomMessage(warpNocturneText) }));
- std::string warpPreludeText;
- SaveManager::Instance->LoadData("warpPreludeText", warpPreludeText);
- randoContext->AddHint(RH_PRELUDE_WARP_LOC, Rando::Hint(RH_PRELUDE_WARP_LOC, { CustomMessage(warpPreludeText) }));
-
- SaveManager::Instance->LoadData("triforcePiecesCollected",
- gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected);
-
- SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount);
-
- std::shared_ptr<Randomizer> randomizer = OTRGlobals::Instance->gRandomizer;
-
- size_t merchantPricesSize = 0;
- SaveManager::Instance->LoadData("merchantPricesSize", merchantPricesSize);
-
- SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) {
- SaveManager::Instance->LoadStruct("", [&]() {
- RandomizerCheck rc;
- SaveManager::Instance->LoadData("check", rc);
- uint32_t price;
- SaveManager::Instance->LoadData("price", price);
- randoContext->GetItemLocation(rc)->SetCustomPrice(price);
- });
- });
-
- size_t mqDungeonCount;
- SaveManager::Instance->LoadData("masterQuestDungeonCount", mqDungeonCount, (size_t)0);
-
- randoContext->GetDungeons()->ClearAllMQ();
- SaveManager::Instance->LoadArray("masterQuestDungeons", mqDungeonCount, [&](size_t i) {
- uint16_t scene;
- SaveManager::Instance->LoadData("", scene);
- randoContext->GetDungeons()->GetDungeonFromScene(SceneID(scene))->SetMQ();
- });
-}
-
-void SaveManager::LoadRandomizerVersion3() {
+void SaveManager::LoadRandomizer() {
auto randoContext = Rando::Context::GetInstance();
SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) {
SaveManager::Instance->LoadStruct("", [&]() {
@@ -1355,8 +1126,57 @@ void SaveManager::LoadFile(int fileNum) {
switch (saveBlock["version"].get<int>()) {
case 1:
for (auto& block : saveBlock["sections"].items()) {
- int sectionVersion = block.value()["version"];
std::string sectionName = block.key();
+ if (sectionName == "randomizer") {
+ bool hasStats = saveBlock["sections"].contains("sohStats");
+ if (block.value()["data"].contains("aat0") || !hasStats) { // Rachael rando data
+ SohGui::RegisterPopup(
+ "Loading old file",
+ "The file in slot " + std::to_string(fileNum + 1) +
+ " appears to contain randomizer data, but is a very old format.\n" +
+ "The randomizer data has been removed, and this file will be treated as a vanilla "
+ "file.\n" +
+ "If this was a randomizer file, the file will not work, and should be deleted.");
+ input.close();
+ saveMtx.unlock();
+ SaveFile(fileNum);
+ return;
+ }
+ s16 major = saveBlock["sections"]["sohStats"]["data"]["buildVersionMajor"];
+ s16 minor = saveBlock["sections"]["sohStats"]["data"]["buildVersionMinor"];
+ s16 patch = saveBlock["sections"]["sohStats"]["data"]["buildVersionPatch"];
+ // block loading outdated rando save
+ if (!(major == gBuildVersionMajor && minor == gBuildVersionMinor &&
+ patch == gBuildVersionPatch)) {
+ input.close();
+ std::string newFileName = Ship::Context::GetPathRelativeToAppDirectory("Save") +
+ ("/file" + std::to_string(fileNum + 1) + "-" +
+ std::to_string(GetUnixTimestamp()) + ".bak");
+ std::filesystem::path newFile(newFileName);
+
+#if defined(__SWITCH__) || defined(__WIIU__)
+ copy_file(fileName.c_str(), newFile.c_str());
+#else
+ std::filesystem::copy_file(fileName, newFile);
+#endif
+
+ std::filesystem::remove(fileName);
+ SohGui::RegisterPopup(
+ "Outdated Randomizer Save",
+ "The SoH version in the file in slot " + std::to_string(fileNum + 1) +
+ " does not match the currently running version.\n" +
+ "Non-matching rando saves are unsupported, and the file has been renamed to\n" +
+ " " + newFileName + "\n" +
+ "If this was not in error, the file should be deleted.");
+ saveMtx.unlock();
+ SaveFile(fileNum);
+ return;
+ }
+ }
+ int sectionVersion = block.value()["version"];
+ if (sectionName == "randomizer" && sectionVersion != 1) {
+ sectionVersion = 1;
+ }
if (!sectionLoadHandlers.contains(sectionName)) {
// Unloadable sections aren't necessarily errors, they are probably mods that were unloaded
// TODO report in a more noticeable manner
diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h
index 3c4a31243..6d7ef3b33 100644
--- a/soh/soh/SaveManager.h
+++ b/soh/soh/SaveManager.h
@@ -166,11 +166,7 @@ class SaveManager {
static void InitFileDebug();
static void InitFileMaxed();
- static void LoadRandomizerVersion1();
- static void LoadRandomizerVersion2();
- static void LoadRandomizerVersion3();
- static void LoadTrackerData();
- static void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave);
+ static void LoadRandomizer();
static void SaveRandomizer(SaveContext* saveContext, int sectionID, bool fullSave);
static void LoadBaseVersion1();
diff --git a/soh/soh/SohGui/Menu.cpp b/soh/soh/SohGui/Menu.cpp
index 87c362496..5a609a935 100644
--- a/soh/soh/SohGui/Menu.cpp
+++ b/soh/soh/SohGui/Menu.cpp
@@ -185,33 +185,35 @@ bool ModernMenuHeaderEntry(std::string label) {
}
uint32_t Menu::DrawSearchResults(std::string& menuSearchText) {
- ImGui::BeginChild("Search Results");
int searchCount = 0;
- for (auto& menuLabel : menuOrder) {
- auto& menuEntry = menuEntries.at(menuLabel);
- for (auto& sidebarLabel : menuEntry.sidebarOrder) {
- auto& sidebar = menuEntry.sidebars[sidebarLabel];
- for (int i = 0; i < sidebar.columnWidgets.size(); i++) {
- auto& column = sidebar.columnWidgets.at(i);
- for (auto& info : column) {
- if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR ||
- info.type == WIDGET_SEPARATOR_TEXT || info.isHidden) {
- continue;
- }
- const char* tooltip = info.options->tooltip;
- std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : "");
- std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower);
- menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '),
- menuSearchText.end());
- std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower);
- widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end());
- if (widgetStr.find(menuSearchText) != std::string::npos) {
- MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex);
- ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray));
- std::string origin = fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1);
- ImGui::Text("%s", origin.c_str());
- ImGui::PopStyleColor();
- searchCount++;
+ if (ImGui::BeginChild("Search Results")) {
+ for (auto& menuLabel : menuOrder) {
+ auto& menuEntry = menuEntries.at(menuLabel);
+ for (auto& sidebarLabel : menuEntry.sidebarOrder) {
+ auto& sidebar = menuEntry.sidebars[sidebarLabel];
+ for (int i = 0; i < sidebar.columnWidgets.size(); i++) {
+ auto& column = sidebar.columnWidgets.at(i);
+ for (auto& info : column) {
+ if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR ||
+ info.type == WIDGET_SEPARATOR_TEXT || info.isHidden) {
+ continue;
+ }
+ const char* tooltip = info.options->tooltip;
+ std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : "");
+ std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower);
+ menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '),
+ menuSearchText.end());
+ std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower);
+ widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end());
+ if (widgetStr.find(menuSearchText) != std::string::npos) {
+ MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex);
+ ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray));
+ std::string origin =
+ fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1);
+ ImGui::Text("%s", origin.c_str());
+ ImGui::PopStyleColor();
+ searchCount++;
+ }
}
}
}
diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp
index b3b6b5c7a..374e2f602 100644
--- a/soh/soh/util.cpp
+++ b/soh/soh/util.cpp
@@ -371,17 +371,6 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size
}
std::string SohUtils::Sanitize(std::string stringValue) {
- // Add backslashes.
- for (auto i = stringValue.begin();;) {
- auto const pos =
- std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
- if (pos == stringValue.end()) {
- break;
- }
- i = std::next(stringValue.insert(pos, '\\'), 2);
- }
-
- // Removes others.
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(),
[](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }),
stringValue.end());
diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c
index 81542eaad..a11180a7b 100644
--- a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c
+++ b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c
@@ -131,8 +131,14 @@ void func_808BAF40(BgTokiSwd* this, PlayState* play) {
Item_Give(play, ITEM_SWORD_MASTER);
}
play->csCtx.segment = D_808BB2F0;
+
+ // Discover adult spawn
+ Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false);
} else {
play->csCtx.segment = D_808BB7A0;
+
+ // Discover child spawn
+ Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false);
}
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP);
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_MASTER_SWORD);
diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c
index 50ac7e11b..78f75dabd 100644
--- a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c
+++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c
@@ -303,7 +303,7 @@ void EnMb_Init(Actor* thisx, PlayState* play) {
relYawFromPlayer =
this->actor.world.rot.y - Math_Vec3f_Yaw(&this->actor.world.pos, &player->actor.world.pos);
- if (ABS(relYawFromPlayer) > 0x4000) {
+ if (ABS(relYawFromPlayer) > 0x4000 && !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0)) {
this->actor.world.rot.y = thisx->world.rot.y + 0x8000;
this->actor.shape.rot.y = thisx->world.rot.y;
this->actor.world.pos.z = thisx->world.pos.z + 600.0f;
diff --git a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c
index 3797adafe..831e11b8c 100644
--- a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c
+++ b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c
@@ -507,7 +507,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) {
this->collider2.base.ocFlags1 &= ~OC1_HIT;
// "catch"
osSyncPrintf(VT_FGCOL(GREEN) "キャッチ(%d)!!" VT_RST "\n", this->frameCount);
- if (play->grabPlayer(play, player)) {
+ if (GameInteractor_Should(VB_LIKE_LIKE_GRAB_PLAYER, true, this) && play->grabPlayer(play, player)) {
player->actor.parent = &this->actor;
this->stopScroll = false;
EnRr_SetupGrabPlayer(this, player);
diff --git a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c
index 4925e73cb..ceca597df 100644
--- a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c
+++ b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c
@@ -706,7 +706,7 @@ void EnTorch2_Update(Actor* thisx, PlayState* play2) {
sStaggerCount = 0;
}
}
- if (player->linearVelocity == -18.0f) {
+ if (GameInteractor_Should(VB_TORCH2_HANDLE_CLANKING, player->linearVelocity == -18.0f, this)) {
if (this->actor.xzDistToPlayer > 80.0f) {
player->linearVelocity = 1.2f;
} else if (this->actor.xzDistToPlayer < 70.0f) {
diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c
index 6cc57ca22..4ae6fb744 100644
--- a/soh/src/overlays/actors/ovl_player_actor/z_player.c
+++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c
@@ -4764,7 +4764,9 @@ s32 func_808382DC(Player* this, PlayState* play) {
gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawnInfo->yaw;
}
- Play_TriggerVoidOut(play);
+ if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
+ Play_TriggerVoidOut(play);
+ }
}
Player_PlayVoiceSfx(this, NA_SE_VO_LI_TAKEN_AWAY);
@@ -5129,7 +5131,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol
}
if (exitIndex == 0) {
- Play_TriggerVoidOut(play);
+ if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
+ Play_TriggerVoidOut(play);
+ }
Scene_SetTransitionForNextEntrance(play);
} else {
play->nextEntranceIndex = play->setupExitList[exitIndex - 1];
@@ -5163,7 +5167,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol
SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2,
play->setupExitList[exitIndex - 1])) {
gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = play->nextEntranceIndex;
- Play_TriggerVoidOut(play);
+ if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
+ Play_TriggerVoidOut(play);
+ }
gSaveContext.respawnFlag = -2;
}
gSaveContext.retainWeatherMode = 1;
@@ -5226,7 +5232,7 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol
if (this->actor.bgCheckFlags & 1) {
if (this->floorProperty == 5) {
Play_TriggerRespawn(play);
- } else {
+ } else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
Play_TriggerVoidOut(play);
}
play->transitionType = TRANS_TYPE_FADE_BLACK_FAST;
@@ -9584,6 +9590,10 @@ static FallImpactInfo D_80854600[] = {
s32 func_80843E64(PlayState* play, Player* this) {
s32 sp34;
+ if (!GameInteractor_Should(VB_RECIEVE_FALL_DAMAGE, true, this)) {
+ return 0;
+ }
+
if ((sFloorType == 6) || (sFloorType == 9)) {
sp34 = 0;
} else {
@@ -14977,7 +14987,7 @@ void Player_Action_8084F88C(Player* this, PlayState* play) {
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) {
Grotto_ForceRegularVoidOut();
}
- } else {
+ } else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
Play_TriggerVoidOut(play);
}
diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
index 2b6e9fd5d..b715b488e 100644
--- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
+++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
@@ -26,6 +26,7 @@
#include "soh/SaveManager.h"
#include "soh/OTRGlobals.h"
#include "soh/ResourceManagerHelpers.h"
+#include "soh/ShipUtils.h"
typedef struct {
s16 left;
@@ -1053,20 +1054,27 @@ void FileChoose_UpdateRandomizer() {
if (!SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) &&
!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) {
CVarSetString(CVAR_GENERAL("SpoilerLog"), "");
+ Randomizer_SetSpoilerLoaded(false);
}
if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0 ||
!(Randomizer_IsSeedGenerated() || Randomizer_IsSpoilerLoaded()) &&
SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) && !fileSelectSpoilerFileLoaded) {
if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0) {
- CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""));
- Audio_PlayFanfare(NA_BGM_HORSE_GOAL);
+ if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""))) {
+ CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""));
+ Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME);
+ } else {
+ Sfx_PlaySfxCentered(NA_SE_SY_ERROR);
+ }
}
const char* fileLoc = CVarGetString(CVAR_GENERAL("SpoilerLog"), "");
CVarSetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0);
CVarSetString(CVAR_GENERAL("RandomizerDroppedFile"), "");
- Randomizer_ParseSpoiler(fileLoc);
- fileSelectSpoilerFileLoaded = true;
+ if (!Ship_IsCStringEmpty(fileLoc)) {
+ Randomizer_ParseSpoiler(fileLoc);
+ fileSelectSpoilerFileLoaded = true;
+ }
if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) {