Merge branch 'develop-blair' into jabu-mouth-qol

This commit is contained in:
JordanLongstaff 2025-06-10 12:12:50 -04:00
commit 1f93a2f596
57 changed files with 1112 additions and 576 deletions

View file

@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.26.0 FATAL_ERROR)
set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE) set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE)
set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")

View file

@ -4,6 +4,7 @@ set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE)
project(soh LANGUAGES C CXX) project(soh LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use")
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
enable_language(OBJCXX) enable_language(OBJCXX)

View file

@ -15,7 +15,7 @@ extern "C"
#include <soh/Enhancements/randomizer/randomizer_inf.h> #include <soh/Enhancements/randomizer/randomizer_inf.h>
#if defined(INCLUDE_GAME_PRINTF) && defined(_DEBUG) #if defined(INCLUDE_GAME_PRINTF) && defined(_DEBUG)
#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, __VA_ARGS__) #define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, ##__VA_ARGS__)
#else #else
#define osSyncPrintf(fmt, ...) osSyncPrintfUnused(fmt, ##__VA_ARGS__) #define osSyncPrintf(fmt, ...) osSyncPrintfUnused(fmt, ##__VA_ARGS__)
#endif #endif

View file

@ -265,6 +265,9 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN
} }
} }
auto playingFromMenu = CVarGetInteger(CVAR_AUDIO("Playing"), 0);
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
// Longest text in Audio Editor // Longest text in Audio Editor
ImVec2 columnSize = ImGui::CalcTextSize("Navi - Look/Hey/Watchout (Target Enemy)"); ImVec2 columnSize = ImGui::CalcTextSize("Navi - Look/Hey/Watchout (Target Enemy)");
ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit); ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit);
@ -291,10 +294,13 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN
const std::string lockedButton = ICON_FA_LOCK + hiddenKey; const std::string lockedButton = ICON_FA_LOCK + hiddenKey;
const std::string unlockedButton = ICON_FA_UNLOCK + hiddenKey; const std::string unlockedButton = ICON_FA_UNLOCK + hiddenKey;
const int currentValue = CVarGetInteger(cvarKey.c_str(), defaultValue); const int currentValue = CVarGetInteger(cvarKey.c_str(), defaultValue);
const bool isCurrentlyPlaying = currentValue == playingFromMenu || seqData.sequenceId == currentBGM;
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s", seqData.label.c_str()); ImGui::TextColored(
UIWidgets::ColorValues.at(isCurrentlyPlaying ? UIWidgets::Colors::Yellow : UIWidgets::Colors::White), "%s",
seqData.label.c_str());
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::PushItemWidth(-FLT_MIN); ImGui::PushItemWidth(-FLT_MIN);
const int initialValue = map.contains(currentValue) ? currentValue : defaultValue; const int initialValue = map.contains(currentValue) ? currentValue : defaultValue;

View file

@ -11,6 +11,7 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/cosmetics/CosmeticsEditor.h" #include "soh/Enhancements/cosmetics/CosmeticsEditor.h"
#include "soh/Enhancements/audio/AudioEditor.h" #include "soh/Enhancements/audio/AudioEditor.h"
#include "soh/Enhancements/randomizer/logic.h"
#define Path _Path #define Path _Path
#define PATH_HACK #define PATH_HACK
@ -1450,6 +1451,55 @@ static bool SfxHandler(std::shared_ptr<Ship::Console> Console, const std::vector
return 0; 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) { void DebugConsole_Init(void) {
// Console // Console
CMD_REGISTER("file_select", { FileSelectHandler, "Returns to the file select." }); CMD_REGISTER("file_select", { FileSelectHandler, "Returns to the file select." });
@ -1708,5 +1758,15 @@ void DebugConsole_Init(void) {
{ "group_name", Ship::ArgumentType::TEXT, true }, { "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(); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
} }

View file

@ -11,8 +11,13 @@
extern "C" { extern "C" {
#include <z64.h> #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)
const char* enemyCVarList[] = { const char* enemyCVarList[] = {
CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"), CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"),
CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"), CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"),
@ -270,15 +275,7 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* po
// Get randomized enemy ID and parameter. // Get randomized enemy ID and parameter.
uint32_t seed = uint32_t seed =
play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params; play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params;
EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed); EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed, play);
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++;
}
*actorId = randomEnemy.id; *actorId = randomEnemy.id;
*params = randomEnemy.params; *params = randomEnemy.params;
@ -334,19 +331,28 @@ void GetSelectedEnemies() {
} }
} }
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed) { EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play) {
std::vector<EnemyEntry> filteredEnemyList = {};
if (selectedEnemyList.size() == 0) { if (selectedEnemyList.size() == 0) {
GetSelectedEnemies(); 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 = uint32_t finalSeed =
seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt); seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt);
Random_Init(finalSeed); Random_Init(finalSeed);
uint32_t randomNumber = Random(0, selectedEnemyList.size()); uint32_t randomNumber = Random(0, filteredEnemyList.size());
return selectedEnemyList[randomNumber]; return filteredEnemyList[randomNumber];
} else { } else {
uint32_t randomSelectedEnemy = Random(0, selectedEnemyList.size()); uint32_t randomSelectedEnemy = Random(0, filteredEnemyList.size());
return selectedEnemyList[randomSelectedEnemy]; return filteredEnemyList[randomSelectedEnemy];
} }
} }
@ -532,3 +538,45 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
return 1; return 1;
} }
} }
void RegisterEnemyRandomizer() {
// 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 });

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <libultraship/bridge.h> #include <libultraship/bridge.h>
#include "item-tables/ItemTableTypes.h"
typedef struct EnemyEntry { typedef struct EnemyEntry {
int16_t id; int16_t id;
@ -11,7 +12,7 @@ typedef struct EnemyEntry {
bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX); 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); bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy);
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed); EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play);
extern const char* enemyCVarList[]; extern const char* enemyCVarList[];
extern const char* enemyNameList[]; extern const char* enemyNameList[];

View file

