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_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")

View file

@ -4,6 +4,7 @@ set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE)
project(soh LANGUAGES C CXX)
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")
enable_language(OBJCXX)

View file

@ -15,7 +15,7 @@ extern "C"
#include <soh/Enhancements/randomizer/randomizer_inf.h>
#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
#define osSyncPrintf(fmt, ...) osSyncPrintfUnused(fmt, ##__VA_ARGS__)
#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
ImVec2 columnSize = ImGui::CalcTextSize("Navi - Look/Hey/Watchout (Target Enemy)");
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 unlockedButton = ICON_FA_UNLOCK + hiddenKey;
const int currentValue = CVarGetInteger(cvarKey.c_str(), defaultValue);
const bool isCurrentlyPlaying = currentValue == playingFromMenu || seqData.sequenceId == currentBGM;
ImGui::TableNextRow();
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::PushItemWidth(-FLT_MIN);
const int initialValue = map.contains(currentValue) ? currentValue : defaultValue;

View file

@ -11,6 +11,7 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/cosmetics/CosmeticsEditor.h"
#include "soh/Enhancements/audio/AudioEditor.h"
#include "soh/Enhancements/randomizer/logic.h"
#define Path _Path
#define PATH_HACK
@ -1450,6 +1451,55 @@ static bool SfxHandler(std::shared_ptr<Ship::Console> Console, const std::vector
return 0;
}
static bool AvailableChecksProcessUndiscoveredExitsHandler(std::shared_ptr<Ship::Console> Console,
const std::vector<std::string>& args, std::string* output) {
const auto& logic = Rando::Context::GetInstance()->GetLogic();
bool enabled = false;
if (args.size() == 1) {
enabled = !logic->ACProcessUndiscoveredExits;
} else {
try {
enabled = std::stoi(args[1]);
} catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Enable should be 0 or 1");
return 1;
}
}
logic->ACProcessUndiscoveredExits = enabled;
INFO_MESSAGE("[SOH] Available Checks - Process Undiscovered Exits %s",
logic->ACProcessUndiscoveredExits ? "enabled" : "disabled");
if (GameInteractor::IsSaveLoaded(true)) {
CheckTracker::RecalculateAvailableChecks();
}
return 0;
}
static bool AvailableChecksRecalculateHandler(std::shared_ptr<Ship::Console> Console,
const std::vector<std::string>& args, std::string* output) {
RandomizerRegion startingRegion = RR_ROOT;
if (args.size() > 1) {
try {
startingRegion = static_cast<RandomizerRegion>(std::stoi(args[1]));
} catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Region should be a number");
return 1;
}
if (startingRegion <= RR_NONE || startingRegion >= RR_MAX) {
ERROR_MESSAGE("[SOH] Region should be between 1 and %d", RR_MAX - 1);
return 1;
}
}
CheckTracker::RecalculateAvailableChecks(startingRegion);
return 0;
}
void DebugConsole_Init(void) {
// Console
CMD_REGISTER("file_select", { FileSelectHandler, "Returns to the file select." });
@ -1708,5 +1758,15 @@ void DebugConsole_Init(void) {
{ "group_name", Ship::ArgumentType::TEXT, true },
} });
CMD_REGISTER("acpue", { AvailableChecksProcessUndiscoveredExitsHandler,
"Available Checks - Process Undiscovered Exits",
{ { "enable", Ship::ArgumentType::NUMBER, true } } });
CMD_REGISTER("acr", { AvailableChecksRecalculateHandler,
"Available Checks - Recalculate",
{
{ "starting_region", Ship::ArgumentType::NUMBER, true },
} });
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
}

View file