@ -474,6 +474,14 @@ typedef enum {
// - `*BgHeavyBlock` // - `*BgHeavyBlock`
VB_FREEZE_LINK_FOR_BLOCK_THROW, VB_FREEZE_LINK_FOR_BLOCK_THROW,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - None
VB_FREEZE_LINK_FOR_FOREST_PILLARS,
// #### `result` // #### `result`
// ```c // ```c
// true // true
@ -1358,6 +1366,14 @@ typedef enum {
// - `*BgTreemouth` // - `*BgTreemouth`
VB_PLAY_DEKU_TREE_INTRO_CS, VB_PLAY_DEKU_TREE_INTRO_CS,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*DemoKekkai`
VB_PLAY_DISPEL_BARRIER_CS,
// #### `result` // #### `result`
// ```c // ```c
// true // true
@ -1422,6 +1438,15 @@ typedef enum {
// - None // - None
VB_PLAY_FIRE_ARROW_CS, VB_PLAY_FIRE_ARROW_CS,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*EnHeishi2`
// - `bool` (clearCamera - true if the code clears a sub-camera, false otherwise)
VB_PLAY_GATE_OPENING_OR_CLOSING_CS,
// #### `result` // #### `result`
// ```c // ```c
// true // true
@ -1986,6 +2011,38 @@ typedef enum {
// #### `args` // #### `args`
// - `*EnWonderTalk2` // - `*EnWonderTalk2`
VB_WONDER_TALK, 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; } GIVanillaBehavior;
#endif #endif

View file

@ -99,6 +99,13 @@ void SwitchAge() {
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK_FAST; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK_FAST;
gPlayState->linkAgeOnLoad ^= 1; 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; static HOOK_ID hookId = 0;
hookId = REGISTER_VB_SHOULD(VB_INFLICT_VOID_DAMAGE, { hookId = REGISTER_VB_SHOULD(VB_INFLICT_VOID_DAMAGE, {
*should = false; *should = false;

View file

@ -209,6 +209,13 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer
bool stopOnBeatable = false, bool addToPlaythrough = false) { bool stopOnBeatable = false, bool addToPlaythrough = false) {
auto ctx = Rando::Context::GetInstance(); auto ctx = Rando::Context::GetInstance();
for (auto& exit : region->exits) { 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(); Region* exitRegion = exit.GetConnectedRegion();
// Update Time of Day Access for the exit // Update Time of Day Access for the exit
if (UpdateToDAccess(&exit, exitRegion)) { if (UpdateToDAccess(&exit, exitRegion)) {
@ -421,18 +428,13 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals
Rando::ItemLocation* location = ctx->GetItemLocation(loc); Rando::ItemLocation* location = ctx->GetItemLocation(loc);
RandomizerGet locItem = location->GetPlacedRandomizerGet(); RandomizerGet locItem = location->GetPlacedRandomizerGet();
if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) { if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, logic->CalculatingAvailableChecks)) {
if (gals.calculatingAvailableChecks) {
gals.accessibleLocations.push_back(loc);
StopPerformanceTimer(PT_LOCATION_LOGIC);
return false;
}
location->AddToPool(); location->AddToPool();
if (locItem == RG_NONE) { if (locItem == RG_NONE || logic->CalculatingAvailableChecks) {
gals.accessibleLocations.push_back(loc); // Empty location, consider for placement 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 // 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 // This is necessary due to the below preprocessing for playthrough generation
if (ignore != RG_NONE) { if (ignore != RG_NONE) {
@ -528,11 +530,32 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize
// Return any of the targetLocations that are accessible in logic // Return any of the targetLocations that are accessible in logic
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& targetLocations, std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& targetLocations,
RandomizerGet ignore /* = RG_NONE*/, RandomizerGet ignore /* = RG_NONE*/,
bool calculatingAvailableChecks /* = false */) { bool calculatingAvailableChecks /* = false */,
RandomizerRegion startingRegion /* = RR_ROOT */) {
auto ctx = Rando::Context::GetInstance(); auto ctx = Rando::Context::GetInstance();
GetAccessibleLocationsStruct gals(0); GetAccessibleLocationsStruct gals(0);
gals.calculatingAvailableChecks = calculatingAvailableChecks;
ResetLogic(ctx, gals, !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 { do {
gals.InitLoop(); gals.InitLoop();
for (size_t i = 0; i < gals.regionPool.size(); i++) { for (size_t i = 0; i < gals.regionPool.size(); i++) {

View file

@ -34,8 +34,6 @@ struct GetAccessibleLocationsStruct {
std::vector<RandomizerCheck> itemSphere; std::vector<RandomizerCheck> itemSphere;
std::list<Rando::Entrance*> entranceSphere; std::list<Rando::Entrance*> entranceSphere;
bool calculatingAvailableChecks = false;
GetAccessibleLocationsStruct(int _maxGsCount){ GetAccessibleLocationsStruct(int _maxGsCount){
regionPool = {RR_ROOT}; regionPool = {RR_ROOT};
gsCount = 0; gsCount = 0;
@ -58,13 +56,14 @@ struct GetAccessibleLocationsStruct {
void ClearProgress(); void ClearProgress();
void VanillaFill(); void VanillaFill();
int Fill(); int Fill();
void SetAreas();
std::vector<RandomizerCheck> GetEmptyLocations(std::vector<RandomizerCheck> allowedLocations); std::vector<RandomizerCheck> GetEmptyLocations(std::vector<RandomizerCheck> allowedLocations);
void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, RandomizerGet ignore = RG_NONE, void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, RandomizerGet ignore = RG_NONE,
bool stopOnBeatable = false, bool addToPlaythrough = false); bool stopOnBeatable = false, bool addToPlaythrough = false);
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false); std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false, RandomizerRegion startingRegion=RR_ROOT);
void GeneratePlaythrough(); void GeneratePlaythrough();

View file

@ -2412,13 +2412,13 @@ void StaticData::HintTable_Init() {
hintTextTable[RHT_ISOLATED_PLACE] = HintText(CustomMessage("an Isolated Place")); hintTextTable[RHT_ISOLATED_PLACE] = HintText(CustomMessage("an Isolated Place"));
hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage(" It's ordinary.", hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage("&It's %gordinary%w.",
/*german*/ "&Sieht aus wie immer.", /*german*/ "&Sieht aus %gwie immer%w.",
/*french*/ "&Elle vous semble %rordinaire%w.")); /*french*/ "&Elle vous semble %gordinaire%w."));
hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage(" It's masterful!", hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage("&It's %rmasterful%w!",
/*german*/ "&Man kann darauf die Worte&%r\"Master Quest\"%w entziffern...", /*german*/ "&Man kann darauf die Worte %r\"Master_Quest\"%w entziffern...",
/*french*/ "&Étrange... les mots %r\"Master&Quest\"%w sont gravés dessus.")); /*french*/ "&Étrange... les mots %r\"Master_Quest\"%w sont gravés dessus."));
// clang-format on // clang-format on
} }

View file

@ -362,7 +362,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_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_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_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_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_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)); 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));

View file

@ -400,8 +400,9 @@ void Context::ParseSpoiler(const char* spoilerFileName) {
ParseHashIconIndexesJson(spoilerFileJson); ParseHashIconIndexesJson(spoilerFileJson);
Rando::Settings::GetInstance()->ParseJson(spoilerFileJson); Rando::Settings::GetInstance()->ParseJson(spoilerFileJson);
ParseItemLocationsJson(spoilerFileJson); ParseItemLocationsJson(spoilerFileJson);
ParseHintJson(spoilerFileJson); ParseTricksJson(spoilerFileJson);
mEntranceShuffler->ParseJson(spoilerFileJson); mEntranceShuffler->ParseJson(spoilerFileJson);
ParseHintJson(spoilerFileJson);
mDungeons->ParseJson(spoilerFileJson); mDungeons->ParseJson(spoilerFileJson);
mTrials->ParseJson(spoilerFileJson); mTrials->ParseJson(spoilerFileJson);
mSpoilerLoaded = true; mSpoilerLoaded = true;
@ -469,6 +470,17 @@ void Context::ParseHintJson(nlohmann::json spoilerFileJson) {
CreateStaticHints(); 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() { std::shared_ptr<EntranceShuffler> Context::GetEntranceShuffler() {
return mEntranceShuffler; return mEntranceShuffler;
} }
@ -524,6 +536,10 @@ RandoOptionLACSCondition Context::LACSCondition() const {
return mLACSCondition; return mLACSCondition;
} }
void Context::LACSCondition(RandoOptionLACSCondition lacsCondition) {
mLACSCondition = lacsCondition;
}
std::shared_ptr<Kaleido> Context::GetKaleido() { std::shared_ptr<Kaleido> Context::GetKaleido() {
if (mKaleido == nullptr) { if (mKaleido == nullptr) {
mKaleido = std::make_shared<Kaleido>(); mKaleido = std::make_shared<Kaleido>();

View file

@ -104,12 +104,22 @@ class Context {
* @return RandoOptionLACSCondition * @return RandoOptionLACSCondition
*/ */
RandoOptionLACSCondition LACSCondition() const; 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); GetItemEntry GetFinalGIEntry(RandomizerCheck rc, bool checkObtainability = true, GetItemID ogItemId = GI_NONE);
void ParseSpoiler(const char* spoilerFileName); void ParseSpoiler(const char* spoilerFileName);
void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson); void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson);
void ParseItemLocationsJson(nlohmann::json spoilerFileJson); void ParseItemLocationsJson(nlohmann::json spoilerFileJson);
void WriteHintJson(nlohmann::ordered_json& spoilerFileJson); void WriteHintJson(nlohmann::ordered_json& spoilerFileJson);
void ParseHintJson(nlohmann::json spoilerFileJson); void ParseHintJson(nlohmann::json spoilerFileJson);
void ParseTricksJson(nlohmann::json spoilerFileJson);
std::map<RandomizerCheck, ItemOverride> overrides = {}; std::map<RandomizerCheck, ItemOverride> overrides = {};
std::vector<std::vector<RandomizerCheck>> playthroughLocations = {}; std::vector<std::vector<RandomizerCheck>> playthroughLocations = {};
std::vector<RandomizerCheck> everyPossibleLocation = {}; std::vector<RandomizerCheck> everyPossibleLocation = {};

View file

@ -250,7 +250,319 @@ std::string EntranceNameByRegions(RandomizerRegion parentRegion, RandomizerRegio
return RegionTable(parentRegion)->regionName + " -> " + RegionTable(connectedRegion)->regionName; 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(); auto ctx = Rando::Context::GetInstance();
for (auto& entrancePair : entranceShuffleTable) { for (auto& entrancePair : entranceShuffleTable) {
@ -262,6 +574,7 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
forwardEntrance->SetIndex(forwardEntry.index); forwardEntrance->SetIndex(forwardEntry.index);
forwardEntrance->SetType(forwardEntry.type); forwardEntrance->SetType(forwardEntry.type);
forwardEntrance->SetAsPrimary(); forwardEntrance->SetAsPrimary();
entranceMap[forwardEntry.index] = forwardEntrance;
// When decouple entrances is on, mark the forward entrance // When decouple entrances is on, mark the forward entrance
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
@ -273,6 +586,7 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
returnEntrance->SetIndex(returnEntry.index); returnEntrance->SetIndex(returnEntry.index);
returnEntrance->SetType(returnEntry.type); returnEntrance->SetType(returnEntry.type);
forwardEntrance->BindTwoWay(returnEntrance); forwardEntrance->BindTwoWay(returnEntrance);
entranceMap[returnEntry.index] = returnEntrance;
// Mark reverse entrance as decoupled // Mark reverse entrance as decoupled
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
@ -857,316 +1171,6 @@ int EntranceShuffler::ShuffleAllEntrances() {
mTotalRandomizableEntrances = 0; mTotalRandomizableEntrances = 0;
mCurNumRandomizedEntrances = 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 = { std::map<std::string, PriorityEntrance> priorityEntranceTable = {
{ "Bolero", { { RR_DMC_CENTRAL_LOCAL }, { EntranceType::OwlDrop, EntranceType::WarpSong } } }, { "Bolero", { { RR_DMC_CENTRAL_LOCAL }, { EntranceType::OwlDrop, EntranceType::WarpSong } } },
{ "Nocturne", { "Nocturne",
@ -1178,7 +1182,7 @@ int EntranceShuffler::ShuffleAllEntrances() {
}; };
mEntranceShuffleFailure = false; mEntranceShuffleFailure = false;
SetAllEntrancesData(entranceShuffleTable); SetAllEntrancesData();
EntrancePools oneWayEntrancePools = {}; EntrancePools oneWayEntrancePools = {};
EntrancePools entrancePools = {}; EntrancePools entrancePools = {};
@ -1488,11 +1492,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
if (true /* ctx->GetOption(RSK_SHUFFLE_BLUEWARP_ENTRANCES).Is(RO_BLUEWARP_ENTRANCE_SHUFFLE_DUNGEON) */) { 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 // 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 = { 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(EntranceNameByRegions(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE)) }, GetEntrance(EntranceNameByRegions(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(EntranceNameByRegions(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL)) }, GetEntrance(EntranceNameByRegions(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(EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN)) }, GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN)) },
{ EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY), { EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW)) }, GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW)) },
@ -1530,11 +1534,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
// Pair <BlueWarp exit, BossRoom reverse exit> // Pair <BlueWarp exit, BossRoom reverse exit>
std::vector<EntrancePair> bossRoomExitPairs = { std::vector<EntrancePair> bossRoomExitPairs = {
{ GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)), { GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)),
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY)) }, GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT)) },
{ GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)), { GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)),
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY)) }, GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT)) },
{ GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)), { GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)),
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY)) }, GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT)) },
{ GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)), { GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)),
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY)) }, GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)), { GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)),
@ -1662,6 +1666,30 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) {
} }
} }
} catch (const std::exception& e) { throw e; } } 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 } // namespace Rando

View file

@ -128,6 +128,7 @@ class EntranceShuffler {
void CreateEntranceOverrides(); void CreateEntranceOverrides();
void UnshuffleAllEntrances(); void UnshuffleAllEntrances();
void ParseJson(nlohmann::json spoilerFileJson); void ParseJson(nlohmann::json spoilerFileJson);
void ApplyEntranceOverrides();
private: private:
std::vector<Entrance*> AssumeEntrancePool(std::vector<Entrance*>& entrancePool); std::vector<Entrance*> AssumeEntrancePool(std::vector<Entrance*>& entrancePool);

View file

@ -46,14 +46,20 @@ Item::~Item() = default;
void Item::ApplyEffect() const { void Item::ApplyEffect() const {
auto ctx = Rando::Context::GetInstance(); auto ctx = Rando::Context::GetInstance();
ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true); auto logic = ctx->GetLogic();
ctx->GetLogic()->SetInLogic(logicVal, true); if (!logic->CalculatingAvailableChecks) {
logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true);
}
logic->SetInLogic(logicVal, true);
} }
void Item::UndoEffect() const { void Item::UndoEffect() const {
auto ctx = Rando::Context::GetInstance(); auto ctx = Rando::Context::GetInstance();
ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false); auto logic = ctx->GetLogic();
ctx->GetLogic()->SetInLogic(logicVal, false); if (!logic->CalculatingAvailableChecks) {
logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false);
}
logic->SetInLogic(logicVal, false);
} }
const Text& Item::GetName() const { const Text& Item::GetName() const {

View file

@ -10,9 +10,10 @@
#include "soh/Enhancements/debugger/performanceTimer.h" #include "soh/Enhancements/debugger/performanceTimer.h"
#include <fstream> #include <fstream>
#include <soh/OTRGlobals.h>
#include "3drando/shops.hpp"
extern "C" { extern "C" {
extern SaveContext gSaveContext;
extern PlayState* gPlayState; extern PlayState* gPlayState;
} }
@ -45,17 +46,71 @@ bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailab
conditionsMet = true; conditionsMet = true;
} }
return conditionsMet && return conditionsMet && CanBuy(calculatingAvailableChecks);
(calculatingAvailableChecks || CanBuy()); // TODO: run CanBuy when price is known due to settings
} }
bool LocationAccess::CanBuy() const { static uint16_t GetMinimumPrice(const Rando::Location* loc) {
return CanBuyAnother(location); extern PriceSettingsStruct shopsanityPrices;
extern PriceSettingsStruct scrubPrices;
extern PriceSettingsStruct merchantPrices;
PriceSettingsStruct priceSettings = loc->GetRCType() == RCTYPE_SHOP ? shopsanityPrices
: loc->GetRCType() == RCTYPE_SCRUB ? scrubPrices
: merchantPrices;
auto ctx = Rando::Context::GetInstance();
switch (ctx->GetOption(priceSettings.main).Get()) {
case RO_PRICE_VANILLA:
return loc->GetVanillaPrice();
case RO_PRICE_CHEAP_BALANCED:
return 0;
case RO_PRICE_BALANCED:
return 0;
case RO_PRICE_FIXED:
return ctx->GetOption(priceSettings.fixedPrice).Get() * 5;
case RO_PRICE_RANGE: {
uint16_t range1 = ctx->GetOption(priceSettings.range1).Get() * 5;
uint16_t range2 = ctx->GetOption(priceSettings.range1).Get() * 5;
return range1 < range2 ? range1 : range2;
}
case RO_PRICE_SET_BY_WALLET: {
if (ctx->GetOption(priceSettings.noWallet).Get()) {
return 0;
} else if (ctx->GetOption(priceSettings.childWallet).Get()) {
return 1;
} else if (ctx->GetOption(priceSettings.adultWallet).Get()) {
return 100;
} else if (ctx->GetOption(priceSettings.giantWallet).Get()) {
return 201;
} else {
return 501;
}
}
default:
return 0;
}
}
bool LocationAccess::CanBuy(bool calculatingAvailableChecks) const {
const auto& loc = Rando::StaticData::GetLocation(location);
const auto& itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(location);
if (loc->GetRCType() == RCTYPE_SHOP || loc->GetRCType() == RCTYPE_SCRUB || loc->GetRCType() == RCTYPE_MERCHANT) {
// Checks should only be identified while playing
if (calculatingAvailableChecks && itemLoc->GetCheckStatus() != RCSHOW_IDENTIFIED) {
return CanBuyAnother(GetMinimumPrice(loc));
} else {
return CanBuyAnother(itemLoc->GetPrice());
}
}
return true;
} }
bool CanBuyAnother(RandomizerCheck rc) { bool CanBuyAnother(RandomizerCheck rc) {
uint16_t price = ctx->GetItemLocation(rc)->GetPrice(); return CanBuyAnother(ctx->GetItemLocation(rc)->GetPrice());
}
bool CanBuyAnother(uint16_t price) {
if (price > 500) { if (price > 500) {
return logic->HasItem(RG_TYCOON_WALLET); return logic->HasItem(RG_TYCOON_WALLET);
} else if (price > 200) { } else if (price > 200) {
@ -275,7 +330,7 @@ bool BeanPlanted(const RandomizerRegion region) {
if (gPlayState != nullptr && gPlayState->sceneNum == sceneID) { if (gPlayState != nullptr && gPlayState->sceneNum == sceneID) {
swch = gPlayState->actorCtx.flags.swch; swch = gPlayState->actorCtx.flags.swch;
} else if (sceneID != SCENE_ID_MAX) { } else if (sceneID != SCENE_ID_MAX) {
swch = gSaveContext.sceneFlags[sceneID].swch; swch = Rando::Context::GetInstance()->GetLogic()->GetSaveContext()->sceneFlags[sceneID].swch;
} else { } else {
swch = 0; swch = 0;
} }

View file

@ -100,9 +100,10 @@ class LocationAccess {
std::string condition_str; std::string condition_str;
// Makes sure shop locations are buyable // Makes sure shop locations are buyable
bool CanBuy() const; bool CanBuy(bool calculatingAvailableChecks) const;
}; };
bool CanBuyAnother(uint16_t price);
bool CanBuyAnother(RandomizerCheck rc); bool CanBuyAnother(RandomizerCheck rc);
namespace Rando { namespace Rando {
@ -136,7 +137,6 @@ class Region {
bool adultDay = false; bool adultDay = false;
bool adultNight = false; bool adultNight = false;
bool addedToPool = false; bool addedToPool = false;
;
void ApplyTimePass(); void ApplyTimePass();

View file

@ -25,7 +25,6 @@ void RegionTable_Init_BottomOfTheWell() {
}, { }, {
//Locations //Locations
LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_CENTER_BOMBABLE_CHEST, logic->HasExplosives()), LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_CENTER_BOMBABLE_CHEST, logic->HasExplosives()),
LOCATION(RC_BOTTOM_OF_THE_WELL_FREESTANDING_KEY, (logic->HasItem(RG_BRONZE_SCALE) || logic->LoweredWaterInsideBotw) && logic->CanUse(RG_STICKS) || logic->CanUse(RG_DINS_FIRE)),
LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_FRONT_CHEST, logic->LoweredWaterInsideBotw), LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_FRONT_CHEST, logic->LoweredWaterInsideBotw),
LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_LEFT_CHEST, logic->LoweredWaterInsideBotw), LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_LEFT_CHEST, logic->LoweredWaterInsideBotw),
LOCATION(RC_BOTTOM_OF_THE_WELL_NEAR_ENTRANCE_POT_1, logic->CanBreakPots()), LOCATION(RC_BOTTOM_OF_THE_WELL_NEAR_ENTRANCE_POT_1, logic->CanBreakPots()),

View file

@ -436,11 +436,16 @@ void RegionTable_Init_DekuTree() {
#pragma endregion #pragma endregion
// Boss Room // 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, {}, {}, { 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 // Exits
Entrance(RR_DEKU_TREE_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsVanilla();}), 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_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, { 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()), LOCATION(RC_DEKU_TREE_QUEEN_GOHMA_GRASS_8, logic->CanCutShrubs()),
}, { }, {
// Exits // Exits
Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, []{return true;}), Entrance(RR_DEKU_TREE_BOSS_EXIT, []{return true;}),
Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false), Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false),
}); });
// clang-format on // clang-format on

View file

@ -268,6 +268,7 @@ void RegionTable_Init_DodongosCavern() {
LOCATION(RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT, logic->CanStunDeku()), LOCATION(RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT, logic->CanStunDeku()),
}, { }, {
//Exits //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_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_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);});}), 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 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 //Locations
LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_1, logic->CanBreakPots()), LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_1, logic->CanBreakPots()),
LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_2, 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);});}), 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 //Locations
LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_1, logic->CanBreakPots()), LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_1, logic->CanBreakPots()),
LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_2, logic->CanBreakPots()), LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_2, logic->CanBreakPots()),
@ -558,11 +559,16 @@ void RegionTable_Init_DodongosCavern() {
#pragma endregion #pragma endregion
// Boss Room // 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, {}, {}, { 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 // Exits
Entrance(RR_DODONGOS_CAVERN_BOSS_AREA, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsVanilla();}), 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_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, { 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), LOCATION(RC_KING_DODONGO, logic->DodongosCavernClear),
}, { }, {
// Exits // Exits
Entrance(RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, []{return true;}), Entrance(RR_DODONGOS_CAVERN_BOSS_EXIT, []{return true;}),
Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false), Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false),
}); });
// clang-format on // clang-format on

View file