@ -11,8 +11,13 @@
extern "C" {
#include <z64.h>
#include "src/overlays/actors/ovl_En_Rr/z_en_rr.h"
}
#define CVAR_ENEMY_RANDOMIZER_NAME CVAR_ENHANCEMENT("RandomizedEnemies")
#define CVAR_ENEMY_RANDOMIZER_DEFAULT ENEMY_RANDOMIZER_OFF
#define CVAR_ENEMY_RANDOMIZER_VALUE CVarGetInteger(CVAR_ENEMY_RANDOMIZER_NAME, CVAR_ENEMY_RANDOMIZER_DEFAULT)
const char* enemyCVarList[] = {
CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"),
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.
uint32_t seed =
play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params;
EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed);
int8_t timesRandomized = 1;
// While randomized enemy isn't allowed in certain situations, randomize again.
while (!IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, randomEnemy)) {
randomEnemy = GetRandomizedEnemyEntry(seed + timesRandomized);
timesRandomized++;
}
EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed, play);
*actorId = randomEnemy.id;
*params = randomEnemy.params;
@ -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) {
GetSelectedEnemies();
}
if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM_SEEDED) {
for (EnemyEntry enemy : selectedEnemyList) {
if (IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, enemy)) {
filteredEnemyList.push_back(enemy);
}
}
if (filteredEnemyList.size() == 0) {
filteredEnemyList = selectedEnemyList;
}
if (CVAR_ENEMY_RANDOMIZER_VALUE == ENEMY_RANDOMIZER_RANDOM_SEEDED) {
uint32_t finalSeed =
seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt);
Random_Init(finalSeed);
uint32_t randomNumber = Random(0, selectedEnemyList.size());
return selectedEnemyList[randomNumber];
uint32_t randomNumber = Random(0, filteredEnemyList.size());
return filteredEnemyList[randomNumber];
} else {
uint32_t randomSelectedEnemy = Random(0, selectedEnemyList.size());
return selectedEnemyList[randomSelectedEnemy];
uint32_t randomSelectedEnemy = Random(0, filteredEnemyList.size());
return filteredEnemyList[randomSelectedEnemy];
}
}
@ -532,3 +538,45 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
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
#include <libultraship/bridge.h>
#include "item-tables/ItemTableTypes.h"
typedef struct EnemyEntry {
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 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* enemyNameList[];

View file

@ -474,6 +474,14 @@ typedef enum {
// - `*BgHeavyBlock`
VB_FREEZE_LINK_FOR_BLOCK_THROW,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - None
VB_FREEZE_LINK_FOR_FOREST_PILLARS,
// #### `result`
// ```c
// true
@ -1358,6 +1366,14 @@ typedef enum {
// - `*BgTreemouth`
VB_PLAY_DEKU_TREE_INTRO_CS,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*DemoKekkai`
VB_PLAY_DISPEL_BARRIER_CS,
// #### `result`
// ```c
// true
@ -1422,6 +1438,15 @@ typedef enum {
// - None
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`
// ```c
// true
@ -1986,6 +2011,38 @@ typedef enum {
// #### `args`
// - `*EnWonderTalk2`
VB_WONDER_TALK,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*Actor`
VB_TRIGGER_VOIDOUT,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*Actor`
VB_TORCH2_HANDLE_CLANKING,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*Actor`
VB_RECIEVE_FALL_DAMAGE,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*EnRr`
VB_LIKE_LIKE_GRAB_PLAYER,
} GIVanillaBehavior;
#endif

View file

@ -99,6 +99,13 @@ void SwitchAge() {
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK_FAST;
gPlayState->linkAgeOnLoad ^= 1;
// Discover adult/child spawns
if (gPlayState->linkAgeOnLoad == LINK_AGE_ADULT) {
Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false);
} else {
Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false);
}
static HOOK_ID hookId = 0;
hookId = REGISTER_VB_SHOULD(VB_INFLICT_VOID_DAMAGE, {
*should = false;

View file

@ -209,6 +209,13 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer
bool stopOnBeatable = false, bool addToPlaythrough = false) {
auto ctx = Rando::Context::GetInstance();
for (auto& exit : region->exits) {
int16_t entranceIndex = exit.GetIndex();
if (!logic->ACProcessUndiscoveredExits && logic->CalculatingAvailableChecks &&
ctx->GetOption(RSK_SHUFFLE_ENTRANCES).Get() && exit.IsShuffled() && entranceIndex != -1 &&
!Entrance_GetIsEntranceDiscovered(entranceIndex)) {
continue;
}
Region* exitRegion = exit.GetConnectedRegion();
// Update Time of Day Access for the exit
if (UpdateToDAccess(&exit, exitRegion)) {
@ -421,18 +428,13 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals
Rando::ItemLocation* location = ctx->GetItemLocation(loc);
RandomizerGet locItem = location->GetPlacedRandomizerGet();
if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) {
if (gals.calculatingAvailableChecks) {
gals.accessibleLocations.push_back(loc);
StopPerformanceTimer(PT_LOCATION_LOGIC);
return false;
}
if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, logic->CalculatingAvailableChecks)) {
location->AddToPool();
if (locItem == RG_NONE) {
if (locItem == RG_NONE || logic->CalculatingAvailableChecks) {
gals.accessibleLocations.push_back(loc); // Empty location, consider for placement
} else {
}
if (locItem != RG_NONE) {
// If ignore has a value, we want to check if the item location should be considered or not
// This is necessary due to the below preprocessing for playthrough generation
if (ignore != RG_NONE) {
@ -528,11 +530,32 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize
// Return any of the targetLocations that are accessible in logic
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& targetLocations,
RandomizerGet ignore /* = RG_NONE*/,
bool calculatingAvailableChecks /* = false */) {
bool calculatingAvailableChecks /* = false */,
RandomizerRegion startingRegion /* = RR_ROOT */) {
auto ctx = Rando::Context::GetInstance();
GetAccessibleLocationsStruct gals(0);
gals.calculatingAvailableChecks = calculatingAvailableChecks;
ResetLogic(ctx, gals, !calculatingAvailableChecks);
if (startingRegion != RR_ROOT) {
gals.regionPool.insert(gals.regionPool.begin(), startingRegion);
const auto& region = RegionTable(startingRegion);
if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) {
region->childDay = true;
} else {
region->adultDay = true;
}
if (region->timePass) {
if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) {
region->childNight = true;
} else {
region->adultNight = true;
}
}
}
if (calculatingAvailableChecks) {
logic->Reset(false);
logic->CalculatingAvailableChecks = true;
}
do {
gals.InitLoop();
for (size_t i = 0; i < gals.regionPool.size(); i++) {

View file

@ -34,8 +34,6 @@ struct GetAccessibleLocationsStruct {
std::vector<RandomizerCheck> itemSphere;
std::list<Rando::Entrance*> entranceSphere;
bool calculatingAvailableChecks = false;
GetAccessibleLocationsStruct(int _maxGsCount){
regionPool = {RR_ROOT};
gsCount = 0;
@ -58,16 +56,17 @@ struct GetAccessibleLocationsStruct {
void ClearProgress();
void VanillaFill();
int Fill();
void SetAreas();
std::vector<RandomizerCheck> GetEmptyLocations(std::vector<RandomizerCheck> allowedLocations);
void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, RandomizerGet ignore = RG_NONE,
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();
bool CheckBeatable(RandomizerGet ignore=RG_NONE);
void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess);
void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess);

View file

@ -2412,13 +2412,13 @@ void StaticData::HintTable_Init() {
hintTextTable[RHT_ISOLATED_PLACE] = HintText(CustomMessage("an Isolated Place"));
hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage(" It's ordinary.",
/*german*/ "&Sieht aus wie immer.",
/*french*/ "&Elle vous semble %rordinaire%w."));
hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage("&It's %gordinary%w.",
/*german*/ "&Sieht aus %gwie immer%w.",
/*french*/ "&Elle vous semble %gordinaire%w."));
hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage(" It's masterful!",
/*german*/ "&Man kann darauf die Worte&%r\"Master Quest\"%w entziffern...",
/*french*/ "&Étrange... les mots %r\"Master&Quest\"%w sont gravés dessus."));
hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage("&It's %rmasterful%w!",
/*german*/ "&Man kann darauf die Worte %r\"Master_Quest\"%w entziffern...",
/*french*/ "&Étrange... les mots %r\"Master_Quest\"%w sont gravés dessus."));
// 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_2] = Location::Crate(RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-389, 1470), "Near Impas House Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2));
locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-433, -401), "Near Bazaar Adult Crate 1", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_1));
locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2`", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2));
locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2));
locationTable[RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE] = Location::Crate(RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-724, 871), "Behind GS House Adult Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_BEHIND_GS_HOUSE_ADULT_CRATE));
locationTable[RC_KAK_NEAR_GY_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_GY_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1732, 1366), "Near Graveyard Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_GY_CHILD_CRATE));
locationTable[RC_KAK_NEAR_WINDMILL_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_WINDMILL_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1170, 601), "Near Windmill Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_WINDMILL_CHILD_CRATE));

View file

@ -400,8 +400,9 @@ void Context::ParseSpoiler(const char* spoilerFileName) {
ParseHashIconIndexesJson(spoilerFileJson);
Rando::Settings::GetInstance()->ParseJson(spoilerFileJson);
ParseItemLocationsJson(spoilerFileJson);
ParseHintJson(spoilerFileJson);
ParseTricksJson(spoilerFileJson);
mEntranceShuffler->ParseJson(spoilerFileJson);
ParseHintJson(spoilerFileJson);
mDungeons->ParseJson(spoilerFileJson);
mTrials->ParseJson(spoilerFileJson);
mSpoilerLoaded = true;
@ -469,6 +470,17 @@ void Context::ParseHintJson(nlohmann::json spoilerFileJson) {
CreateStaticHints();
}
void Context::ParseTricksJson(nlohmann::json spoilerFileJson) {
nlohmann::json enabledTricksJson = spoilerFileJson["enabledTricks"];
const auto& settings = Rando::Settings::GetInstance();
for (auto it : enabledTricksJson) {
int rt = settings->GetRandomizerTrickByName(it);
if (rt != -1) {
mTrickOptions[rt].Set(RO_GENERIC_ON);
}
}
}
std::shared_ptr<EntranceShuffler> Context::GetEntranceShuffler() {
return mEntranceShuffler;
}
@ -524,6 +536,10 @@ RandoOptionLACSCondition Context::LACSCondition() const {
return mLACSCondition;
}
void Context::LACSCondition(RandoOptionLACSCondition lacsCondition) {
mLACSCondition = lacsCondition;
}
std::shared_ptr<Kaleido> Context::GetKaleido() {
if (mKaleido == nullptr) {
mKaleido = std::make_shared<Kaleido>();

View file

@ -104,12 +104,22 @@ class Context {
* @return RandoOptionLACSCondition
*/
RandoOptionLACSCondition LACSCondition() const;
/**
* @brief Sets the resolved Light Arrow CutScene check condition.
* There is no direct option for this, it is inferred based on the value of a few other options.
*
* @param lacsCondition
*/
void LACSCondition(RandoOptionLACSCondition lacsCondition);
GetItemEntry GetFinalGIEntry(RandomizerCheck rc, bool checkObtainability = true, GetItemID ogItemId = GI_NONE);
void ParseSpoiler(const char* spoilerFileName);
void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson);
void ParseItemLocationsJson(nlohmann::json spoilerFileJson);
void WriteHintJson(nlohmann::ordered_json& spoilerFileJson);
void ParseHintJson(nlohmann::json spoilerFileJson);
void ParseTricksJson(nlohmann::json spoilerFileJson);
std::map<RandomizerCheck, ItemOverride> overrides = {};
std::vector<std::vector<RandomizerCheck>> playthroughLocations = {};
std::vector<RandomizerCheck> everyPossibleLocation = {};

View file

@ -250,7 +250,319 @@ std::string EntranceNameByRegions(RandomizerRegion parentRegion, RandomizerRegio
return RegionTable(parentRegion)->regionName + " -> " + RegionTable(connectedRegion)->regionName;
}
void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
std::unordered_map<int16_t, Entrance*> entranceMap;
void SetAllEntrancesData() {
std::vector<EntranceInfoPair> entranceShuffleTable = {
// clang-format off
// Type Parent Region Connected Region Index
{ { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE },
{ EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } },
{ { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE },
{ EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } },
{ { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE },
{ EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } },
{ { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE },
{ EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } },
{ { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE },
{ EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } },
{ { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE },
{ EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } },
{ { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE },
{ EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 },
{ EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 },
{ EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 },
{ EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 },
{ EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 },
{ EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } },
{ { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 },
{ EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } },
{ { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 },
{ EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } },
{ { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 },
{ EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } },
{ { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 },
{ EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 },
{ EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 },
{ EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 },
{ EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 },
{ EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } },
{ { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 },
{ EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } },
{ { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE },
{ EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 },
{ EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 },
{ EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT },
{ EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } },
{ { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK },
{ EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } },
{ { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 },
{ EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } },
{ { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 },
{ EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } },
{ { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 },
{ EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } },
{ { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 },
{ EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } },
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE },
{ EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } },
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 },
{ EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } },
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER },
{ EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 },
{ EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 },
{ EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 },
{ EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 },
{ EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } },
{ { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS },
{ EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } },
{ { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC },
{ EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } },
{ { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD },
// 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy
// fountain exits (normally they both use 0x340)
{ EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } },
{ { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC },
{ EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } },
{ { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT },
{ EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } },
{ { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF },
{ EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } },
{ { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 },
{ EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } },
{ { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE },
{ EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } },
{ { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL },
{ EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } },
{ { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT },
{ EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } },
{ { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK },
{ EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } },
// Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the
// grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
// Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the
// grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
{ { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) },
{ EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) },
{ EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) },
{ EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) },
{ EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) },
{ EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) },
{ EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) },
{ EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) },
{ EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) },
{ EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) },
{ EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) },
{ EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) },
{ EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) },
{ EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) },
{ EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) },
{ EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) },
{ EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) },
{ EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } },
// Graves have their own specified entrance indices
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 },
{ EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } },
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 },
{ EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } },
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 },
{ EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } },
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE },
{ EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } },
{ { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT },
{ EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } },
{ { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT },
{ EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } },
{ { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT },
{ EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } },
{ { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT },
{ EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } },
{ { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT },
{ EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } },
{ { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT },
{ EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT },
{ EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT },
{ EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT },
{ EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE },
{ EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT },
{ EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE },
{ EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } },
{ { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT },
{ EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } },
{ { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT },
{ EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } },
{ { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT },
{ EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } },
{ { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT },
{ EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } },
{ { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT },
{ EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } },
{ { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT },
{ EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } },
{ { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT },
{ EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } },
{ { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE },
{ EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } },
{ { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT },
{ EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } },
{ { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT },
{ EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } },
{ { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT },
{ EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } },
{ { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT },
{ EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } },
{ { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE },
{ EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } },
{ { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT },
{ EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } },
{ { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT },
NO_RETURN_ENTRANCE },
{ { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP },
NO_RETURN_ENTRANCE },
{ { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP },
NO_RETURN_ENTRANCE },
{ { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN },
NO_RETURN_ENTRANCE },
{ { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 },
NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between
// Adult Spawn and prelude of light (normally they both use 0x5F4)
{ { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE },
{ EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT, ENTR_DEKU_TREE_BOSS_DOOR } },
{ { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE },
{ EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT, ENTR_DODONGOS_CAVERN_BOSS_DOOR } },
{ { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE },
{ EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT, ENTR_JABU_JABU_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } },
{ { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
// clang-format on
};
auto ctx = Rando::Context::GetInstance();
for (auto& entrancePair : entranceShuffleTable) {
@ -262,6 +574,7 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
forwardEntrance->SetIndex(forwardEntry.index);
forwardEntrance->SetType(forwardEntry.type);
forwardEntrance->SetAsPrimary();
entranceMap[forwardEntry.index] = forwardEntrance;
// When decouple entrances is on, mark the forward entrance
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
@ -273,6 +586,7 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
returnEntrance->SetIndex(returnEntry.index);
returnEntrance->SetType(returnEntry.type);
forwardEntrance->BindTwoWay(returnEntrance);
entranceMap[returnEntry.index] = returnEntrance;
// Mark reverse entrance as decoupled
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
@ -857,316 +1171,6 @@ int EntranceShuffler::ShuffleAllEntrances() {
mTotalRandomizableEntrances = 0;
mCurNumRandomizedEntrances = 0;
std::vector<EntranceInfoPair> entranceShuffleTable = {
// clang-format off
// Type Parent Region Connected Region Index
{ { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE },
{ EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } },
{ { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE },
{ EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } },
{ { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE },
{ EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } },
{ { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE },
{ EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } },
{ { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE },
{ EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } },
{ { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE },
{ EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } },
{ { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE },
{ EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } },
{ { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE },
{ EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 },
{ EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 },
{ EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 },
{ EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 },
{ EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } },
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 },
{ EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } },
{ { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 },
{ EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } },
{ { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 },
{ EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } },
{ { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 },
{ EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } },
{ { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 },
{ EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 },
{ EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 },
{ EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 },
{ EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 },
{ EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } },
{ { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 },
{ EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } },
{ { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE },
{ EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 },
{ EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 },
{ EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT },
{ EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } },
{ { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK },
{ EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } },
{ { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 },
{ EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } },
{ { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 },
{ EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } },
{ { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 },
{ EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } },
{ { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 },
{ EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } },
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE },
{ EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } },
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 },
{ EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } },
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER },
{ EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 },
{ EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } },
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 },
{ EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 },
{ EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } },
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 },
{ EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } },
{ { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS },
{ EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } },
{ { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC },
{ EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } },
{ { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD },
// 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy
// fountain exits (normally they both use 0x340)
{ EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } },
{ { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC },
{ EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } },
{ { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT },
{ EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } },
{ { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF },
{ EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } },
{ { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 },
{ EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } },
{ { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE },
{ EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } },
{ { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL },
{ EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } },
{ { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT },
{ EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } },
{ { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK },
{ EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } },
// Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the
// grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
// Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the
// grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
{ { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) },
{ EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) },
{ EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) },
{ EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) },
{ EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) },
{ EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) },
{ EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) },
{ EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) },
{ EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) },
{ EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) },
{ EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) },
{ EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) },
{ EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) },
{ EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) },
{ EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) },
{ EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) },
{ EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) },
{ EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) },
{ EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } },
{ { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) },
{ EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } },
// Graves have their own specified entrance indices
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 },
{ EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } },
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 },
{ EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } },
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 },
{ EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } },
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE },
{ EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } },
{ { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT },
{ EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } },
{ { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT },
{ EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } },
{ { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT },
{ EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } },
{ { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT },
{ EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } },
{ { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT },
{ EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } },
{ { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT },
{ EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT },
{ EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT },
{ EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT },
{ EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE },
{ EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT },
{ EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } },
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE },
{ EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } },
{ { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT },
{ EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } },
{ { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT },
{ EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } },
{ { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT },
{ EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } },
{ { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT },
{ EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } },
{ { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT },
{ EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } },
{ { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT },
{ EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } },
{ { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT },
{ EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } },
{ { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE },
{ EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } },
{ { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT },
{ EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } },
{ { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT },
{ EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } },
{ { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT },
{ EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } },
{ { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT },
{ EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } },
{ { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE },
{ EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } },
{ { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT },
{ EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } },
{ { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT },
NO_RETURN_ENTRANCE },
{ { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP },
NO_RETURN_ENTRANCE },
{ { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP },
NO_RETURN_ENTRANCE },
{ { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN },
NO_RETURN_ENTRANCE },
{ { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 },
NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between
// Adult Spawn and prelude of light (normally they both use 0x5F4)
{ { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD },
NO_RETURN_ENTRANCE },
{ { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE },
{ EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, ENTR_DEKU_TREE_BOSS_DOOR } },
{ { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE },
{ EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, ENTR_DODONGOS_CAVERN_BOSS_DOOR } },
{ { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE },
{ EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, ENTR_JABU_JABU_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } },
{ { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE },
{ EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } },
{ { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
{ { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP },
NO_RETURN_ENTRANCE },
// clang-format on
};
std::map<std::string, PriorityEntrance> priorityEntranceTable = {
{ "Bolero", { { RR_DMC_CENTRAL_LOCAL }, { EntranceType::OwlDrop, EntranceType::WarpSong } } },
{ "Nocturne",
@ -1178,7 +1182,7 @@ int EntranceShuffler::ShuffleAllEntrances() {
};
mEntranceShuffleFailure = false;
SetAllEntrancesData(entranceShuffleTable);
SetAllEntrancesData();
EntrancePools oneWayEntrancePools = {};
EntrancePools entrancePools = {};
@ -1488,11 +1492,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
if (true /* ctx->GetOption(RSK_SHUFFLE_BLUEWARP_ENTRANCES).Is(RO_BLUEWARP_ENTRANCE_SHUFFLE_DUNGEON) */) {
// If a boss room is inside a boss door, make the blue warp go outside the dungeon's entrance
std::map<std::string, Entrance*> bossExits = {
{ EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY),
{ EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT),
GetEntrance(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)) },
{ 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)) },
{ EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW)) },
@ -1530,11 +1534,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
// Pair <BlueWarp exit, BossRoom reverse exit>
std::vector<EntrancePair> bossRoomExitPairs = {
{ GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)),
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY)) },
GetEntrance(EntranceNameByRegions(RR_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_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_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_FOREST_TEMPLE_BOSS_ENTRYWAY)) },
{ 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; }
// 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

View file

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

View file

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

View file

@ -10,9 +10,10 @@
#include "soh/Enhancements/debugger/performanceTimer.h"
#include <fstream>
#include <soh/OTRGlobals.h>
#include "3drando/shops.hpp"
extern "C" {
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}
@ -45,17 +46,71 @@ bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailab
conditionsMet = true;
}
return conditionsMet &&
(calculatingAvailableChecks || CanBuy()); // TODO: run CanBuy when price is known due to settings
return conditionsMet && CanBuy(calculatingAvailableChecks);
}
bool LocationAccess::CanBuy() const {
return CanBuyAnother(location);
static uint16_t GetMinimumPrice(const Rando::Location* loc) {
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) {
uint16_t price = ctx->GetItemLocation(rc)->GetPrice();
return CanBuyAnother(ctx->GetItemLocation(rc)->GetPrice());
}
bool CanBuyAnother(uint16_t price) {
if (price > 500) {
return logic->HasItem(RG_TYCOON_WALLET);
} else if (price > 200) {
@ -275,7 +330,7 @@ bool BeanPlanted(const RandomizerRegion region) {
if (gPlayState != nullptr && gPlayState->sceneNum == sceneID) {
swch = gPlayState->actorCtx.flags.swch;
} else if (sceneID != SCENE_ID_MAX) {
swch = gSaveContext.sceneFlags[sceneID].swch;
swch = Rando::Context::GetInstance()->GetLogic()->GetSaveContext()->sceneFlags[sceneID].swch;
} else {
swch = 0;
}

View file

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

View file

@ -25,7 +25,6 @@ void RegionTable_Init_BottomOfTheWell() {
}, {
//Locations
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_LEFT_CHEST, logic->LoweredWaterInsideBotw),
LOCATION(RC_BOTTOM_OF_THE_WELL_NEAR_ENTRANCE_POT_1, logic->CanBreakPots()),

View file

@ -436,11 +436,16 @@ void RegionTable_Init_DekuTree() {
#pragma endregion
// Boss Room
// RANDOTODO make it so entrance randomiser can properly handle more than 1 access to that entrance
areaTable[RR_DEKU_TREE_BOSS_ENTRYWAY] = Region("Deku Tree Boss Entryway", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}),
});
areaTable[RR_DEKU_TREE_BOSS_EXIT] = Region("Deku Tree Boss Exit", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_DEKU_TREE_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsVanilla();}),
Entrance(RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsMQ();}),
Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}),
});
areaTable[RR_DEKU_TREE_BOSS_ROOM] = Region("Deku Tree Boss Room", "Deku Tree", {}, NO_DAY_NIGHT_CYCLE, {
@ -460,8 +465,8 @@ void RegionTable_Init_DekuTree() {
LOCATION(RC_DEKU_TREE_QUEEN_GOHMA_GRASS_8, logic->CanCutShrubs()),
}, {
// Exits
Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, []{return true;}),
Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false),
Entrance(RR_DEKU_TREE_BOSS_EXIT, []{return true;}),
Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false),
});
// clang-format on

View file

@ -268,6 +268,7 @@ void RegionTable_Init_DodongosCavern() {
LOCATION(RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT, logic->CanStunDeku()),
}, {
//Exits
Entrance(RR_DODONGOS_CAVERN_MQ_BEGINNING, []{return true;}),
Entrance(RR_DODONGOS_CAVERN_MQ_GOSSIP_STONE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET);});}),
Entrance(RR_DODONGOS_CAVERN_MQ_MOUTH_SIDE_BRIDGE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}),
Entrance(RR_DODONGOS_CAVERN_MQ_STAIRS_LOWER, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}),
@ -386,7 +387,7 @@ void RegionTable_Init_DodongosCavern() {
Entrance(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanUse(RG_STICKS) && logic->HasItem(RG_GORONS_BRACELET);}), //Implies access to RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM from here
});
areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Torch Puzzle Lower", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Big Block Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_1, logic->CanBreakPots()),
LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_2, logic->CanBreakPots()),
@ -426,7 +427,7 @@ void RegionTable_Init_DodongosCavern() {
Entrance(RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM, []{return Here(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanKillEnemy(RE_LIZALFOS);});}),
});
areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Before Upper Lizalfos", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Two Fires Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_1, logic->CanBreakPots()),
LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_2, logic->CanBreakPots()),
@ -558,11 +559,16 @@ void RegionTable_Init_DodongosCavern() {
#pragma endregion
// Boss Room
// RANDOTODO make it so entrance randomiser can properly handle more than 1 access to that entrance
areaTable[RR_DODONGOS_CAVERN_BOSS_ENTRYWAY] = Region("Dodongos Cavern Boss Entryway", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}),
});
areaTable[RR_DODONGOS_CAVERN_BOSS_EXIT] = Region("Dodongos Cavern Boss Exit", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_DODONGOS_CAVERN_BOSS_AREA, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsVanilla();}),
Entrance(RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsMQ();}),
Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}),
});
areaTable[RR_DODONGOS_CAVERN_BOSS_ROOM] = Region("Dodongos Cavern Boss Room", "Dodongos Cavern", {}, NO_DAY_NIGHT_CYCLE, {
@ -575,8 +581,8 @@ void RegionTable_Init_DodongosCavern() {
LOCATION(RC_KING_DODONGO, logic->DodongosCavernClear),
}, {
// Exits
Entrance(RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, []{return true;}),
Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false),
Entrance(RR_DODONGOS_CAVERN_BOSS_EXIT, []{return true;}),
Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false),
});
// clang-format on

View file

@ -20,7 +20,7 @@ void RegionTable_Init_FireTemple() {
//Exits
Entrance(RR_FIRE_TEMPLE_ENTRYWAY, []{return true;}),
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_BIG_LAVA_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 2) && logic->FireTimer() >= 24;}),
});
@ -38,12 +38,12 @@ void RegionTable_Init_FireTemple() {
}, {
//Exits
Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return true;}),
Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}))) || logic->CanUse(RG_HOVER_BOOTS));}),
Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}) || logic->CanUse(RG_HOVER_BOOTS));}),
});
areaTable[RR_FIRE_TEMPLE_LOOP_ENEMIES] = Region("Fire Temple Loop Enemies", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//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);});}),
});
@ -445,7 +445,7 @@ void RegionTable_Init_FireTemple() {
Entrance(RR_FIRE_TEMPLE_MQ_FIRST_ROOM_UPPER, []{return true;}),
//Child cannot make it to the north side torches without a hook without specifically bunny hood speed + hover boots
Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM_NORTH, []{return logic->FireTimer() > 32 && (logic->CanUse(RG_HOOKSHOT) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)));}),
Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}),
Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}),
});
//This room assumes tunic logic is handled on entry.
@ -701,7 +701,7 @@ void RegionTable_Init_FireTemple() {
Entrance(RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER, []{return true;}),
});
areaTable[RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER] = Region("Fire Temple MQ North Fire Maze", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER] = Region("Fire Temple MQ Upper Flare Dancer", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_FIRE_TEMPLE_MQ_FREESTANDING_KEY, logic->CanKillEnemy(RE_FLARE_DANCER)),
}, {
@ -740,7 +740,7 @@ void RegionTable_Init_FireTemple() {
// Exits
Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsMQ() && false;}),
Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return true;}),
Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY);}),
});
areaTable[RR_FIRE_TEMPLE_BOSS_ROOM] = Region("Fire Temple Boss Room", "Fire Temple", {}, NO_DAY_NIGHT_CYCLE, {

View file

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

View file

@ -29,12 +29,7 @@ void RegionTable_Init_GanonsCastle() {
Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, []{return true;}),
Entrance(RR_GANONS_CASTLE_SPIRIT_TRIAL, []{return true;}),
Entrance(RR_GANONS_CASTLE_LIGHT_TRIAL, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}),
Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
(logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
(logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
(logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
(logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
(logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}),
Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}),
Entrance(RR_GANONS_CASTLE_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON) || logic->CanUse(RG_LENS_OF_TRUTH);}),
});
@ -162,13 +157,7 @@ void RegionTable_Init_GanonsCastle() {
Entrance(RR_GANONS_CASTLE_MQ_SHADOW_TRIAL_STARTING_LEDGE, []{return true;}),
Entrance(RR_GANONS_CASTLE_MQ_SPIRIT_TRIAL_CHAIRS_ROOM, []{return true;}),
Entrance(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_DINOLFOS_ROOM, []{return Here(RR_GANONS_CASTLE_MQ_MAIN, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);});}),
//RANDOTODO could we just set these events automatically based on the setting?
Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
(logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
(logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
(logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
(logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
(logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}),
Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}),
Entrance(RR_GANONS_CASTLE_MQ_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON_MQ) || logic->CanUse(RG_LENS_OF_TRUTH);}),
});
@ -436,7 +425,13 @@ void RegionTable_Init_GanonsCastle() {
//Exits
Entrance(RR_GANONS_CASTLE_LOBBY, []{return ctx->GetDungeon(GANONS_CASTLE)->IsVanilla();}),
Entrance(RR_GANONS_CASTLE_MQ_MAIN, []{return ctx->GetDungeon(GANONS_CASTLE)->IsMQ();}),
Entrance(RR_GANONS_TOWER_FLOOR_1, []{return true;}),
//RANDOTODO could we just set these events automatically based on the setting?
Entrance(RR_GANONS_TOWER_FLOOR_1, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
(logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
(logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
(logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
(logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
(logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}),
});
areaTable[RR_GANONS_TOWER_FLOOR_1] = Region("Ganon's Tower Floor 1", "Ganons Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {

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);}),
});
areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, {
areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Maze Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&logic->MQGTGMazeSwitch, []{return logic->CanUse(RG_MEGATON_HAMMER);}),
},

View file

@ -84,7 +84,7 @@ void RegionTable_Init_IceCavern() {
//Exits
//the switch for the glass blocking the entrance is linked to the switch that controls the glass around the skulltulla in RR_ICE_CAVERN_MQ_SCARECROW_ROOM
//if you clear the ice, you can hit it with a pot from here.
Entrance(RR_ICE_CAVERN_BEGINNING, []{return logic->BlueFire();}),
Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->BlueFire();}),
Entrance(RR_ICE_CAVERN_MQ_MAP_ROOM, []{return Here(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->CanKillEnemy(RE_WHITE_WOLFOS) && logic->CanKillEnemy(RE_FREEZARD);});}),
Entrance(RR_ICE_CAVERN_MQ_COMPASS_ROOM, []{return logic->IsAdult && logic->BlueFire();}),
Entrance(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, []{return logic->BlueFire();}),

View file

@ -351,10 +351,14 @@ void RegionTable_Init_JabuJabusBelly() {
// Boss Room
areaTable[RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY] = Region("Jabu Jabus Belly Boss Entryway", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}),
});
areaTable[RR_JABU_JABUS_BELLY_BOSS_EXIT] = Region("Jabu Jabus Belly Boss Exit", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsVanilla();}),
Entrance(RR_JABU_JABUS_BELLY_MQ_EAST_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsMQ();}),
Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}),
});
areaTable[RR_JABU_JABUS_BELLY_BOSS_ROOM] = Region("Jabu Jabus Belly Boss Room", "Jabu Jabus Belly", {}, NO_DAY_NIGHT_CYCLE, {
@ -372,8 +376,8 @@ void RegionTable_Init_JabuJabusBelly() {
LOCATION(RC_BARINADE, logic->JabuJabusBellyClear),
}, {
// Exits
Entrance(RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, []{return false;}),
Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false),
Entrance(RR_JABU_JABUS_BELLY_BOSS_EXIT, []{return false;}),
Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false),
});
// clang-format on

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))),
}, {
//Exits
Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS) && logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);})
Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS);})
});
#pragma endregion
@ -370,7 +370,7 @@ void RegionTable_Init_ShadowTemple() {
}, {
//Exits
Entrance(RR_SHADOW_TEMPLE_MQ_ACROSS_CHASM, []{return logic->CanUse(RG_HOVER_BOOTS) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH));}),
Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}),
Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
//Assumes lens is checked on entry
@ -403,7 +403,7 @@ void RegionTable_Init_ShadowTemple() {
// Exits
Entrance(RR_SHADOW_TEMPLE_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_SHADOW_TEMPLE_MQ_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsMQ() && false;}),
Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return true;}),
Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}),
});
areaTable[RR_SHADOW_TEMPLE_BOSS_ROOM] = Region("Shadow Temple Boss Room", "Shadow Temple", {}, NO_DAY_NIGHT_CYCLE, {

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, {}, {}, {
// Exits
Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
#pragma endregion
@ -194,13 +194,13 @@ void RegionTable_Init_SpiritTemple() {
});
// Room to store the 2 pots in to handle glitch logic going backwards around the loop later
areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ 1F Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_1, logic->CanBreakPots()),
LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_2, logic->CanBreakPots()),
}, {});
areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple MQ Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
//Events
//For non-fairy pot items, you can also get them with rang without killing the stalfos
EventAccess(&logic->FairyPot, []{return Here(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}),
@ -315,7 +315,7 @@ void RegionTable_Init_SpiritTemple() {
Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
});
areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ West Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}),
Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}),
@ -541,7 +541,7 @@ void RegionTable_Init_SpiritTemple() {
areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
// Exits
Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
#pragma endregion
@ -551,7 +551,7 @@ void RegionTable_Init_SpiritTemple() {
// Exits
Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}),
Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
});
areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", "Spirit Temple", {}, NO_DAY_NIGHT_CYCLE, {

View file

@ -277,7 +277,7 @@ void RegionTable_Init_WaterTemple() {
}, {
//Exits
Entrance(RR_WATER_TEMPLE_LOBBY, []{return true;}),
Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}),
Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
#pragma endregion
@ -362,10 +362,10 @@ void RegionTable_Init_WaterTemple() {
Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_ICE_ARROWS) || logic->CanUse(RG_NAYRUS_LOVE);}),
});
areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Main", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Boss Door", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_WATER_TEMPLE_MQ_3F_NORTH_LEDGE, []{return logic->CanUse(RG_ICE_ARROWS) || logic->TakeDamage();}),
Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}),
Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
});
areaTable[RR_WATER_TEMPLE_MQ_EAST_TOWER] = Region("Water Temple MQ East Tower", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {
@ -507,7 +507,7 @@ void RegionTable_Init_WaterTemple() {
Entrance(RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F, []{return logic->CanUse(RG_LONGSHOT);}),
});
areaTable[RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F] = Region("Water Temple MQ Behind Blue Switch 2F", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
areaTable[RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F] = Region("Water Temple MQ Behind Blue Switch 3F", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_WATER_TEMPLE_MQ_GS_BEFORE_UPPER_WATER_SWITCH, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)),
}, {
@ -552,7 +552,7 @@ void RegionTable_Init_WaterTemple() {
}, {});
//This room exists to hold the wonderitems that drop from the emblems here. Specifically this assumes you are standing on the final ledge
areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple Waterfall", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple MQ Waterfall", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_WATER_TEMPLE_MQ_3F_CENTRAL, []{return logic->SmallKeys(RR_WATER_TEMPLE, 1) && logic->CanUse(RG_LONGSHOT);}),
Entrance(RR_WATER_TEMPLE_MQ_STALFOS_PIT, []{return true;}),
@ -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, {
//Events
EventAccess(&logic->MQWaterDragonTorches, []{return true;}),
EventAccess(&logic->MQWaterDragonTorches, []{return logic->HasFireSource();}),
},
{
//Locations
@ -843,7 +843,7 @@ void RegionTable_Init_WaterTemple() {
// Exits
Entrance(RR_WATER_TEMPLE_PRE_BOSS_ROOM, []{return ctx->GetDungeon(WATER_TEMPLE)->IsVanilla() && false;}),
Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return ctx->GetDungeon(WATER_TEMPLE)->IsMQ() && false;}),
Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return true;}),
Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}),
});
areaTable[RR_WATER_TEMPLE_BOSS_ROOM] = Region("Water Temple Boss Room", "Water Temple", {}, NO_DAY_NIGHT_CYCLE, {

View file

@ -265,7 +265,7 @@ void RegionTable_Init_Kakariko() {
Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return true;}),
});
areaTable[RR_KAK_WELL] = Region("Kak Behind Gate", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
areaTable[RR_KAK_WELL] = Region("Kak Well", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
//Exits
Entrance(RR_KAKARIKO_VILLAGE, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->DrainWell;}),
Entrance(RR_BOTTOM_OF_THE_WELL_ENTRYWAY, []{return logic->IsChild || (logic->DrainWell && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).IsNot(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF));}),

View file

@ -105,7 +105,7 @@ void RegionTable_Init_LakeHylia() {
areaTable[RR_LH_LAB] = Region("LH Lab", "LH Lab", {}, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))),
LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_BRONZE_SCALE))),
LOCATION(RC_LH_TRADE_FROG, logic->IsAdult && logic->CanUse(RG_EYEBALL_FROG)),
LOCATION(RC_LH_GS_LAB_CRATE, logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->CanBreakCrates()),
LOCATION(RC_LH_LAB_FRONT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)),

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -232,6 +232,8 @@ extern "C" void Randomizer_InitSaveFile() {
// Reset triforce pieces collected.
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected = 0;
SetStartingItems();
// Set Cutscene flags and texts to skip them.
Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO);
Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA);
@ -269,9 +271,9 @@ extern "C" void Randomizer_InitSaveFile() {
// Remove One Time Scrubs with Scrubsanity off
if (Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_OFF) {
Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE);
Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT);
Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO);
Flags_SetItemGetInf(ITEMGETINF_DEKU_SCRUB_HEART_PIECE);
Flags_SetInfTable(INFTABLE_BOUGHT_STICK_UPGRADE);
Flags_SetInfTable(INFTABLE_BOUGHT_NUT_UPGRADE);
}
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] |= 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_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_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_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);
@ -1742,6 +1742,14 @@ TrickOption& Settings::GetTrickOption(const RandomizerTrick key) {
return mTrickOptions[key];
}
int Settings::GetRandomizerTrickByName(const std::string& name) {
const auto& it = mTrickNameToEnum.find(name);
if (it == mTrickNameToEnum.end()) {
return -1;
}
return it->second;
}
void Context::ResetTrickOptions() {
for (int count = 0; count < RT_MAX; count++) {
mTrickOptions[count].Set(0); // RANDOTODO this can probably be done better

View file

@ -50,6 +50,14 @@ class Settings {
*/
TrickOption& GetTrickOption(RandomizerTrick key);
/**
* @brief Get the RandomizerTrick corresponding to the provided name.
*
* @param name
* @return int RandomizerTrick index or -1 if not found
*/
int GetRandomizerTrickByName(const std::string& name);
/**
* @brief Returns a reference to the entire array of options.
*

View file

@ -13,12 +13,14 @@ extern "C" {
#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_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_Ma1/z_en_ma1.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_Box/z_en_box.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_Bg_Ddan_Kd/z_bg_ddan_kd.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) &&
(entranceFlag != EVENTCHKINF_EPONA_OBTAINED) && entranceIndex != ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE) {
*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;
}
@ -280,8 +301,9 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
}
switch (actor->id) {
case ACTOR_OBJ_SWITCH: {
if (actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN &&
CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) {
if ((actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN) ||
(actor->params == 6979 && gPlayState->sceneNum == SCENE_WATER_TEMPLE) &&
CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) {
break;
}
ObjSwitch* switchActor = (ObjSwitch*)actor;
@ -358,6 +380,11 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
}
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:
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO)) {
*should = false;
@ -450,6 +477,26 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
*should = false;
}
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: {
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) && *should) {
EnOwl* enOwl = va_arg(args, EnOwl*);
@ -729,6 +776,18 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
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: {
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
*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.");
exit(1);
}
if (ownPath.string().find("OneDrive") != std::string::npos) {
Extractor::ShowErrorBox(
"Error",
"SoH appears to be in a OneDrive folder, which will cause issues. "
"Please move it to a folder outside of OneDrive, like the root of a drive (e.g. \"C:\\Games\\SoH\").");
exit(1);
}
#endif
#if not defined(__SWITCH__) && not defined(__WIIU__)
@ -2437,15 +2444,15 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
// animation until the text box auto-dismisses.
// RANDOTODO: Implement a way to determine if an item came from a skulltula and
// inject the auto-dismiss control code if it did.
if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 &&
!(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF)) {
bool gsTokensShuffled = Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF;
if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && !(IS_RANDO && gsTokensShuffled)) {
textId = TEXT_GS_NO_FREEZE;
} else {
textId = TEXT_GS_FREEZE;
}
// 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
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.Replace("[[gsCount]]", std::to_string(gsCount));
} else if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 &&
@ -2608,4 +2615,7 @@ void SoH_ProcessDroppedFiles(std::string filePath) {
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 SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose();
void CheckTracker_RecalculateAvailableChecks();
GetItemID RetrieveGetItemIDFromItemID(ItemID itemID);
RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID);

View file

@ -342,10 +342,10 @@ void SohMenu::AddMenuEnhancements() {
.Options(CheckboxOptions().DefaultValue(IS_RANDO));
AddWidget(path, "Exclude Glitch-Aiding Cutscenes", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"))
.Options(
CheckboxOptions().Tooltip("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 "
"Door Switch, and the Box Skip One Point in Jabu."));
.Options(CheckboxOptions().Tooltip(
"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 "
"Door Switch CS, Water Temple Dragon Switch CS, and the Box Skip One Point in Jabu."));
AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT);
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) {
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) {

View file

@ -6,6 +6,7 @@
#include "z_bg_mori_kaitenkabe.h"
#include "objects/object_mori_objects/object_mori_objects.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS 0
@ -97,7 +98,9 @@ void BgMoriKaitenkabe_Wait(BgMoriKaitenkabe* this, PlayState* play) {
if ((this->timer > (28 - CVarGetInteger(CVAR_ENHANCEMENT("FasterBlockPush"), 0) * 4)) &&
!Player_InCsMode(play)) {
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);
push.x = Math_SinS(this->dyna.unk_158);
push.y = 0.0f;
@ -131,7 +134,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) {
Math_StepToF(&this->rotSpeed, 0.6f, 0.02f);
if (Math_StepToF(&this->rotYdeg, this->rotDirection * 45.0f, this->rotSpeed)) {
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) {
thisx->home.rot.y += 0x2000;
} else {
@ -148,7 +153,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) {
this->dyna.unk_150 = 0.0f;
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) {

View file

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

View file

@ -7,6 +7,7 @@
#include "z_demo_kekkai.h"
#include "objects/object_demo_kekkai/object_demo_kekkai.h"
#include "scenes/dungeons/ganontika/ganontika_scene.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ResourceManagerHelpers.h"
#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_SetOC(play, &play->colChkCtx, &this->collider1.base);
if (this->collider2.base.acFlags & AC_HIT) {
Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME);
// "I got it"
LOG_STRING("当ったよ");
this->actor.update = DemoKekkai_TrialBarrierDispel;
this->timer = 0;
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]);
gSaveContext.cutsceneTrigger = 1;
if (GameInteractor_Should(VB_PLAY_DISPEL_BARRIER_CS, true, this)) {
Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME);
// "I got it"
LOG_STRING("当ったよ");
this->actor.update = DemoKekkai_TrialBarrierDispel;
this->timer = 0;
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]);
gSaveContext.cutsceneTrigger = 1;
}
}
CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider2.base);
func_8002F974(&this->actor, NA_SE_EV_TOWER_ENERGY - SFX_FLAG);

View file

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

View file

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

View file

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

View file

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

View file

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