@ -20,7 +20,7 @@ void RegionTable_Init_FireTemple() {
//Exits //Exits
Entrance(RR_FIRE_TEMPLE_ENTRYWAY, []{return true;}), Entrance(RR_FIRE_TEMPLE_ENTRYWAY, []{return true;}),
Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return logic->FireTimer() >= 24;}), Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return logic->FireTimer() >= 24;}),
Entrance(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return Here(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);}) && (logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsKeysanity);}), Entrance(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return Here(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);}) && (logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsFireLoopLocked);}),
Entrance(RR_FIRE_TEMPLE_LOOP_EXIT, []{return true;}), Entrance(RR_FIRE_TEMPLE_LOOP_EXIT, []{return true;}),
Entrance(RR_FIRE_TEMPLE_BIG_LAVA_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 2) && logic->FireTimer() >= 24;}), Entrance(RR_FIRE_TEMPLE_BIG_LAVA_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 2) && logic->FireTimer() >= 24;}),
}); });
@ -38,12 +38,12 @@ void RegionTable_Init_FireTemple() {
}, { }, {
//Exits //Exits
Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return true;}), 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, {}, {}, { areaTable[RR_FIRE_TEMPLE_LOOP_ENEMIES] = Region("Fire Temple Loop Enemies", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits //Exits
Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsKeysanity;}), Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsFireLoopLocked;}),
Entrance(RR_FIRE_TEMPLE_LOOP_TILES, []{return Here(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return logic->CanKillEnemy(RE_TORCH_SLUG) && logic->CanKillEnemy(RE_FIRE_KEESE);});}), Entrance(RR_FIRE_TEMPLE_LOOP_TILES, []{return Here(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return logic->CanKillEnemy(RE_TORCH_SLUG) && logic->CanKillEnemy(RE_FIRE_KEESE);});}),
}); });
@ -445,7 +445,7 @@ void RegionTable_Init_FireTemple() {
Entrance(RR_FIRE_TEMPLE_MQ_FIRST_ROOM_UPPER, []{return true;}), 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 //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_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. //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;}), 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 //Locations
LOCATION(RC_FIRE_TEMPLE_MQ_FREESTANDING_KEY, logic->CanKillEnemy(RE_FLARE_DANCER)), LOCATION(RC_FIRE_TEMPLE_MQ_FREESTANDING_KEY, logic->CanKillEnemy(RE_FLARE_DANCER)),
}, { }, {
@ -740,7 +740,7 @@ void RegionTable_Init_FireTemple() {
// Exits // Exits
Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsVanilla() && false;}), 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_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, { areaTable[RR_FIRE_TEMPLE_BOSS_ROOM] = Region("Fire Temple Boss Room", "Fire Temple", {}, NO_DAY_NIGHT_CYCLE, {

View file

@ -298,7 +298,7 @@ void RegionTable_Init_ForestTemple() {
}, { }, {
//Exits //Exits
Entrance(RR_FOREST_TEMPLE_LOBBY, []{return true;}), 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 #pragma endregion
@ -481,7 +481,7 @@ void RegionTable_Init_ForestTemple() {
Entrance(RR_FOREST_TEMPLE_MQ_FALLING_ROOM, []{return logic->CanUse(RG_SONG_OF_TIME);}), 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 //Events
EventAccess(&logic->ForestTempleJoelle, []{return logic->CanUse(RG_FAIRY_BOW);}), 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, {}, {}, { areaTable[RR_FOREST_TEMPLE_MQ_BOSS_REGION] = Region("Forest Temple MQ Boss Region", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits //Exits
Entrance(RR_FOREST_TEMPLE_MQ_BASEMENT, []{return logic->ForestOpenBossCorridor;}), 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 #pragma endregion
@ -603,7 +603,7 @@ void RegionTable_Init_ForestTemple() {
// Exits // Exits
Entrance(RR_FOREST_TEMPLE_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsVanilla() && false;}), 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_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, { areaTable[RR_FOREST_TEMPLE_BOSS_ROOM] = Region("Forest Temple Boss Room", "Forest Temple", {}, NO_DAY_NIGHT_CYCLE, {

View file

@ -29,12 +29,7 @@ void RegionTable_Init_GanonsCastle() {
Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, []{return true;}), Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, []{return true;}),
Entrance(RR_GANONS_CASTLE_SPIRIT_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_CASTLE_LIGHT_TRIAL, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}),
Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}),
(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_CASTLE_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON) || logic->CanUse(RG_LENS_OF_TRUTH);}), 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_SHADOW_TRIAL_STARTING_LEDGE, []{return true;}),
Entrance(RR_GANONS_CASTLE_MQ_SPIRIT_TRIAL_CHAIRS_ROOM, []{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);});}), 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 true;}),
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_CASTLE_MQ_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON_MQ) || logic->CanUse(RG_LENS_OF_TRUTH);}), 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 //Exits
Entrance(RR_GANONS_CASTLE_LOBBY, []{return ctx->GetDungeon(GANONS_CASTLE)->IsVanilla();}), 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_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, {}, {}, { areaTable[RR_GANONS_TOWER_FLOOR_1] = Region("Ganon's Tower Floor 1", "Ganons Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {

View file

@ -149,7 +149,7 @@ void RegionTable_Init_GerudoTrainingGround() {
Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER, []{return logic->SmallKeys(RR_GERUDO_TRAINING_GROUND, 3);}), 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 //Events
EventAccess(&logic->MQGTGMazeSwitch, []{return logic->CanUse(RG_MEGATON_HAMMER);}), EventAccess(&logic->MQGTGMazeSwitch, []{return logic->CanUse(RG_MEGATON_HAMMER);}),
}, },

View file

@ -84,7 +84,7 @@ void RegionTable_Init_IceCavern() {
//Exits //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 //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. //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_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_COMPASS_ROOM, []{return logic->IsAdult && logic->BlueFire();}),
Entrance(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, []{return logic->BlueFire();}), Entrance(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, []{return logic->BlueFire();}),

View file

@ -351,10 +351,14 @@ void RegionTable_Init_JabuJabusBelly() {
// Boss Room // 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, {}, {}, { 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 // Exits
Entrance(RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsVanilla();}), 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_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, { areaTable[RR_JABU_JABUS_BELLY_BOSS_ROOM] = Region("Jabu Jabus Belly Boss Room", "Jabu Jabus Belly", {}, NO_DAY_NIGHT_CYCLE, {
@ -372,8 +376,8 @@ void RegionTable_Init_JabuJabusBelly() {
LOCATION(RC_BARINADE, logic->JabuJabusBellyClear), LOCATION(RC_BARINADE, logic->JabuJabusBellyClear),
}, { }, {
// Exits // Exits
Entrance(RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, []{return false;}), Entrance(RR_JABU_JABUS_BELLY_BOSS_EXIT, []{return false;}),
Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false), Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false),
}); });
// clang-format on // clang-format on

View file

@ -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))), 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 //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 #pragma endregion
@ -370,7 +370,7 @@ void RegionTable_Init_ShadowTemple() {
}, { }, {
//Exits //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_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 //Assumes lens is checked on entry
@ -403,7 +403,7 @@ void RegionTable_Init_ShadowTemple() {
// Exits // Exits
Entrance(RR_SHADOW_TEMPLE_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsVanilla() && false;}), 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_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, { areaTable[RR_SHADOW_TEMPLE_BOSS_ROOM] = Region("Shadow Temple Boss Room", "Shadow Temple", {}, NO_DAY_NIGHT_CYCLE, {

View file

@ -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, {}, {}, { areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits // Exits
Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return true;}), 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 #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 // 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 //Locations
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_1, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_2, 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 //Events
//For non-fairy pot items, you can also get them with rang without killing the stalfos //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);});}), 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);}), 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 //Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), 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);}), 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, {}, {}, { 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 // Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), 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 #pragma endregion
@ -551,7 +551,7 @@ void RegionTable_Init_SpiritTemple() {
// Exits // Exits
Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}), 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_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, { areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", "Spirit Temple", {}, NO_DAY_NIGHT_CYCLE, {

View file

@ -277,7 +277,7 @@ void RegionTable_Init_WaterTemple() {
}, { }, {
//Exits //Exits
Entrance(RR_WATER_TEMPLE_LOBBY, []{return true;}), 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 #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);}), 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 //Exits
Entrance(RR_WATER_TEMPLE_MQ_3F_NORTH_LEDGE, []{return logic->CanUse(RG_ICE_ARROWS) || logic->TakeDamage();}), 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, { 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);}), 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 //Locations
LOCATION(RC_WATER_TEMPLE_MQ_GS_BEFORE_UPPER_WATER_SWITCH, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)), 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 //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 //Exits
Entrance(RR_WATER_TEMPLE_MQ_3F_CENTRAL, []{return logic->SmallKeys(RR_WATER_TEMPLE, 1) && logic->CanUse(RG_LONGSHOT);}), 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;}), Entrance(RR_WATER_TEMPLE_MQ_STALFOS_PIT, []{return true;}),
@ -658,7 +658,7 @@ void RegionTable_Init_WaterTemple() {
areaTable[RR_WATER_TEMPLE_MQ_DRAGON_ROOM_ALCOVE] = Region("Water Temple MQ Dragon Room Alcove", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, { areaTable[RR_WATER_TEMPLE_MQ_DRAGON_ROOM_ALCOVE] = Region("Water Temple MQ Dragon Room Alcove", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events //Events
EventAccess(&logic->MQWaterDragonTorches, []{return true;}), EventAccess(&logic->MQWaterDragonTorches, []{return logic->HasFireSource();}),
}, },
{ {
//Locations //Locations
@ -843,7 +843,7 @@ void RegionTable_Init_WaterTemple() {
// Exits // Exits
Entrance(RR_WATER_TEMPLE_PRE_BOSS_ROOM, []{return ctx->GetDungeon(WATER_TEMPLE)->IsVanilla() && false;}), 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_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, { areaTable[RR_WATER_TEMPLE_BOSS_ROOM] = Region("Water Temple Boss Room", "Water Temple", {}, NO_DAY_NIGHT_CYCLE, {

View file

@ -265,7 +265,7 @@ void RegionTable_Init_Kakariko() {
Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return true;}), 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 //Exits
Entrance(RR_KAKARIKO_VILLAGE, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->DrainWell;}), 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));}), Entrance(RR_BOTTOM_OF_THE_WELL_ENTRYWAY, []{return logic->IsChild || (logic->DrainWell && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).IsNot(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF));}),

View file

@ -105,7 +105,7 @@ void RegionTable_Init_LakeHylia() {
areaTable[RR_LH_LAB] = Region("LH Lab", "LH Lab", {}, NO_DAY_NIGHT_CYCLE, {}, { areaTable[RR_LH_LAB] = Region("LH Lab", "LH Lab", {}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations //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_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_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)), LOCATION(RC_LH_LAB_FRONT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)),

View file

@ -1389,6 +1389,7 @@ bool Logic::SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmountGlitchless
static_cast<uint8_t>(GlitchDifficulty::INTERMEDIATE) || GetDifficultyValueFromString(GlitchHover) >= static_cast<uint8_t>(GlitchDifficulty::INTERMEDIATE) || GetDifficultyValueFromString(GlitchHover) >=
static_cast<uint8_t>(GlitchDifficulty::INTERMEDIATE))) { return FireTempleKeys >= requiredAmountGlitched; static_cast<uint8_t>(GlitchDifficulty::INTERMEDIATE))) { return FireTempleKeys >= requiredAmountGlitched;
}*/ }*/
// If the Fire Temple loop lock is removed, Small key Count is set to 1 before starting
return GetSmallKeyCount(SCENE_FIRE_TEMPLE) >= requiredAmountGlitchless; return GetSmallKeyCount(SCENE_FIRE_TEMPLE) >= requiredAmountGlitchless;
case RR_WATER_TEMPLE: case RR_WATER_TEMPLE:
@ -2331,14 +2332,16 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) {
inLogic[logicVal] = value; inLogic[logicVal] = value;
} }
void Logic::Reset() { void Logic::Reset(bool resetSaveContext /*= true*/) {
NewSaveContext(); if (resetSaveContext) {
NewSaveContext();
}
StartPerformanceTimer(PT_LOGIC_RESET); StartPerformanceTimer(PT_LOGIC_RESET);
memset(inLogic, false, sizeof(inLogic)); memset(inLogic, false, sizeof(inLogic));
// Settings-dependent variables // Settings-dependent variables
IsKeysanity = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || IsFireLoopLocked = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) ||
ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OVERWORLD) ||
ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE); ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON);
// AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE)*/ false; TODO: AmmoDrop setting // AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE)*/ false; TODO: AmmoDrop setting
@ -2371,37 +2374,39 @@ void Logic::Reset() {
ShadowTrialClear = false; ShadowTrialClear = false;
LightTrialClear = false; LightTrialClear = false;
// Ocarina C Buttons if (resetSaveContext) {
bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true); // Ocarina C Buttons
SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle); bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true);
SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle); SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle);
SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle); SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle);
SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle); SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle);
SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle); SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle);
SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle);
// Progressive Items // Progressive Items
SetUpgrade(UPG_STICKS, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).Is(true) ? 0 : 1); 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); 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 we're not shuffling swim, we start with it
if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) { if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) {
SetRandoInf(RAND_INF_CAN_SWIM, true); SetRandoInf(RAND_INF_CAN_SWIM, true);
} }
// If we're not shuffling child's wallet, we start with it // If we're not shuffling child's wallet, we start with it
if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) { if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) {
SetRandoInf(RAND_INF_HAS_WALLET, true); SetRandoInf(RAND_INF_HAS_WALLET, true);
} }
// If we're not shuffling fishing pole, we start with it // If we're not shuffling fishing pole, we start with it
if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) { if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) {
SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true); 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 // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in
// FiT // vanilla FiT
if (!IsKeysanity && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) {
SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1);
}
} }
// Bottle Count // Bottle Count
@ -2454,7 +2459,9 @@ void Logic::Reset() {
// Other // Other
AtDay = false; AtDay = false;
AtNight = false; AtNight = false;
GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get(); if (resetSaveContext) {
GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get();
}
// Events // Events
ShowedMidoSwordAndShield = false; ShowedMidoSwordAndShield = false;
@ -2519,6 +2526,8 @@ void Logic::Reset() {
Spirit1FSilverRupees = false; Spirit1FSilverRupees = false;
JabuRutoIn1F = false; JabuRutoIn1F = false;
CalculatingAvailableChecks = false;
StopPerformanceTimer(PT_LOGIC_RESET); StopPerformanceTimer(PT_LOGIC_RESET);
} }
} // namespace Rando } // namespace Rando

View file

@ -59,7 +59,7 @@ class Logic {
bool LightTrialClear = false; bool LightTrialClear = false;
// Logical keysanity // Logical keysanity
bool IsKeysanity = false; bool IsFireLoopLocked = false;
// Bottle Count // Bottle Count
uint8_t Bottles = 0; uint8_t Bottles = 0;
@ -183,6 +183,9 @@ class Logic {
/* --- END OF HELPERS AND LOCATION ACCESS --- */ /* --- END OF HELPERS AND LOCATION ACCESS --- */
bool CalculatingAvailableChecks = false;
bool ACProcessUndiscoveredExits = false;
SaveContext* mSaveContext = nullptr; SaveContext* mSaveContext = nullptr;
Logic(); Logic();
bool CanUse(RandomizerGet itemName); bool CanUse(RandomizerGet itemName);
@ -254,7 +257,7 @@ class Logic {
bool CanUseProjectile(); bool CanUseProjectile();
bool CanBuildRainbowBridge(); bool CanBuildRainbowBridge();
bool CanTriggerLACS(); bool CanTriggerLACS();
void Reset(); void Reset(bool resetSaveContext = true);
void SetContext(std::shared_ptr<Context> _ctx); void SetContext(std::shared_ptr<Context> _ctx);
bool GetInLogic(LogicVal logicVal); bool GetInLogic(LogicVal logicVal);
void SetInLogic(LogicVal logicVal, bool remove); void SetInLogic(LogicVal logicVal, bool remove);

View file

@ -65,7 +65,7 @@ const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi";
const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap"; const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap";
const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints"; const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints";
static const char* englishRupeeNames[190] = { static const char* englishRupeeNames[188] = {
"[P]", "[P]",
"Bad RNG Rolls", "Bad RNG Rolls",
"Baht", "Baht",
@ -111,8 +111,6 @@ static const char* englishRupeeNames[190] = {
"Dimes", "Dimes",
"Dinars", "Dinars",
"DNA", "DNA",
"Doge",
"Dogecoin",
"Doll Hairs", "Doll Hairs",
"Dollars", "Dollars",
"Dollarydoos", "Dollarydoos",
@ -258,25 +256,25 @@ static const char* englishRupeeNames[190] = {
"Zorkmids", "Zorkmids",
}; };
static const char* germanRupeeNames[80] = { static const char* germanRupeeNames[79] = {
"Baht", "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent", "Baht", "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent",
"Diamanten", "Dinar", "Diridari", "Dogecoin", "Dollar", "Draken", "ECU", "Elexit", "Diamanten", "Dinar", "Diridari", "Dollar", "Draken", "ECU", "Elexit", "Erz",
"Erz", "Erzbrocken", "Euro", "EXP", "Forint", "Franken", "Freunde", "Gil", "Erzbrocken", "Euro", "EXP", "Forint", "Franken", "Freunde", "Gil", "Gold",
"Gold", "Groschen", "Gulden", "Gummibären", "Heller", "Juwelen", "Karolin", "Kartoffeln", "Groschen", "Gulden", "Gummibären", "Heller", "Juwelen", "Karolin", "Kartoffeln", "Kies",
"Kies", "Knete", "Knochen", "Kohle", "Kraniche", "Kreuzer", "Kronen", "Kronkorken", "Knete", "Knochen", "Kohle", "Kraniche", "Kreuzer", "Kronen", "Kronkorken", "Kröten",
"Kröten", "Lira", "Mark", "Mäuse", "Monde", "Moorhühner", "Moos", "Münzen", "Lira", "Mark", "Mäuse", "Monde", "Moorhühner", "Moos", "Münzen", "Naira",
"Naira", "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", "Pilze", "Plastiks", "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", "Pilze", "Plastiks", "Pokédollar",
"Pokédollar", "Radieschen", "Rand", "Rappen", "Real", "Rial", "Riyal", "Rubine", "Radieschen", "Rand", "Rappen", "Real", "Rial", "Riyal", "Rubine", "Rupien",
"Rupien", "Saphire", "Schilling", "Seelen", "Septime", "Smaragde", "Steine", "Sterne", "Saphire", "Schilling", "Seelen", "Septime", "Smaragde", "Steine", "Sterne", "Sternis",
"Sternis", "Tael", "Taler", "Wagenchips", "Won", "Yen", "Yuan", "Zenny", "Tael", "Taler", "Wagenchips", "Won", "Yen", "Yuan", "Zenny",
}; };
static const char* frenchRupeeNames[40] = { static const char* frenchRupeeNames[39] = {
"Anneaux", "Baguettes", "Balles", "Bananes", "Bitcoin", "Blés", "Bling", "Capsules", "Anneaux", "Baguettes", "Balles", "Bananes", "Bitcoin", "Blés", "Bling", "Capsules",
"Centimes", "Champignons", "Clochettes", "Crédits", "Croissants", "Diamants", "Dogecoin", "Dollars", "Centimes", "Champignons", "Clochettes", "Crédits", "Croissants", "Diamants", "Dollars", "Émeraudes",
"Émeraudes", "Éthers", "Étoiles", "Euros", "Florens", "Francs", "Galds", "Gils", "Éthers", "Étoiles", "Euros", "Florens", "Francs", "Galds", "Gils", "Grouses",
"Grouses", "Halos", "Joyaux", "Lunes", "Mailles", "Munnies", "Orbes", "Orens", "Halos", "Joyaux", "Lunes", "Mailles", "Munnies", "Orbes", "Orens", "Pépètes",
"Pépètes", "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies", "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies",
}; };
Randomizer::Randomizer() { Randomizer::Randomizer() {
@ -3726,13 +3724,15 @@ void RandomizerSettingsWindow::DrawElement() {
} }
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
ImGui::BeginDisabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); UIWidgets::ButtonOptions options = UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR);
if (UIWidgets::Button("Generate Randomizer", options.Disabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded());
UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR))) { if (options.disabled) {
options.DisabledTooltip("Must be on File Select to generate a randomizer seed.");
}
if (UIWidgets::Button("Generate Randomizer", options)) {
ctx->SetSpoilerLoaded(false); ctx->SetSpoilerLoaded(false);
GenerateRandomizer(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0) ? seedString : ""); GenerateRandomizer(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0) ? seedString : "");
} }
ImGui::EndDisabled();
ImGui::SameLine(); ImGui::SameLine();
if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) {
@ -4515,6 +4515,9 @@ CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) {
messageEntry.Replace("[[typeHint]]", Rando::StaticData::hintTextTable[RHT_DUNGEON_ORDINARY].GetHintMessage()); messageEntry.Replace("[[typeHint]]", Rando::StaticData::hintTextTable[RHT_DUNGEON_ORDINARY].GetHintMessage());
} }
// BUG: the icon is not in the message yet so are not accounted for, so overflows are possible
messageEntry.AutoFormat();
return messageEntry; return messageEntry;
} }

View file

@ -566,6 +566,7 @@ typedef enum {
RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM,
RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ENTRYWAY,
RR_DEKU_TREE_BOSS_EXIT,
RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ROOM,
RR_DODONGOS_CAVERN_BEGINNING, RR_DODONGOS_CAVERN_BEGINNING,
@ -613,7 +614,9 @@ typedef enum {
RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH, RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH,
RR_DODONGOS_CAVERN_MQ_BACK_BEHIND_FIRE, RR_DODONGOS_CAVERN_MQ_BACK_BEHIND_FIRE,
RR_DODONGOS_CAVERN_MQ_BACK_SWITCH_GRAVE, RR_DODONGOS_CAVERN_MQ_BACK_SWITCH_GRAVE,
RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY,
RR_DODONGOS_CAVERN_BOSS_EXIT,
RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ROOM,
RR_JABU_JABUS_BELLY_BEGINNING, RR_JABU_JABUS_BELLY_BEGINNING,
@ -642,6 +645,7 @@ typedef enum {
RR_JABU_JABUS_BELLY_MQ_EAST_ROOM, RR_JABU_JABUS_BELLY_MQ_EAST_ROOM,
RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY,
RR_JABU_JABUS_BELLY_BOSS_EXIT,
RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ROOM,
RR_FOREST_TEMPLE_FIRST_ROOM, RR_FOREST_TEMPLE_FIRST_ROOM,

View file

@ -580,6 +580,13 @@ void CheckTrackerLoadGame(int32_t fileNum) {
UpdateAllOrdering(); UpdateAllOrdering();
UpdateInventoryChecks(); UpdateInventoryChecks();
UpdateFilters(); UpdateFilters();
RegionTable_Init();
if (Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_ENTRANCES).Get()) {
Rando::Context::GetInstance()->GetEntranceShuffler()->ApplyEntranceOverrides();
}
RecalculateAvailableChecks(); RecalculateAvailableChecks();
} }
@ -898,7 +905,6 @@ void LoadFile() {
SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled, (uint32_t)0); SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled, (uint32_t)0);
UpdateAllOrdering(); UpdateAllOrdering();
UpdateAllAreas(); UpdateAllAreas();
RegionTable_Init();
} }
void Teardown() { void Teardown() {
@ -1481,6 +1487,27 @@ void LoadSettings() {
showOverworldFreestanding = false; showOverworldFreestanding = false;
showDungeonFreestanding = true; 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) { bool IsCheckShuffled(RandomizerCheck rc) {
@ -1820,7 +1847,7 @@ void DrawLocation(RandomizerCheck rc) {
case RCSHOW_IDENTIFIED: case RCSHOW_IDENTIFIED:
case RCSHOW_SEEN: case RCSHOW_SEEN:
if (IS_RANDO) { if (IS_RANDO) {
if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery && !itemLoc->IsAddedToPool()) { if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery) {
if (status == RCSHOW_IDENTIFIED) { if (status == RCSHOW_IDENTIFIED) {
txt = OTRGlobals::Instance->gRandoContext->overrides[rc].GetTrickName().GetForLanguage( txt = OTRGlobals::Instance->gRandoContext->overrides[rc].GetTrickName().GetForLanguage(
gSaveContext.language); gSaveContext.language);
@ -1830,11 +1857,10 @@ void DrawLocation(RandomizerCheck rc) {
.GetName() .GetName()
.GetForLanguage(gSaveContext.language); .GetForLanguage(gSaveContext.language);
} }
} else if (!mystery && !itemLoc->IsAddedToPool()) { } else if (!mystery) {
txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language); txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language);
} }
if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery && if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery) {
!itemLoc->IsAddedToPool()) {
auto price = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetPrice(); auto price = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetPrice();
if (price) { if (price) {
txt += fmt::format(" - {}", price); txt += fmt::format(" - {}", price);
@ -1958,7 +1984,7 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName,
UIWidgets::PopStyleCombobox(); UIWidgets::PopStyleCombobox();
} }
void RecalculateAvailableChecks() { void RecalculateAvailableChecks(RandomizerRegion startingRegion /* = RR_ROOT */) {
if (!enableAvailableChecks) { if (!enableAvailableChecks) {
return; return;
} }
@ -1966,35 +1992,30 @@ void RecalculateAvailableChecks() {
ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
const auto& ctx = Rando::Context::GetInstance();
std::vector<RandomizerCheck> targetLocations; std::vector<RandomizerCheck> targetLocations;
targetLocations.reserve(RR_MAX); targetLocations.reserve(RC_MAX);
for (auto& location : Rando::StaticData::GetLocationTable()) { for (auto& location : Rando::StaticData::GetLocationTable()) {
RandomizerCheck rc = location.GetRandomizerCheck(); RandomizerCheck rc = location.GetRandomizerCheck();
Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc);
itemLocation->SetAvailable(false); itemLocation->SetAvailable(false);
if (!itemLocation->HasObtained()) { if (!itemLocation->HasObtained()) {
targetLocations.emplace_back(rc); 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) { for (auto& rc : availableChecks) {
const auto& location = Rando::StaticData::GetLocation(rc); const auto& itemLocation = ctx->GetItemLocation(rc);
const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); itemLocation->SetAvailable(true);
if (location->GetRCType() == RCTYPE_SHOP && itemLocation->GetCheckStatus() == RCSHOW_IDENTIFIED) {
if (CanBuyAnother(rc)) {
itemLocation->SetAvailable(true);
}
} else {
itemLocation->SetAvailable(true);
}
} }
totalChecksAvailable = 0; totalChecksAvailable = 0;
for (auto& [rcArea, vec] : checksByArea) { for (auto& [rcArea, vec] : checksByArea) {
areaChecksAvailable[rcArea] = 0; areaChecksAvailable[rcArea] = 0;
for (auto& rc : vec) { 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)) { if (itemLocation->IsAvailable() && IsVisibleInCheckTracker(rc) && !IsCheckHidden(rc)) {
areaChecksAvailable[rcArea]++; areaChecksAvailable[rcArea]++;
} }
@ -2114,7 +2135,10 @@ void CheckTrackerSettingsWindow::DrawElement() {
"with your current progress.") "with your current progress.")
.Color(THEME_COLOR))) { .Color(THEME_COLOR))) {
enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0); enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0);
RecalculateAvailableChecks();
if (GameInteractor::IsSaveLoaded(true)) {
RecalculateAvailableChecks();
}
} }
ImGui::EndDisabled(); ImGui::EndDisabled();

View file

@ -61,5 +61,5 @@ void UpdateAllOrdering();
void UpdateAllAreas(); void UpdateAllAreas();
void RecalculateAllAreaTotals(); void RecalculateAllAreaTotals();
void SpoilAreaFromCheck(RandomizerCheck rc); void SpoilAreaFromCheck(RandomizerCheck rc);
void RecalculateAvailableChecks(); void RecalculateAvailableChecks(RandomizerRegion startingRegion = RR_ROOT);
} // namespace CheckTracker } // namespace CheckTracker

View file

@ -809,6 +809,7 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) {
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) { if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex)); u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
gSaveContext.ship.stats.entrancesDiscovered[idx] |= entranceBit; gSaveContext.ship.stats.entrancesDiscovered[idx] |= entranceBit;
CheckTracker_RecalculateAvailableChecks();
// Set reverse entrance when not decoupled // Set reverse entrance when not decoupled
if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) { if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) {

View file

@ -232,6 +232,8 @@ extern "C" void Randomizer_InitSaveFile() {
// Reset triforce pieces collected. // Reset triforce pieces collected.
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected = 0; gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected = 0;
SetStartingItems();
// Set Cutscene flags and texts to skip them. // Set Cutscene flags and texts to skip them.
Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO); Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO);
Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA); Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA);
@ -269,9 +271,9 @@ extern "C" void Randomizer_InitSaveFile() {
// Remove One Time Scrubs with Scrubsanity off // Remove One Time Scrubs with Scrubsanity off
if (Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_OFF) { if (Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_OFF) {
Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE); Flags_SetItemGetInf(ITEMGETINF_DEKU_SCRUB_HEART_PIECE);
Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT); Flags_SetInfTable(INFTABLE_BOUGHT_STICK_UPGRADE);
Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO); Flags_SetInfTable(INFTABLE_BOUGHT_NUT_UPGRADE);
} }
int startingAge = OTRGlobals::Instance->gRandoContext->GetOption(RSK_SELECTED_STARTING_AGE).Get(); int startingAge = OTRGlobals::Instance->gRandoContext->GetOption(RSK_SELECTED_STARTING_AGE).Get();
@ -430,6 +432,4 @@ extern "C" void Randomizer_InitSaveFile() {
gSaveContext.itemGetInf[3] |= 0x800; // Bunny Hood related gSaveContext.itemGetInf[3] |= 0x800; // Bunny Hood related
gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth
} }
SetStartingItems();
} }

View file

@ -251,7 +251,7 @@ void Settings::CreateOptions() {
OPT_U8(RSK_LACS_REWARD_COUNT, "GCBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardCount"), "", WidgetType::Slider, 9, true); OPT_U8(RSK_LACS_REWARD_COUNT, "GCBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardCount"), "", WidgetType::Slider, 9, true);
OPT_U8(RSK_LACS_DUNGEON_COUNT, "GCBK Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsDungeonCount"), "", WidgetType::Slider, 8, true); OPT_U8(RSK_LACS_DUNGEON_COUNT, "GCBK Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsDungeonCount"), "", WidgetType::Slider, 8, true);
OPT_U8(RSK_LACS_TOKEN_COUNT, "GCBK Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsTokenCount"), "", WidgetType::Slider, 100, true); OPT_U8(RSK_LACS_TOKEN_COUNT, "GCBK Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsTokenCount"), "", WidgetType::Slider, 100, true);
OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), "", WidgetType::Combobox, RO_LACS_STANDARD_REWARD); OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), mOptionDescriptions[RSK_LACS_OPTIONS], WidgetType::Combobox, RO_LACS_STANDARD_REWARD);
OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WidgetType::Combobox, RO_KEYRINGS_OFF); OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WidgetType::Combobox, RO_KEYRINGS_OFF);
OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WidgetType::Slider, 8); OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WidgetType::Slider, 8);
OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WidgetType::Combobox, 0); OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WidgetType::Combobox, 0);
@ -1742,6 +1742,14 @@ TrickOption& Settings::GetTrickOption(const RandomizerTrick key) {
return mTrickOptions[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() { void Context::ResetTrickOptions() {
for (int count = 0; count < RT_MAX; count++) { for (int count = 0; count < RT_MAX; count++) {
mTrickOptions[count].Set(0); // RANDOTODO this can probably be done better mTrickOptions[count].Set(0); // RANDOTODO this can probably be done better

View file

@ -50,6 +50,14 @@ class Settings {
*/ */
TrickOption& GetTrickOption(RandomizerTrick key); 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. * @brief Returns a reference to the entire array of options.
* *

View file

@ -13,12 +13,14 @@ extern "C" {
#include "src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h" #include "src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h"
#include "src/overlays/actors/ovl_En_Owl/z_en_owl.h" #include "src/overlays/actors/ovl_En_Owl/z_en_owl.h"
#include "src/overlays/actors/ovl_En_Go2/z_en_go2.h" #include "src/overlays/actors/ovl_En_Go2/z_en_go2.h"
#include "src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.h"
#include "src/overlays/actors/ovl_En_Ko/z_en_ko.h" #include "src/overlays/actors/ovl_En_Ko/z_en_ko.h"
#include "src/overlays/actors/ovl_En_Ma1/z_en_ma1.h" #include "src/overlays/actors/ovl_En_Ma1/z_en_ma1.h"
#include "src/overlays/actors/ovl_En_Ru2/z_en_ru2.h" #include "src/overlays/actors/ovl_En_Ru2/z_en_ru2.h"
#include "src/overlays/actors/ovl_En_Zl4/z_en_zl4.h" #include "src/overlays/actors/ovl_En_Zl4/z_en_zl4.h"
#include "src/overlays/actors/ovl_En_Box/z_en_box.h" #include "src/overlays/actors/ovl_En_Box/z_en_box.h"
#include "src/overlays/actors/ovl_Demo_Im/z_demo_im.h" #include "src/overlays/actors/ovl_Demo_Im/z_demo_im.h"
#include "src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h"
#include "src/overlays/actors/ovl_En_Sa/z_en_sa.h" #include "src/overlays/actors/ovl_En_Sa/z_en_sa.h"
#include "src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h" #include "src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h"
#include "src/overlays/actors/ovl_En_Tk/z_en_tk.h" #include "src/overlays/actors/ovl_En_Tk/z_en_tk.h"
@ -222,6 +224,25 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) && if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) &&
(entranceFlag != EVENTCHKINF_EPONA_OBTAINED) && entranceIndex != ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE) { (entranceFlag != EVENTCHKINF_EPONA_OBTAINED) && entranceIndex != ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE) {
*should = false; *should = false;
// Check for dispulsion of Ganon's Tower barrier
switch (entranceIndex) {
case ENTR_INSIDE_GANONS_CASTLE_2:
case ENTR_INSIDE_GANONS_CASTLE_3:
case ENTR_INSIDE_GANONS_CASTLE_4:
case ENTR_INSIDE_GANONS_CASTLE_5:
case ENTR_INSIDE_GANONS_CASTLE_6:
case ENTR_INSIDE_GANONS_CASTLE_7:
if (Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_FOREST_TRIAL) &&
Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_WATER_TRIAL) &&
Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_SHADOW_TRIAL) &&
Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_FIRE_TRIAL) &&
Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_LIGHT_TRIAL) &&
Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_SPIRIT_TRIAL)) {
Flags_SetEventChkInf(EVENTCHKINF_DISPELLED_GANONS_TOWER_BARRIER);
}
break;
}
} }
break; break;
} }
@ -280,8 +301,9 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
} }
switch (actor->id) { switch (actor->id) {
case ACTOR_OBJ_SWITCH: { case ACTOR_OBJ_SWITCH: {
if (actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN && if ((actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN) ||
CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { (actor->params == 6979 && gPlayState->sceneNum == SCENE_WATER_TEMPLE) &&
CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) {
break; break;
} }
ObjSwitch* switchActor = (ObjSwitch*)actor; ObjSwitch* switchActor = (ObjSwitch*)actor;
@ -358,6 +380,11 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
} }
break; break;
} }
case VB_FREEZE_LINK_FOR_FOREST_PILLARS:
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) {
*should = false;
}
break;
case VB_SHOW_TITLE_CARD: case VB_SHOW_TITLE_CARD:
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO)) { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO)) {
*should = false; *should = false;
@ -450,6 +477,26 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
*should = false; *should = false;
} }
break; break;
case VB_PLAY_DISPEL_BARRIER_CS: {
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) {
static s16 trialEntrances[] = {
0,
ENTR_INSIDE_GANONS_CASTLE_3,
ENTR_INSIDE_GANONS_CASTLE_6,
ENTR_INSIDE_GANONS_CASTLE_5,
ENTR_INSIDE_GANONS_CASTLE_4,
ENTR_INSIDE_GANONS_CASTLE_7,
ENTR_INSIDE_GANONS_CASTLE_2,
};
RateLimitedSuccessChime();
DemoKekkai* kekkai = va_arg(args, DemoKekkai*);
gPlayState->nextEntranceIndex = trialEntrances[kekkai->actor.params];
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
*should = false;
}
break;
}
case VB_OWL_INTERACTION: { case VB_OWL_INTERACTION: {
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) && *should) { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) && *should) {
EnOwl* enOwl = va_arg(args, EnOwl*); EnOwl* enOwl = va_arg(args, EnOwl*);
@ -729,6 +776,18 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
break; break;
} }
case VB_PLAY_GATE_OPENING_OR_CLOSING_CS: {
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
EnHeishi2* enHeishi2 = va_arg(args, EnHeishi2*);
enHeishi2->unk_2F2[0] = 0;
// The second argument determines whether the vanilla code should be run anyway. It
// should be set to `true` ONLY IF said code calls `Play_ClearCamera`, false otherwise.
bool clearCamera = (bool)va_arg(args, int);
*should = clearCamera && enHeishi2->cameraId != MAIN_CAM;
}
break;
}
case VB_PLAY_RAINBOW_BRIDGE_CS: { case VB_PLAY_RAINBOW_BRIDGE_CS: {
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
*should = false; *should = false;

View file

@ -1145,6 +1145,13 @@ extern "C" void InitOTR() {
"Error", "SoH does not have proper file permissions. Please move it to a folder that does and run again."); "Error", "SoH does not have proper file permissions. Please move it to a folder that does and run again.");
exit(1); 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 #endif
#if not defined(__SWITCH__) && not defined(__WIIU__) #if not defined(__SWITCH__) && not defined(__WIIU__)
@ -2437,15 +2444,15 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
// animation until the text box auto-dismisses. // animation until the text box auto-dismisses.
// RANDOTODO: Implement a way to determine if an item came from a skulltula and // RANDOTODO: Implement a way to determine if an item came from a skulltula and
// inject the auto-dismiss control code if it did. // inject the auto-dismiss control code if it did.
if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && bool gsTokensShuffled = Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF;
!(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF)) { if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && !(IS_RANDO && gsTokensShuffled)) {
textId = TEXT_GS_NO_FREEZE; textId = TEXT_GS_NO_FREEZE;
} else { } else {
textId = TEXT_GS_FREEZE; textId = TEXT_GS_FREEZE;
} }
// In vanilla, GS token count is incremented prior to the text box displaying // In vanilla, GS token count is incremented prior to the text box displaying
// In rando we need to bump the token count by one to show the correct count // In rando we need to bump the token count by one to show the correct count
s16 gsCount = gSaveContext.inventory.gsTokens + (IS_RANDO ? 1 : 0); s16 gsCount = gSaveContext.inventory.gsTokens + ((IS_RANDO && gsTokensShuffled) ? 1 : 0);
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId, MF_FORMATTED); messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId, MF_FORMATTED);
messageEntry.Replace("[[gsCount]]", std::to_string(gsCount)); messageEntry.Replace("[[gsCount]]", std::to_string(gsCount));
} else if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && } else if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 &&
@ -2608,4 +2615,7 @@ void SoH_ProcessDroppedFiles(std::string filePath) {
return; return;
} }
} }
// #endregion
extern "C" void CheckTracker_RecalculateAvailableChecks() {
CheckTracker::RecalculateAvailableChecks();
}

View file

@ -163,6 +163,7 @@ void Gfx_UnregisterBlendedTexture(const char* name);
void Gfx_TextureCacheDelete(const uint8_t* addr); void Gfx_TextureCacheDelete(const uint8_t* addr);
void SaveManager_ThreadPoolWait(); void SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose(); void CheckTracker_OnMessageClose();
void CheckTracker_RecalculateAvailableChecks();
GetItemID RetrieveGetItemIDFromItemID(ItemID itemID); GetItemID RetrieveGetItemIDFromItemID(ItemID itemID);
RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID); RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID);

View file

@ -342,10 +342,10 @@ void SohMenu::AddMenuEnhancements() {
.Options(CheckboxOptions().DefaultValue(IS_RANDO)); .Options(CheckboxOptions().DefaultValue(IS_RANDO));
AddWidget(path, "Exclude Glitch-Aiding Cutscenes", WIDGET_CVAR_CHECKBOX) AddWidget(path, "Exclude Glitch-Aiding Cutscenes", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding")) .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"))
.Options( .Options(CheckboxOptions().Tooltip(
CheckboxOptions().Tooltip("Don't skip cutscenes that are associated with useful glitches. Currently, it is " "Don't skip cutscenes that are associated with useful glitches. Currently, it is "
"only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, Dodongo Boss " "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, Dodongo Boss "
"Door Switch, and the Box Skip One Point in Jabu.")); "Door Switch CS, Water Temple Dragon Switch CS, and the Box Skip One Point in Jabu."));
AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Skip Pickup Messages", WIDGET_CVAR_CHECKBOX) AddWidget(path, "Skip Pickup Messages", WIDGET_CVAR_CHECKBOX)

View file

@ -145,7 +145,47 @@ void Audio_osWritebackDCache(void* mem, s32 size) {
} }
s32 osAiSetFrequency(u32 freq) { s32 osAiSetFrequency(u32 freq) {
return 1; // this is based off the math from the original method
/*
s32 osAiSetFrequency(u32 frequency) {
u8 bitrate;
f32 dacRateF = ((f32)osViClock / frequency) + 0.5f;
u32 dacRate = dacRateF;
if (dacRate < 132) {
return -1;
}
bitrate = (dacRate / 66);
if (bitrate > 16) {
bitrate = 16;
}
HW_REG(AI_DACRATE_REG, u32) = dacRate - 1;
HW_REG(AI_BITRATE_REG, u32) = bitrate - 1;
return osViClock / (s32)dacRate;
}
*/
// bitrate is unused
// osViClock comes from
// #define VI_NTSC_CLOCK 48681812 /* Hz = 48.681812 MHz */
// s32 osViClock = VI_NTSC_CLOCK;
// frequency was originally 32000
// given all of that, dacRate is
// (u32)(((f32)48681812 / 32000) + 0.5f)
// which evaluates to 1521 (which is > 132)
// this leaves us with a final calculation of
// 48681812 / 1521
// which evaluates to 32006
return 32006;
} }
void osInvalDCache(void* vaddr, s32 nbytes) { void osInvalDCache(void* vaddr, s32 nbytes) {

View file

@ -6,6 +6,7 @@
#include "z_bg_mori_kaitenkabe.h" #include "z_bg_mori_kaitenkabe.h"
#include "objects/object_mori_objects/object_mori_objects.h" #include "objects/object_mori_objects/object_mori_objects.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS 0 #define FLAGS 0
@ -97,7 +98,9 @@ void BgMoriKaitenkabe_Wait(BgMoriKaitenkabe* this, PlayState* play) {
if ((this->timer > (28 - CVarGetInteger(CVAR_ENHANCEMENT("FasterBlockPush"), 0) * 4)) && if ((this->timer > (28 - CVarGetInteger(CVAR_ENHANCEMENT("FasterBlockPush"), 0) * 4)) &&
!Player_InCsMode(play)) { !Player_InCsMode(play)) {
BgMoriKaitenkabe_SetupRotate(this); BgMoriKaitenkabe_SetupRotate(this);
Player_SetCsActionWithHaltedActors(play, &this->dyna.actor, 8); if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) {
Player_SetCsActionWithHaltedActors(play, &this->dyna.actor, 8);
}
Math_Vec3f_Copy(&this->lockedPlayerPos, &player->actor.world.pos); Math_Vec3f_Copy(&this->lockedPlayerPos, &player->actor.world.pos);
push.x = Math_SinS(this->dyna.unk_158); push.x = Math_SinS(this->dyna.unk_158);
push.y = 0.0f; push.y = 0.0f;
@ -131,7 +134,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) {
Math_StepToF(&this->rotSpeed, 0.6f, 0.02f); Math_StepToF(&this->rotSpeed, 0.6f, 0.02f);
if (Math_StepToF(&this->rotYdeg, this->rotDirection * 45.0f, this->rotSpeed)) { if (Math_StepToF(&this->rotYdeg, this->rotDirection * 45.0f, this->rotSpeed)) {
BgMoriKaitenkabe_SetupWait(this); BgMoriKaitenkabe_SetupWait(this);
Player_SetCsActionWithHaltedActors(play, thisx, 7); if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) {
Player_SetCsActionWithHaltedActors(play, thisx, 7);
}
if (this->rotDirection > 0.0f) { if (this->rotDirection > 0.0f) {
thisx->home.rot.y += 0x2000; thisx->home.rot.y += 0x2000;
} else { } else {
@ -148,7 +153,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) {
this->dyna.unk_150 = 0.0f; this->dyna.unk_150 = 0.0f;
player->stateFlags2 &= ~PLAYER_STATE2_MOVING_DYNAPOLY; player->stateFlags2 &= ~PLAYER_STATE2_MOVING_DYNAPOLY;
} }
Math_Vec3f_Copy(&player->actor.world.pos, &this->lockedPlayerPos); if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) {
Math_Vec3f_Copy(&player->actor.world.pos, &this->lockedPlayerPos);
}
} }
void BgMoriKaitenkabe_Update(Actor* thisx, PlayState* play) { void BgMoriKaitenkabe_Update(Actor* thisx, PlayState* play) {

View file

@ -131,8 +131,14 @@ void func_808BAF40(BgTokiSwd* this, PlayState* play) {
Item_Give(play, ITEM_SWORD_MASTER); Item_Give(play, ITEM_SWORD_MASTER);
} }
play->csCtx.segment = D_808BB2F0; play->csCtx.segment = D_808BB2F0;
// Discover adult spawn
Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false);
} else { } else {
play->csCtx.segment = D_808BB7A0; 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_STOP);
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_MASTER_SWORD); Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_MASTER_SWORD);

View file

@ -7,6 +7,7 @@
#include "z_demo_kekkai.h" #include "z_demo_kekkai.h"
#include "objects/object_demo_kekkai/object_demo_kekkai.h" #include "objects/object_demo_kekkai/object_demo_kekkai.h"
#include "scenes/dungeons/ganontika/ganontika_scene.h" #include "scenes/dungeons/ganontika/ganontika_scene.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ResourceManagerHelpers.h" #include "soh/ResourceManagerHelpers.h"
#define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED) #define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED)
@ -257,13 +258,15 @@ void DemoKekkai_TrialBarrierIdle(Actor* thisx, PlayState* play) {
CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider1.base); CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider1.base);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider1.base); CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider1.base);
if (this->collider2.base.acFlags & AC_HIT) { if (this->collider2.base.acFlags & AC_HIT) {
Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME); if (GameInteractor_Should(VB_PLAY_DISPEL_BARRIER_CS, true, this)) {
// "I got it" Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME);
LOG_STRING("当ったよ"); // "I got it"
this->actor.update = DemoKekkai_TrialBarrierDispel; LOG_STRING("当ったよ");
this->timer = 0; this->actor.update = DemoKekkai_TrialBarrierDispel;
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]); this->timer = 0;
gSaveContext.cutsceneTrigger = 1; play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]);
gSaveContext.cutsceneTrigger = 1;
}
} }
CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider2.base); CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider2.base);
func_8002F974(&this->actor, NA_SE_EV_TOWER_ENERGY - SFX_FLAG); func_8002F974(&this->actor, NA_SE_EV_TOWER_ENERGY - SFX_FLAG);

View file

@ -6,7 +6,7 @@
struct DemoKekkai; struct DemoKekkai;
typedef void (*DemoKekkaiUpdateFunc)(struct DemoKekkai* this, PlayState* play); typedef void (*DemoKekkaiUpdateFunc)(struct DemoKekkai* thisx, PlayState* play);
typedef struct DemoKekkai { typedef struct DemoKekkai {
/* 0x0000 */ Actor actor; /* 0x0000 */ Actor actor;

View file

@ -314,19 +314,21 @@ void func_80A5372C(EnHeishi2* this, PlayState* play) {
f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim);
Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f);
this->unk_2F2[0] = 200; if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) {
this->cameraId = Play_CreateSubCamera(play); this->unk_2F2[0] = 200;
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); this->cameraId = Play_CreateSubCamera(play);
Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT);
this->unk_280.x = 947.0f; Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE);
this->unk_280.y = 1195.0f; this->unk_280.x = 947.0f;
this->unk_280.z = 2682.0f; this->unk_280.y = 1195.0f;
this->unk_280.z = 2682.0f;
this->unk_28C.x = 1164.0f; this->unk_28C.x = 1164.0f;
this->unk_28C.y = 1145.0f; this->unk_28C.y = 1145.0f;
this->unk_28C.z = 3014.0f; this->unk_28C.z = 3014.0f;
Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C);
}
this->actionFunc = func_80A53850; this->actionFunc = func_80A53850;
} }
@ -334,11 +336,15 @@ void func_80A53850(EnHeishi2* this, PlayState* play) {
BgSpot15Saku* gate; BgSpot15Saku* gate;
SkelAnime_Update(&this->skelAnime); SkelAnime_Update(&this->skelAnime);
Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) {
Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C);
}
gate = (BgSpot15Saku*)this->gate; gate = (BgSpot15Saku*)this->gate;
if ((this->unk_2F2[0] == 0) || (gate->unk_168 == 0)) { if ((this->unk_2F2[0] == 0) || (gate->unk_168 == 0)) {
Play_ClearCamera(play, this->cameraId); if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, true)) {
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); Play_ClearCamera(play, this->cameraId);
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE);
}
Message_CloseTextbox(play); Message_CloseTextbox(play);
this->unk_30C = 1; this->unk_30C = 1;
Player_SetCsActionWithHaltedActors(play, NULL, 7); Player_SetCsActionWithHaltedActors(play, NULL, 7);
@ -479,23 +485,25 @@ void func_80A53DF8(EnHeishi2* this, PlayState* play) {
f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim);
Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f);
this->unk_2F2[0] = 200; if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) {
this->cameraId = Play_CreateSubCamera(play); this->unk_2F2[0] = 200;
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); this->cameraId = Play_CreateSubCamera(play);
Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT);
this->unk_2BC.x = -71.0f; Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE);
this->unk_280.x = -71.0f; this->unk_2BC.x = -71.0f;
this->unk_2BC.y = 571.0f; this->unk_280.x = -71.0f;
this->unk_280.y = 571.0f; this->unk_2BC.y = 571.0f;
this->unk_2BC.z = -1487.0f; this->unk_280.y = 571.0f;
this->unk_280.z = -1487.0f; this->unk_2BC.z = -1487.0f;
this->unk_298.x = 181.0f; this->unk_280.z = -1487.0f;
this->unk_28C.x = 181.0f; this->unk_298.x = 181.0f;
this->unk_298.y = 417.0f; this->unk_28C.x = 181.0f;
this->unk_28C.y = 417.0f; this->unk_298.y = 417.0f;
this->unk_298.z = -1079.0f; this->unk_28C.y = 417.0f;
this->unk_28C.z = -1079.0f; this->unk_298.z = -1079.0f;
Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); this->unk_28C.z = -1079.0f;
Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C);
}
this->actionFunc = func_80A53F30; this->actionFunc = func_80A53F30;
} }
@ -503,11 +511,15 @@ void func_80A53F30(EnHeishi2* this, PlayState* play) {
BgGateShutter* gate; BgGateShutter* gate;
SkelAnime_Update(&this->skelAnime); SkelAnime_Update(&this->skelAnime);
Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) {
Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C);
}
gate = (BgGateShutter*)this->gate; gate = (BgGateShutter*)this->gate;
if ((this->unk_2F2[0] == 0) || (gate->openingState == 0)) { if ((this->unk_2F2[0] == 0) || (gate->openingState == 0)) {
Play_ClearCamera(play, this->cameraId); if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, true)) {
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); Play_ClearCamera(play, this->cameraId);
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE);
}
if ((this->unk_30A != 2)) { if ((this->unk_30A != 2)) {
if (this->unk_30A == 0) { if (this->unk_30A == 0) {
this->actor.textId = 0x2015; this->actor.textId = 0x2015;

View file

@ -507,7 +507,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) {
this->collider2.base.ocFlags1 &= ~OC1_HIT; this->collider2.base.ocFlags1 &= ~OC1_HIT;
// "catch" // "catch"
osSyncPrintf(VT_FGCOL(GREEN) "キャッチ(%d)" VT_RST "\n", this->frameCount); 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; player->actor.parent = &this->actor;
this->stopScroll = false; this->stopScroll = false;
EnRr_SetupGrabPlayer(this, player); EnRr_SetupGrabPlayer(this, player);

View file

@ -706,7 +706,7 @@ void EnTorch2_Update(Actor* thisx, PlayState* play2) {
sStaggerCount = 0; 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) { if (this->actor.xzDistToPlayer > 80.0f) {
player->linearVelocity = 1.2f; player->linearVelocity = 1.2f;
} else if (this->actor.xzDistToPlayer < 70.0f) { } else if (this->actor.xzDistToPlayer < 70.0f) {

View file

@ -4764,7 +4764,9 @@ s32 func_808382DC(Player* this, PlayState* play) {
gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawnInfo->yaw; 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); 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) { if (exitIndex == 0) {
Play_TriggerVoidOut(play); if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
Play_TriggerVoidOut(play);
}
Scene_SetTransitionForNextEntrance(play); Scene_SetTransitionForNextEntrance(play);
} else { } else {
play->nextEntranceIndex = play->setupExitList[exitIndex - 1]; 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, SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2,
play->setupExitList[exitIndex - 1])) { play->setupExitList[exitIndex - 1])) {
gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = play->nextEntranceIndex; 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.respawnFlag = -2;
} }
gSaveContext.retainWeatherMode = 1; gSaveContext.retainWeatherMode = 1;
@ -5226,7 +5232,7 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol
if (this->actor.bgCheckFlags & 1) { if (this->actor.bgCheckFlags & 1) {
if (this->floorProperty == 5) { if (this->floorProperty == 5) {
Play_TriggerRespawn(play); Play_TriggerRespawn(play);
} else { } else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
Play_TriggerVoidOut(play); Play_TriggerVoidOut(play);
} }
play->transitionType = TRANS_TYPE_FADE_BLACK_FAST; play->transitionType = TRANS_TYPE_FADE_BLACK_FAST;
@ -9613,6 +9619,10 @@ static FallImpactInfo D_80854600[] = {
s32 func_80843E64(PlayState* play, Player* this) { s32 func_80843E64(PlayState* play, Player* this) {
s32 sp34; s32 sp34;
if (!GameInteractor_Should(VB_RECIEVE_FALL_DAMAGE, true, this)) {
return 0;
}
if ((sFloorType == 6) || (sFloorType == 9)) { if ((sFloorType == 6) || (sFloorType == 9)) {
sp34 = 0; sp34 = 0;
} else { } else {
@ -15009,7 +15019,7 @@ void Player_Action_8084F88C(Player* this, PlayState* play) {
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) {
Grotto_ForceRegularVoidOut(); Grotto_ForceRegularVoidOut();
} }
} else { } else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
Play_TriggerVoidOut(play); Play_TriggerVoidOut(play);
} }