From a01645304262a91b8242885ef2cc1d1b5b77be9e Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Thu, 1 May 2025 21:59:37 -0400 Subject: [PATCH 01/32] Ganon's Tower barrier dispelled if cutscene is skipped (#5462) * Ganon's Tower barrier dispelled if cutscene is skipped * More format-compliant comment --- .../Enhancements/timesaver_hook_handlers.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 523021050..cffa70643 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -217,6 +217,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; } From 6c14311b66bc4398a440c1d4362bcfe707616ae9 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Thu, 1 May 2025 21:59:47 -0400 Subject: [PATCH 02/32] add dragon room CS (#5461) --- soh/soh/Enhancements/timesaver_hook_handlers.cpp | 5 +++-- soh/soh/SohGui/SohMenuEnhancements.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index cffa70643..32de18552 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -294,8 +294,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; diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 415c5a33f..9305c8ec5 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -334,10 +334,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) From baa91cbadbd357dfb3244fa53322b77f10161016 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Fri, 2 May 2025 03:00:22 +0100 Subject: [PATCH 03/32] Format map GI text (#5417) * Format map GI text * dum --- .../Enhancements/randomizer/3drando/hint_list.cpp | 12 ++++++------ soh/soh/Enhancements/randomizer/randomizer.cpp | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index 90ddcef06..72c90fa19 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -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 } diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index f1ecd608c..29d1c4293 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -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; } From bc3b17f4abdae665b0aafbd7b877a110efe90695 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Thu, 1 May 2025 22:00:43 -0400 Subject: [PATCH 04/32] Add coloured text as current BGM indicator in Audio Editor (#5392) --- soh/soh/Enhancements/audio/AudioEditor.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index 9865615bd..4cf1f389d 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -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; From 19eb4f39abe6e10cbc603c3d2f2e407e6d173b80 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Thu, 1 May 2025 23:27:20 -0400 Subject: [PATCH 05/32] Skip trial barrier dispel cutscenes (#5464) --- .../vanilla-behavior/GIVanillaBehavior.h | 8 +++++++ .../Enhancements/timesaver_hook_handlers.cpp | 21 +++++++++++++++++++ .../actors/ovl_Demo_Kekkai/z_demo_kekkai.c | 17 ++++++++------- .../actors/ovl_Demo_Kekkai/z_demo_kekkai.h | 2 +- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index d57d5029a..3eb7916a7 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -1358,6 +1358,14 @@ typedef enum { // - `*BgTreemouth` VB_PLAY_DEKU_TREE_INTRO_CS, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*DemoKekkai` + VB_PLAY_DISPEL_BARRIER_CS, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 32de18552..0a9a77e40 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -19,6 +19,7 @@ extern "C" { #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" @@ -465,6 +466,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*); diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c index ad1c76a3d..3d50bf330 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c @@ -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); diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h index 4e4a06c18..a8df145c2 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h @@ -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; From 6cbb298f76f2c1b809891eeb38c4d9d6110ecf82 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Tue, 6 May 2025 20:01:36 -0400 Subject: [PATCH 06/32] Skip Kakariko and Hyrule Castle gate cutscenes (#5314) * Skip Kakariko gate cutscenes * Simplify a bit * Revert "Simplify a bit" This reverts commit ffa68c130fede6962bb4488b85c9ec61ccc60170. * Add Hyrule Castle gate skip * Add missing backtick * Redo simplifications * Run clang-format * Proper casting of clearCamera argument Co-authored-by: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> --------- Co-authored-by: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> --- .../vanilla-behavior/GIVanillaBehavior.h | 9 +++ .../Enhancements/timesaver_hook_handlers.cpp | 13 +++ .../actors/ovl_En_Heishi2/z_en_heishi2.c | 80 +++++++++++-------- 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 3eb7916a7..0c11e3c93 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -1430,6 +1430,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 diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 0a9a77e40..13c14b0a6 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -13,6 +13,7 @@ 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" @@ -765,6 +766,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; diff --git a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c index 61a10b056..192d749d4 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c +++ b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c @@ -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; From 804a24861b2dd888efbd4be57d65fb24ecbf0ad1 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Wed, 7 May 2025 17:45:42 -0400 Subject: [PATCH 07/32] add option description to LACS reward options (#5472) --- soh/soh/Enhancements/randomizer/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 6f3553261..6e494a6b5 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -250,7 +250,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); From f72085b6caff7817d5c5872885f6acdfb4aab8dc Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 13 May 2025 04:31:40 +0100 Subject: [PATCH 08/32] fix oversight in dragon room of MQ water (#5482) --- .../randomizer/location_access/dungeons/water_temple.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp index 0ae5ac289..04c1279bd 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp @@ -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 From e8c2db815129a8a3e93425c7f08384ce9d1d80ae Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Fri, 16 May 2025 03:51:14 +0100 Subject: [PATCH 09/32] remove all refernces to Doge (#5488) --- .../Enhancements/randomizer/randomizer.cpp | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 29d1c4293..d3e2c83b5 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -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() { From 4fd0bf402ee2cc354225f921d1f2e976bfa9f486 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Fri, 16 May 2025 18:07:52 -0400 Subject: [PATCH 10/32] set c standard to 17 (#5492) --- CMakeLists.txt | 1 + soh/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec9f14f5b..b9a5afe4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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") diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 7277b2dfb..3511a0caf 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -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) From 6acabae38f9445b199979861bcadf432d37522a7 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sat, 17 May 2025 02:11:04 -0400 Subject: [PATCH 11/32] Prevent another use of gSaveContext during Seed Generation (#5458) * Prevent BeanPlanted using gSaveContext for seed gen * address malk's comment about extern --------- Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com> --- soh/soh/Enhancements/randomizer/location_access.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 6cf2aa2ab..ade8cbf72 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -12,7 +12,6 @@ #include extern "C" { -extern SaveContext gSaveContext; extern PlayState* gPlayState; } @@ -275,7 +274,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; } From 9cb65308584bb863d00c016d77210057905ea47a Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Sat, 17 May 2025 19:29:53 -0500 Subject: [PATCH 12/32] Fix Enable Available Checks from title screen. (#5502) --- soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 6c670e447..5346a31ef 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -2114,7 +2114,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(); From 66351fa4e4b23711bc80fd9fe3e00c2a05fbdb11 Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Sat, 17 May 2025 19:30:01 -0500 Subject: [PATCH 13/32] Remove freestanding key from Bottom of the Well Perimeter. (#5496) --- .../randomizer/location_access/dungeons/bottom_of_the_well.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp index 44c3fa817..8a8393ba1 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp @@ -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()), From b30fff5d57500c65e8fe246fabb49c2303e74a60 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Sat, 17 May 2025 20:56:57 -0400 Subject: [PATCH 14/32] Skip Forest Temple Basement Pillars Cutscene (#5473) * vb forest pillar cs skip * change to one point cutscene skip * rm whitespace --- .../vanilla-behavior/GIVanillaBehavior.h | 8 ++++++++ soh/soh/Enhancements/timesaver_hook_handlers.cpp | 5 +++++ .../ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c | 13 ++++++++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 0c11e3c93..223001711 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -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 diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 13c14b0a6..9f488efce 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -375,6 +375,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; diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c index 04aff53be..692497415 100644 --- a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c @@ -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) { From e0d5fbec42d270a17e0b14c4a1d836136c6fe66e Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Sat, 17 May 2025 20:05:20 -0500 Subject: [PATCH 15/32] Available Checks Prices (#5446) * Improved the item location price availability. * Moved the available checks price logic into location_access.cpp. * Fixed typo and clarified check status identified. --- .../Enhancements/randomizer/3drando/fill.cpp | 2 +- .../Enhancements/randomizer/3drando/fill.hpp | 2 +- .../randomizer/location_access.cpp | 66 +++++++++++++++++-- .../Enhancements/randomizer/location_access.h | 3 +- .../randomizer/randomizer_check_tracker.cpp | 11 +--- 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 8885117c3..94b495639 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -421,7 +421,7 @@ 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 (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion)) { if (gals.calculatingAvailableChecks) { gals.accessibleLocations.push_back(loc); StopPerformanceTimer(PT_LOCATION_LOGIC); diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index bb013cc13..a9b864ce2 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -70,4 +70,4 @@ void GeneratePlaythrough(); bool CheckBeatable(RandomizerGet ignore=RG_NONE); -void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess); \ No newline at end of file +void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess); diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index ade8cbf72..e9da7568e 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -10,7 +10,9 @@ #include "soh/Enhancements/debugger/performanceTimer.h" #include +#include +#include "3drando/shops.hpp" extern "C" { extern PlayState* gPlayState; } @@ -31,7 +33,7 @@ bool LocationAccess::CheckConditionAtAgeTime(bool& age, bool& time) const { return GetConditionsMet(); } -bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const { +bool LocationAccess::ConditionsMet(Region* parentRegion) const { // WARNING enterance validation can run this after resetting the access for sphere 0 validation // When refactoring ToD access, either fix the above or do not assume that we // have any access at all just because this is being run @@ -44,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(); +} + +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() const { - return CanBuyAnother(location); + 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 (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) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 828c31a08..8d6815089 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -84,7 +84,7 @@ class LocationAccess { bool CheckConditionAtAgeTime(bool& age, bool& time) const; - bool ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const; + bool ConditionsMet(Region* parentRegion) const; RandomizerCheck GetLocation() const { return location; @@ -103,6 +103,7 @@ class LocationAccess { bool CanBuy() const; }; +bool CanBuyAnother(uint16_t price); bool CanBuyAnother(RandomizerCheck rc); namespace Rando { diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 5346a31ef..8648c9826 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -1967,7 +1967,7 @@ void RecalculateAvailableChecks() { StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); std::vector 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); @@ -1979,15 +1979,8 @@ void RecalculateAvailableChecks() { std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true); 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); - } + itemLocation->SetAvailable(true); } totalChecksAvailable = 0; From 8e349429245aee3590b801ed04027cdb46695c28 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 18 May 2025 02:06:24 +0100 Subject: [PATCH 16/32] Toggle the vanilla flags instead of the rando flags when removing one time scrubs (#5504) --- soh/soh/Enhancements/randomizer/savefile.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index cb8c7eaef..1bed22a71 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -269,9 +269,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(); From ecad59e31fe134dea65f8da7b160b17692276011 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sat, 17 May 2025 18:52:45 -0700 Subject: [PATCH 17/32] Add tooltip to Generate Seed button when disabled indicating the need to be on File Select. (#5509) --- soh/soh/Enhancements/randomizer/randomizer.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index d3e2c83b5..18a23df53 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -3724,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)) { From 53566c9a73a3f176824547af2f940085e5f425ae Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Sun, 18 May 2025 16:25:24 -0400 Subject: [PATCH 18/32] fix incorrect `__VA_ARGS__` use in `lusprintf` version of `osSyncPrintf` (#5510) --- soh/include/functions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/include/functions.h b/soh/include/functions.h index 86e3bf77f..f29153a9c 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -15,7 +15,7 @@ extern "C" #include #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 From f16e34e8b8f538725229c2d4ff9018e50596d7de Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 18 May 2025 22:25:18 +0100 Subject: [PATCH 19/32] fix and rename IsKeysanity to IsFireLoopLocked (#5515) * c * fix and rename IsKeysanity * add comment --- .../randomizer/location_access/dungeons/fire_temple.cpp | 4 ++-- soh/soh/Enhancements/randomizer/logic.cpp | 9 +++++---- soh/soh/Enhancements/randomizer/logic.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 8888cdac9..70cf890b0 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -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;}), }); @@ -43,7 +43,7 @@ void RegionTable_Init_FireTemple() { 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);});}), }); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 252806473..7f4b7e102 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1389,6 +1389,7 @@ bool Logic::SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmountGlitchless static_cast(GlitchDifficulty::INTERMEDIATE) || GetDifficultyValueFromString(GlitchHover) >= static_cast(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: @@ -2336,9 +2337,9 @@ void Logic::Reset() { 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 @@ -2400,7 +2401,7 @@ void Logic::Reset() { // 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()) { + if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); } diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 4f966f5de..77c49d25f 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -59,7 +59,7 @@ class Logic { bool LightTrialClear = false; // Logical keysanity - bool IsKeysanity = false; + bool IsFireLoopLocked = false; // Bottle Count uint8_t Bottles = 0; From f0e40fd1dc09b894c0ee6956ca2e4fc369436e85 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Sun, 18 May 2025 17:26:10 -0400 Subject: [PATCH 20/32] fix incorrect token count in messages when tokensanity is off (#5503) * fix incorrect token count in messages when tokensanity is off * Update OTRGlobals.cpp --- soh/soh/OTRGlobals.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 72030657e..dcabfca70 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2437,15 +2437,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 && From 4334a132e31ef12a1cccf2c77e7006ed9c54c88d Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 18 May 2025 22:28:55 +0100 Subject: [PATCH 21/32] fix the wrong codepath being used in CanBuy when generating seeds (#5514) --- soh/soh/Enhancements/randomizer/3drando/fill.cpp | 2 +- soh/soh/Enhancements/randomizer/location_access.cpp | 8 ++++---- soh/soh/Enhancements/randomizer/location_access.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 94b495639..8885117c3 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -421,7 +421,7 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals Rando::ItemLocation* location = ctx->GetItemLocation(loc); RandomizerGet locItem = location->GetPlacedRandomizerGet(); - if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion)) { + if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) { if (gals.calculatingAvailableChecks) { gals.accessibleLocations.push_back(loc); StopPerformanceTimer(PT_LOCATION_LOGIC); diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index e9da7568e..cdd684f04 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -33,7 +33,7 @@ bool LocationAccess::CheckConditionAtAgeTime(bool& age, bool& time) const { return GetConditionsMet(); } -bool LocationAccess::ConditionsMet(Region* parentRegion) const { +bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const { // WARNING enterance validation can run this after resetting the access for sphere 0 validation // When refactoring ToD access, either fix the above or do not assume that we // have any access at all just because this is being run @@ -46,7 +46,7 @@ bool LocationAccess::ConditionsMet(Region* parentRegion) const { conditionsMet = true; } - return conditionsMet && CanBuy(); + return conditionsMet && CanBuy(calculatingAvailableChecks); } static uint16_t GetMinimumPrice(const Rando::Location* loc) { @@ -90,13 +90,13 @@ static uint16_t GetMinimumPrice(const Rando::Location* loc) { } } -bool LocationAccess::CanBuy() const { +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 (itemLoc->GetCheckStatus() != RCSHOW_IDENTIFIED) { + if (calculatingAvailableChecks && itemLoc->GetCheckStatus() != RCSHOW_IDENTIFIED) { return CanBuyAnother(GetMinimumPrice(loc)); } else { return CanBuyAnother(itemLoc->GetPrice()); diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 8d6815089..9c8df0e9b 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -84,7 +84,7 @@ class LocationAccess { bool CheckConditionAtAgeTime(bool& age, bool& time) const; - bool ConditionsMet(Region* parentRegion) const; + bool ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const; RandomizerCheck GetLocation() const { return location; @@ -100,7 +100,7 @@ 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); From 081f82875a8afbacc77d2baa1230040dc785266c Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Mon, 19 May 2025 00:24:57 -0400 Subject: [PATCH 22/32] fix link voice missing when hanging off ledges (#5506) * fix link voice missing when hanging off ledges * remove unused bitrate var * hardcoded but justified * format --- soh/soh/stubs.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/soh/soh/stubs.c b/soh/soh/stubs.c index 56013e56f..118b36e72 100644 --- a/soh/soh/stubs.c +++ b/soh/soh/stubs.c @@ -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) { From 5bf3761a18160c298d29d98c6094840cb0a47eaa Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 21 May 2025 16:51:34 +0100 Subject: [PATCH 23/32] Fix Boss and Tower entryways skipping requirements in decoupled (#5484) * Fix boss and tower entryways in decoupled * Add patches to child boss room entryways * Apply fixes * remember to set ALL the new exits --- soh/soh/Enhancements/randomizer/entrance.cpp | 44 +++++++++---------- .../location_access/dungeons/deku_tree.cpp | 11 +++-- .../dungeons/dodongos_cavern.cpp | 11 +++-- .../location_access/dungeons/fire_temple.cpp | 6 +-- .../dungeons/forest_temple.cpp | 6 +-- .../dungeons/ganons_castle.cpp | 23 ++++------ .../dungeons/jabujabus_belly.cpp | 10 +++-- .../dungeons/shadow_temple.cpp | 6 +-- .../dungeons/spirit_temple.cpp | 6 +-- .../location_access/dungeons/water_temple.cpp | 6 +-- .../Enhancements/randomizer/randomizerTypes.h | 4 ++ 11 files changed, 73 insertions(+), 60 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index 40eaca212..66da4ee18 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -1131,22 +1131,22 @@ int EntranceShuffler::ShuffleAllEntrances() { { { 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::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 }, @@ -1488,11 +1488,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 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 +1530,11 @@ int EntranceShuffler::ShuffleAllEntrances() { // Pair std::vector 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)), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp index f327c9992..c829d1bc9 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp @@ -436,11 +436,16 @@ void RegionTable_Init_DekuTree() { #pragma endregion // Boss Room + // RANDOTODO make it so entrance randomiser can properly handle more than 1 access to that entrance areaTable[RR_DEKU_TREE_BOSS_ENTRYWAY] = Region("Deku Tree Boss Entryway", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + // Exits + Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}), + }); + + areaTable[RR_DEKU_TREE_BOSS_EXIT] = Region("Deku Tree Boss Exit", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_DEKU_TREE_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsVanilla();}), Entrance(RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsMQ();}), - Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}), }); areaTable[RR_DEKU_TREE_BOSS_ROOM] = Region("Deku Tree Boss Room", "Deku Tree", {}, NO_DAY_NIGHT_CYCLE, { @@ -460,8 +465,8 @@ void RegionTable_Init_DekuTree() { LOCATION(RC_DEKU_TREE_QUEEN_GOHMA_GRASS_8, logic->CanCutShrubs()), }, { // Exits - Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, []{return true;}), - Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false), + Entrance(RR_DEKU_TREE_BOSS_EXIT, []{return true;}), + Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false), }); // clang-format on diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp index 8cd60f268..9488f2d82 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp @@ -558,11 +558,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 +580,8 @@ void RegionTable_Init_DodongosCavern() { LOCATION(RC_KING_DODONGO, logic->DodongosCavernClear), }, { // Exits - Entrance(RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, []{return true;}), - Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false), + Entrance(RR_DODONGOS_CAVERN_BOSS_EXIT, []{return true;}), + Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false), }); // clang-format on diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 70cf890b0..2e1a6ef14 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -38,7 +38,7 @@ void RegionTable_Init_FireTemple() { }, { //Exits Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return true;}), - Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}))) || logic->CanUse(RG_HOVER_BOOTS));}), + Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}) || logic->CanUse(RG_HOVER_BOOTS));}), }); areaTable[RR_FIRE_TEMPLE_LOOP_ENEMIES] = Region("Fire Temple Loop Enemies", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -445,7 +445,7 @@ void RegionTable_Init_FireTemple() { Entrance(RR_FIRE_TEMPLE_MQ_FIRST_ROOM_UPPER, []{return true;}), //Child cannot make it to the north side torches without a hook without specifically bunny hood speed + hover boots Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM_NORTH, []{return logic->FireTimer() > 32 && (logic->CanUse(RG_HOOKSHOT) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)));}), - Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}), + Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}), }); //This room assumes tunic logic is handled on entry. @@ -740,7 +740,7 @@ void RegionTable_Init_FireTemple() { // Exits Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsVanilla() && false;}), Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsMQ() && false;}), - Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY);}), }); areaTable[RR_FIRE_TEMPLE_BOSS_ROOM] = Region("Fire Temple Boss Room", "Fire Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp index 2f5ba96bf..c8d80f98d 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp @@ -298,7 +298,7 @@ void RegionTable_Init_ForestTemple() { }, { //Exits Entrance(RR_FOREST_TEMPLE_LOBBY, []{return true;}), - Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}), + Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -593,7 +593,7 @@ void RegionTable_Init_ForestTemple() { areaTable[RR_FOREST_TEMPLE_MQ_BOSS_REGION] = Region("Forest Temple MQ Boss Region", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_FOREST_TEMPLE_MQ_BASEMENT, []{return logic->ForestOpenBossCorridor;}), - Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}), + Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -603,7 +603,7 @@ void RegionTable_Init_ForestTemple() { // Exits Entrance(RR_FOREST_TEMPLE_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsVanilla() && false;}), Entrance(RR_FOREST_TEMPLE_MQ_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsMQ() && false;}), - Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}), }); areaTable[RR_FOREST_TEMPLE_BOSS_ROOM] = Region("Forest Temple Boss Room", "Forest Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp index 451225273..e4c33b49a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp @@ -29,12 +29,7 @@ void RegionTable_Init_GanonsCastle() { Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, []{return true;}), Entrance(RR_GANONS_CASTLE_SPIRIT_TRIAL, []{return true;}), Entrance(RR_GANONS_CASTLE_LIGHT_TRIAL, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}), - Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && - (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && - (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && - (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && - (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) && - (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}), + Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}), Entrance(RR_GANONS_CASTLE_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON) || logic->CanUse(RG_LENS_OF_TRUTH);}), }); @@ -162,13 +157,7 @@ void RegionTable_Init_GanonsCastle() { Entrance(RR_GANONS_CASTLE_MQ_SHADOW_TRIAL_STARTING_LEDGE, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_SPIRIT_TRIAL_CHAIRS_ROOM, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_DINOLFOS_ROOM, []{return Here(RR_GANONS_CASTLE_MQ_MAIN, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);});}), - //RANDOTODO could we just set these events automatically based on the setting? - Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && - (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && - (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && - (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && - (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) && - (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}), + Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON_MQ) || logic->CanUse(RG_LENS_OF_TRUTH);}), }); @@ -436,7 +425,13 @@ void RegionTable_Init_GanonsCastle() { //Exits Entrance(RR_GANONS_CASTLE_LOBBY, []{return ctx->GetDungeon(GANONS_CASTLE)->IsVanilla();}), Entrance(RR_GANONS_CASTLE_MQ_MAIN, []{return ctx->GetDungeon(GANONS_CASTLE)->IsMQ();}), - Entrance(RR_GANONS_TOWER_FLOOR_1, []{return true;}), + //RANDOTODO could we just set these events automatically based on the setting? + Entrance(RR_GANONS_TOWER_FLOOR_1, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && + (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && + (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && + (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && + (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) && + (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}), }); areaTable[RR_GANONS_TOWER_FLOOR_1] = Region("Ganon's Tower Floor 1", "Ganons Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp index 3c45682ae..95494212a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp @@ -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 diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp index 3be1acac4..eef68c78b 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp @@ -112,7 +112,7 @@ void RegionTable_Init_ShadowTemple() { LOCATION(RC_SHADOW_TEMPLE_AFTER_SHIP_LOWER_HEART, (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->CanUse(RG_SONG_OF_TIME) || (logic->CanUse(RG_DISTANT_SCARECROW) && logic->CanUse(RG_HOVER_BOOTS))), }, { //Exits - Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS) && logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}) + Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS);}) }); #pragma endregion @@ -370,7 +370,7 @@ void RegionTable_Init_ShadowTemple() { }, { //Exits Entrance(RR_SHADOW_TEMPLE_MQ_ACROSS_CHASM, []{return logic->CanUse(RG_HOVER_BOOTS) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH));}), - Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}), + Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); //Assumes lens is checked on entry @@ -403,7 +403,7 @@ void RegionTable_Init_ShadowTemple() { // Exits Entrance(RR_SHADOW_TEMPLE_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsVanilla() && false;}), Entrance(RR_SHADOW_TEMPLE_MQ_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsMQ() && false;}), - Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}), }); areaTable[RR_SHADOW_TEMPLE_BOSS_ROOM] = Region("Shadow Temple Boss Room", "Shadow Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index e7f9c0ee6..72aa9974b 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -145,7 +145,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -541,7 +541,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -551,7 +551,7 @@ void RegionTable_Init_SpiritTemple() { // Exits Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}), Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", "Spirit Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp index 04c1279bd..ccbc2f688 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp @@ -277,7 +277,7 @@ void RegionTable_Init_WaterTemple() { }, { //Exits Entrance(RR_WATER_TEMPLE_LOBBY, []{return true;}), - Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}), + Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -365,7 +365,7 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Main", "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, { @@ -843,7 +843,7 @@ void RegionTable_Init_WaterTemple() { // Exits Entrance(RR_WATER_TEMPLE_PRE_BOSS_ROOM, []{return ctx->GetDungeon(WATER_TEMPLE)->IsVanilla() && false;}), Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return ctx->GetDungeon(WATER_TEMPLE)->IsMQ() && false;}), - Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}), }); areaTable[RR_WATER_TEMPLE_BOSS_ROOM] = Region("Water Temple Boss Room", "Water Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index ce074bf4c..396a37b23 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -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, From 01ce1eeac8d8dacfc962c52666e622e77f720eba Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 21 May 2025 16:51:43 +0100 Subject: [PATCH 24/32] Fix oversight in lab boots trick, you need to be able to swim back up (#5524) --- .../randomizer/location_access/overworld/lake_hylia.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp index 36d3ee1a3..19e0497f0 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp @@ -105,7 +105,7 @@ void RegionTable_Init_LakeHylia() { areaTable[RR_LH_LAB] = Region("LH Lab", "LH Lab", {}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))), + LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_BRONZE_SCALE))), LOCATION(RC_LH_TRADE_FROG, logic->IsAdult && logic->CanUse(RG_EYEBALL_FROG)), LOCATION(RC_LH_GS_LAB_CRATE, logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->CanBreakCrates()), LOCATION(RC_LH_LAB_FRONT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)), From 2511275b8ba11f5e2149c312f42e402e23891ae8 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 21 May 2025 23:01:54 +0100 Subject: [PATCH 25/32] Fix getting heart containers on pocket and skipped impas song (#5518) * fix getting heart containers on pocket and skipped impas song * curse you clang! CURSE YOU! --- soh/soh/Enhancements/randomizer/savefile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 1bed22a71..91b5a50fc 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -232,6 +232,8 @@ extern "C" void Randomizer_InitSaveFile() { // Reset triforce pieces collected. gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected = 0; + SetStartingItems(); + // Set Cutscene flags and texts to skip them. Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO); Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA); @@ -430,6 +432,4 @@ extern "C" void Randomizer_InitSaveFile() { gSaveContext.itemGetInf[3] |= 0x800; // Bunny Hood related gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth } - - SetStartingItems(); } From a9fc317a5a916389449d68d666ff2e2965a0b023 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 22 May 2025 23:23:14 +0100 Subject: [PATCH 26/32] Fix crash when no valid enemies are selected (#5519) * fix crash when no valid enemies are selected * Clanged --- soh/soh/Enhancements/enemyrandomizer.cpp | 29 ++++++++++++------------ soh/soh/Enhancements/enemyrandomizer.h | 3 ++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index 84e1d040e..bdb259719 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -270,15 +270,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 +326,28 @@ void GetSelectedEnemies() { } } -EnemyEntry GetRandomizedEnemyEntry(uint32_t seed) { +EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play) { + std::vector filteredEnemyList = {}; if (selectedEnemyList.size() == 0) { GetSelectedEnemies(); } + for (EnemyEntry enemy : selectedEnemyList) { + if (IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, enemy)) { + filteredEnemyList.push_back(enemy); + } + } + if (filteredEnemyList.size() == 0) { + filteredEnemyList = selectedEnemyList; + } if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == 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]; } } diff --git a/soh/soh/Enhancements/enemyrandomizer.h b/soh/soh/Enhancements/enemyrandomizer.h index 4c4fd5576..6f80503df 100644 --- a/soh/soh/Enhancements/enemyrandomizer.h +++ b/soh/soh/Enhancements/enemyrandomizer.h @@ -1,6 +1,7 @@ #pragma once #include +#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[]; From d69a45674f7641eae3764f8bbbc6f016314c8854 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Fri, 23 May 2025 14:06:25 -0700 Subject: [PATCH 27/32] Adds a check for "OneDrive" in the execution path to the startup errors that prevent running. (#5522) --- soh/soh/OTRGlobals.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index dcabfca70..3195bf2b7 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -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__) From d330f22071e4257759dc5e04f2b47a069b68e29b Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Fri, 23 May 2025 19:49:01 -0500 Subject: [PATCH 28/32] Available Checks Entrance Shuffle (#5363) * EntranceShuffler ApplyEntranceOverrides. * Updated ApplyEntranceOverrides. * Updated ApplyEntranceOverrides. * Initial Entrance Discovery. * Added Randomizer_EntranceDiscovered. * Updated Randomizer_DiscoverRegion to discover unshuffled connected regions. * Removed extra semi-colon. * Update to latest entranceShuffleTable. * Format fixes. * Updated EntranceDiscovered to account for entrances not in the entranceShuffleTable. * Rediscover regions when loading a game. * Rediscover entrances when enabling Available Checks. * Added Rando::Context::ParseTricksJson. * Updated ApplyEntranceOverrides to skip default (unset) overrides. * Fix clang-format failed check. * Set mLACSCondition when loading a Randomizer game. * Updated rediscover loop to use MAX_ENTRANCE_RANDO_USED_INDEX. * Move entrance discovered into ProcessExits. * Discover spawns when pulling/placing the master sword. * Discover adult/child spawn when using Song of Time to switch age. * Reset logic and ApplyOrStoreItems without applying the item effects to the save when calculating available checks. * Removed !itemLoc->IsAddedToPool() from the check tracker. * Remove region discovery as its not needed anymore. * Cleanup changed files for PR. * Added Available Checks - Process Undiscovered Exits DebugConsole command. * Added initial Available Checks - Recalculate DebugConsole command. * ACPUE, recalculate only if save loaded. ACR, set age and times for the starting region. --- soh/soh/Enhancements/debugconsole.cpp | 60 ++ soh/soh/Enhancements/mods.cpp | 7 + .../Enhancements/randomizer/3drando/fill.cpp | 45 +- .../Enhancements/randomizer/3drando/fill.hpp | 4 +- soh/soh/Enhancements/randomizer/context.cpp | 16 + soh/soh/Enhancements/randomizer/context.h | 10 + soh/soh/Enhancements/randomizer/entrance.cpp | 648 +++++++++--------- soh/soh/Enhancements/randomizer/entrance.h | 1 + soh/soh/Enhancements/randomizer/item.cpp | 14 +- .../Enhancements/randomizer/location_access.h | 1 - soh/soh/Enhancements/randomizer/logic.cpp | 66 +- soh/soh/Enhancements/randomizer/logic.h | 5 +- .../randomizer/randomizer_check_tracker.cpp | 48 +- .../randomizer/randomizer_check_tracker.h | 2 +- .../randomizer/randomizer_entrance.c | 1 + soh/soh/Enhancements/randomizer/settings.cpp | 8 + soh/soh/Enhancements/randomizer/settings.h | 8 + soh/soh/OTRGlobals.cpp | 5 +- soh/soh/OTRGlobals.h | 1 + .../actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c | 6 + 20 files changed, 583 insertions(+), 373 deletions(-) diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index db5cc824f..32fc8e72f 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -11,6 +11,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/cosmetics/CosmeticsEditor.h" #include "soh/Enhancements/audio/AudioEditor.h" +#include "soh/Enhancements/randomizer/logic.h" #define Path _Path #define PATH_HACK @@ -1450,6 +1451,55 @@ static bool SfxHandler(std::shared_ptr Console, const std::vector return 0; } +static bool AvailabeChecksProcessUndiscoveredExitsHandler(std::shared_ptr Console, + const std::vector& 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 AvailabeChecksRecalculateHandler(std::shared_ptr Console, + const std::vector& args, std::string* output) { + RandomizerRegion startingRegion = RR_ROOT; + + if (args.size() > 1) { + try { + startingRegion = static_cast(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", { AvailabeChecksProcessUndiscoveredExitsHandler, + "Available Checks - Process Undiscovered Exits", + { { "enable", Ship::ArgumentType::NUMBER, true } } }); + + CMD_REGISTER("acr", { AvailabeChecksRecalculateHandler, + "Available Checks - Recalculate", + { + { "starting_region", Ship::ArgumentType::NUMBER, true }, + } }); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 2c357fd34..180ba4f5e 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -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; diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 8885117c3..f2b0e782c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -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 ReachabilitySearch(const std::vector& targetLocations, RandomizerGet ignore /* = RG_NONE*/, - bool calculatingAvailableChecks /* = false */) { + bool calculatingAvailableChecks /* = false */, + RandomizerRegion startingRegion /* = RR_ROOT */) { auto ctx = Rando::Context::GetInstance(); GetAccessibleLocationsStruct gals(0); - gals.calculatingAvailableChecks = calculatingAvailableChecks; ResetLogic(ctx, gals, !calculatingAvailableChecks); + if (startingRegion != RR_ROOT) { + gals.regionPool.insert(gals.regionPool.begin(), startingRegion); + + const auto& region = RegionTable(startingRegion); + if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) { + region->childDay = true; + } else { + region->adultDay = true; + } + if (region->timePass) { + if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) { + region->childNight = true; + } else { + region->adultNight = true; + } + } + } + if (calculatingAvailableChecks) { + logic->Reset(false); + logic->CalculatingAvailableChecks = true; + } do { gals.InitLoop(); for (size_t i = 0; i < gals.regionPool.size(); i++) { diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index a9b864ce2..67043952f 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -34,8 +34,6 @@ struct GetAccessibleLocationsStruct { std::vector itemSphere; std::list entranceSphere; - bool calculatingAvailableChecks = false; - GetAccessibleLocationsStruct(int _maxGsCount){ regionPool = {RR_ROOT}; gsCount = 0; @@ -64,7 +62,7 @@ std::vector GetEmptyLocations(std::vector allo void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, RandomizerGet ignore = RG_NONE, bool stopOnBeatable = false, bool addToPlaythrough = false); -std::vector ReachabilitySearch(const std::vector& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false); +std::vector ReachabilitySearch(const std::vector& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false, RandomizerRegion startingRegion=RR_ROOT); void GeneratePlaythrough(); diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index fa755325e..7178dd18d 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -401,6 +401,7 @@ void Context::ParseSpoiler(const char* spoilerFileName) { Rando::Settings::GetInstance()->ParseJson(spoilerFileJson); ParseItemLocationsJson(spoilerFileJson); ParseHintJson(spoilerFileJson); + ParseTricksJson(spoilerFileJson); mEntranceShuffler->ParseJson(spoilerFileJson); mDungeons->ParseJson(spoilerFileJson); mTrials->ParseJson(spoilerFileJson); @@ -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 Context::GetEntranceShuffler() { return mEntranceShuffler; } @@ -524,6 +536,10 @@ RandoOptionLACSCondition Context::LACSCondition() const { return mLACSCondition; } +void Context::LACSCondition(RandoOptionLACSCondition lacsCondition) { + mLACSCondition = lacsCondition; +} + std::shared_ptr Context::GetKaleido() { if (mKaleido == nullptr) { mKaleido = std::make_shared(); diff --git a/soh/soh/Enhancements/randomizer/context.h b/soh/soh/Enhancements/randomizer/context.h index 7f18f7a9d..c7e3b3c19 100644 --- a/soh/soh/Enhancements/randomizer/context.h +++ b/soh/soh/Enhancements/randomizer/context.h @@ -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 overrides = {}; std::vector> playthroughLocations = {}; std::vector everyPossibleLocation = {}; diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index 66da4ee18..45cde2f6f 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -250,7 +250,319 @@ std::string EntranceNameByRegions(RandomizerRegion parentRegion, RandomizerRegio return RegionTable(parentRegion)->regionName + " -> " + RegionTable(connectedRegion)->regionName; } -void SetAllEntrancesData(std::vector& entranceShuffleTable) { +std::unordered_map entranceMap; + +void SetAllEntrancesData() { + std::vector 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& 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& 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 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 - }; - std::map 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 = {}; @@ -1663,6 +1667,26 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) { } } catch (const std::exception& e) { throw e; } } + +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 extern "C" EntranceOverride* Randomizer_GetEntranceOverrides() { diff --git a/soh/soh/Enhancements/randomizer/entrance.h b/soh/soh/Enhancements/randomizer/entrance.h index 026da7426..6cb631db3 100644 --- a/soh/soh/Enhancements/randomizer/entrance.h +++ b/soh/soh/Enhancements/randomizer/entrance.h @@ -128,6 +128,7 @@ class EntranceShuffler { void CreateEntranceOverrides(); void UnshuffleAllEntrances(); void ParseJson(nlohmann::json spoilerFileJson); + void ApplyEntranceOverrides(); private: std::vector AssumeEntrancePool(std::vector& entrancePool); diff --git a/soh/soh/Enhancements/randomizer/item.cpp b/soh/soh/Enhancements/randomizer/item.cpp index ee0dc9403..3b75a0c95 100644 --- a/soh/soh/Enhancements/randomizer/item.cpp +++ b/soh/soh/Enhancements/randomizer/item.cpp @@ -46,14 +46,20 @@ Item::~Item() = default; void Item::ApplyEffect() const { auto ctx = Rando::Context::GetInstance(); - ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true); - ctx->GetLogic()->SetInLogic(logicVal, true); + auto logic = ctx->GetLogic(); + if (!logic->CalculatingAvailableChecks) { + logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true); + } + logic->SetInLogic(logicVal, true); } void Item::UndoEffect() const { auto ctx = Rando::Context::GetInstance(); - ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false); - ctx->GetLogic()->SetInLogic(logicVal, false); + auto logic = ctx->GetLogic(); + if (!logic->CalculatingAvailableChecks) { + logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false); + } + logic->SetInLogic(logicVal, false); } const Text& Item::GetName() const { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 9c8df0e9b..8f7bb82d3 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -137,7 +137,6 @@ class Region { bool adultDay = false; bool adultNight = false; bool addedToPool = false; - ; void ApplyTimePass(); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 7f4b7e102..597feb463 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2332,8 +2332,10 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { inLogic[logicVal] = value; } -void Logic::Reset() { - NewSaveContext(); +void Logic::Reset(bool resetSaveContext /*= true*/) { + if (resetSaveContext) { + NewSaveContext(); + } StartPerformanceTimer(PT_LOGIC_RESET); memset(inLogic, false, sizeof(inLogic)); // Settings-dependent variables @@ -2372,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 (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { - SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); + // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in + // vanilla FiT + if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { + SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); + } } // Bottle Count @@ -2455,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; @@ -2520,6 +2526,8 @@ void Logic::Reset() { Spirit1FSilverRupees = false; JabuRutoIn1F = false; + CalculatingAvailableChecks = false; + StopPerformanceTimer(PT_LOGIC_RESET); } } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 77c49d25f..0c138a3eb 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -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 _ctx); bool GetInLogic(LogicVal logicVal); void SetInLogic(LogicVal logicVal, bool remove); diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 8648c9826..3a2c5e475 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -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,20 +1992,22 @@ void RecalculateAvailableChecks() { ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); + const auto& ctx = Rando::Context::GetInstance(); + std::vector targetLocations; targetLocations.reserve(RC_MAX); for (auto& location : Rando::StaticData::GetLocationTable()) { RandomizerCheck rc = location.GetRandomizerCheck(); - Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc); itemLocation->SetAvailable(false); if (!itemLocation->HasObtained()) { targetLocations.emplace_back(rc); } } - std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true); + std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true, startingRegion); for (auto& rc : availableChecks) { - const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + const auto& itemLocation = ctx->GetItemLocation(rc); itemLocation->SetAvailable(true); } @@ -1987,7 +2015,7 @@ void RecalculateAvailableChecks() { for (auto& [rcArea, vec] : checksByArea) { areaChecksAvailable[rcArea] = 0; for (auto& rc : vec) { - Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc); if (itemLocation->IsAvailable() && IsVisibleInCheckTracker(rc) && !IsCheckHidden(rc)) { areaChecksAvailable[rcArea]++; } diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h index d6fd503b3..7966c4b37 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h @@ -61,5 +61,5 @@ void UpdateAllOrdering(); void UpdateAllAreas(); void RecalculateAllAreaTotals(); void SpoilAreaFromCheck(RandomizerCheck rc); -void RecalculateAvailableChecks(); +void RecalculateAvailableChecks(RandomizerRegion startingRegion = RR_ROOT); } // namespace CheckTracker diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c index 11d17e621..faec117eb 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -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) { diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 6e494a6b5..a2db7ee0e 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1739,6 +1739,14 @@ TrickOption& Settings::GetTrickOption(const RandomizerTrick key) { return mTrickOptions[key]; } +int Settings::GetRandomizerTrickByName(const std::string& name) { + const auto& it = mTrickNameToEnum.find(name); + if (it == mTrickNameToEnum.end()) { + return -1; + } + return it->second; +} + void Context::ResetTrickOptions() { for (int count = 0; count < RT_MAX; count++) { mTrickOptions[count].Set(0); // RANDOTODO this can probably be done better diff --git a/soh/soh/Enhancements/randomizer/settings.h b/soh/soh/Enhancements/randomizer/settings.h index c820a9df9..ed214471a 100644 --- a/soh/soh/Enhancements/randomizer/settings.h +++ b/soh/soh/Enhancements/randomizer/settings.h @@ -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. * diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 3195bf2b7..9bd3008f8 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2615,4 +2615,7 @@ void SoH_ProcessDroppedFiles(std::string filePath) { return; } } -// #endregion + +extern "C" void CheckTracker_RecalculateAvailableChecks() { + CheckTracker::RecalculateAvailableChecks(); +} diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index b14d322d2..bcc5c8657 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -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); diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c index 81542eaad..a11180a7b 100644 --- a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c @@ -131,8 +131,14 @@ void func_808BAF40(BgTokiSwd* this, PlayState* play) { Item_Give(play, ITEM_SWORD_MASTER); } play->csCtx.segment = D_808BB2F0; + + // Discover adult spawn + Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false); } else { play->csCtx.segment = D_808BB7A0; + + // Discover child spawn + Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false); } Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_MASTER_SWORD); From de96f3cd0a7f6d6adf4bf7e20719f0ef7b843843 Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Sat, 24 May 2025 12:39:53 -0500 Subject: [PATCH 29/32] Typo Fixes (#5533) * Fixed a couple typos. * Ran clang format. --- soh/soh/Enhancements/debugconsole.cpp | 12 ++++++------ soh/soh/Enhancements/randomizer/ShuffleCrates.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index 32fc8e72f..d25c3cfff 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -1451,8 +1451,8 @@ static bool SfxHandler(std::shared_ptr Console, const std::vector return 0; } -static bool AvailabeChecksProcessUndiscoveredExitsHandler(std::shared_ptr Console, - const std::vector& args, std::string* output) { +static bool AvailableChecksProcessUndiscoveredExitsHandler(std::shared_ptr Console, + const std::vector& args, std::string* output) { const auto& logic = Rando::Context::GetInstance()->GetLogic(); bool enabled = false; @@ -1478,8 +1478,8 @@ static bool AvailabeChecksProcessUndiscoveredExitsHandler(std::shared_ptr Console, - const std::vector& args, std::string* output) { +static bool AvailableChecksRecalculateHandler(std::shared_ptr Console, + const std::vector& args, std::string* output) { RandomizerRegion startingRegion = RR_ROOT; if (args.size() > 1) { @@ -1758,11 +1758,11 @@ void DebugConsole_Init(void) { { "group_name", Ship::ArgumentType::TEXT, true }, } }); - CMD_REGISTER("acpue", { AvailabeChecksProcessUndiscoveredExitsHandler, + CMD_REGISTER("acpue", { AvailableChecksProcessUndiscoveredExitsHandler, "Available Checks - Process Undiscovered Exits", { { "enable", Ship::ArgumentType::NUMBER, true } } }); - CMD_REGISTER("acr", { AvailabeChecksRecalculateHandler, + CMD_REGISTER("acr", { AvailableChecksRecalculateHandler, "Available Checks - Recalculate", { { "starting_region", Ship::ArgumentType::NUMBER, true }, diff --git a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp index 3cb341980..fa88d455b 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp @@ -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)); From f0e36c2694fe004cbadaa45d9149c8b67c8eba88 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 27 May 2025 21:19:30 +0100 Subject: [PATCH 30/32] Fix failure to assign areas to regions on initial spoiler load (#5540) --- soh/soh/Enhancements/randomizer/3drando/fill.hpp | 1 + soh/soh/Enhancements/randomizer/context.cpp | 2 +- soh/soh/Enhancements/randomizer/entrance.cpp | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index 67043952f..e8124c0dc 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -56,6 +56,7 @@ struct GetAccessibleLocationsStruct { void ClearProgress(); void VanillaFill(); int Fill(); +void SetAreas(); std::vector GetEmptyLocations(std::vector allowedLocations); diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index 7178dd18d..550cd0e93 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -400,9 +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; diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index 45cde2f6f..ea9b80ee3 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -1666,6 +1666,10 @@ 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() { From fc10e36cdb72e39235852d059580761ae78a0954 Mon Sep 17 00:00:00 2001 From: Hunter Marshall Date: Tue, 27 May 2025 16:34:36 -0500 Subject: [PATCH 31/32] Fix minor mistakes in region table (#5545) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix minor mistakes in region table * Update soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp Co-authored-by: Philip Dubé --------- Co-authored-by: Philip Dubé --- .../randomizer/location_access/dungeons/dodongos_cavern.cpp | 5 +++-- .../randomizer/location_access/dungeons/fire_temple.cpp | 2 +- .../randomizer/location_access/dungeons/forest_temple.cpp | 2 +- .../location_access/dungeons/gerudo_training_ground.cpp | 2 +- .../randomizer/location_access/dungeons/ice_cavern.cpp | 2 +- .../randomizer/location_access/dungeons/spirit_temple.cpp | 6 +++--- .../randomizer/location_access/dungeons/water_temple.cpp | 6 +++--- .../randomizer/location_access/overworld/kakariko.cpp | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp index 9488f2d82..bffc6c290 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp @@ -268,6 +268,7 @@ void RegionTable_Init_DodongosCavern() { LOCATION(RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT, logic->CanStunDeku()), }, { //Exits + Entrance(RR_DODONGOS_CAVERN_MQ_BEGINNING, []{return true;}), Entrance(RR_DODONGOS_CAVERN_MQ_GOSSIP_STONE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET);});}), Entrance(RR_DODONGOS_CAVERN_MQ_MOUTH_SIDE_BRIDGE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}), Entrance(RR_DODONGOS_CAVERN_MQ_STAIRS_LOWER, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}), @@ -386,7 +387,7 @@ void RegionTable_Init_DodongosCavern() { Entrance(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanUse(RG_STICKS) && logic->HasItem(RG_GORONS_BRACELET);}), //Implies access to RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM from here }); - areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Torch Puzzle Lower", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Big Block Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_1, logic->CanBreakPots()), LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_2, logic->CanBreakPots()), @@ -426,7 +427,7 @@ void RegionTable_Init_DodongosCavern() { Entrance(RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM, []{return Here(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanKillEnemy(RE_LIZALFOS);});}), }); - areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Before Upper Lizalfos", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Two Fires Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_1, logic->CanBreakPots()), LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_2, logic->CanBreakPots()), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 2e1a6ef14..978f5eabf 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -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)), }, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp index c8d80f98d..3deacb042 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp @@ -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);}), }, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp index 5466c598c..f1a0ebee7 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp @@ -149,7 +149,7 @@ void RegionTable_Init_GerudoTrainingGround() { Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER, []{return logic->SmallKeys(RR_GERUDO_TRAINING_GROUND, 3);}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Maze Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->MQGTGMazeSwitch, []{return logic->CanUse(RG_MEGATON_HAMMER);}), }, diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp index 7aa2066ca..20ff9b1e1 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp @@ -84,7 +84,7 @@ void RegionTable_Init_IceCavern() { //Exits //the switch for the glass blocking the entrance is linked to the switch that controls the glass around the skulltulla in RR_ICE_CAVERN_MQ_SCARECROW_ROOM //if you clear the ice, you can hit it with a pot from here. - Entrance(RR_ICE_CAVERN_BEGINNING, []{return logic->BlueFire();}), + Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->BlueFire();}), Entrance(RR_ICE_CAVERN_MQ_MAP_ROOM, []{return Here(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->CanKillEnemy(RE_WHITE_WOLFOS) && logic->CanKillEnemy(RE_FREEZARD);});}), Entrance(RR_ICE_CAVERN_MQ_COMPASS_ROOM, []{return logic->IsAdult && logic->BlueFire();}), Entrance(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, []{return logic->BlueFire();}), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 72aa9974b..7589c7b74 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -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);}), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp index ccbc2f688..61fb0962d 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp @@ -362,7 +362,7 @@ 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 true;}), @@ -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;}), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp index 777e29050..3e84bb72a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp @@ -265,7 +265,7 @@ void RegionTable_Init_Kakariko() { Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return true;}), }); - areaTable[RR_KAK_WELL] = Region("Kak Behind Gate", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_KAK_WELL] = Region("Kak Well", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_KAKARIKO_VILLAGE, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->DrainWell;}), Entrance(RR_BOTTOM_OF_THE_WELL_ENTRYWAY, []{return logic->IsChild || (logic->DrainWell && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).IsNot(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF));}), From 40da9997c59436d9acc52ba8a86b6c77a050b913 Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Thu, 29 May 2025 16:51:35 +0200 Subject: [PATCH 32/32] Fix some dark link issues (#5532) --- soh/soh/Enhancements/enemyrandomizer.cpp | 49 ++++++++++++++++++- .../vanilla-behavior/GIVanillaBehavior.h | 32 ++++++++++++ soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c | 2 +- .../actors/ovl_En_Torch2/z_en_torch2.c | 2 +- .../actors/ovl_player_actor/z_player.c | 20 ++++++-- 5 files changed, 97 insertions(+), 8 deletions(-) diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index bdb259719..682ef026c 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -11,8 +11,13 @@ extern "C" { #include +#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"), @@ -339,7 +344,7 @@ EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play) { if (filteredEnemyList.size() == 0) { filteredEnemyList = selectedEnemyList; } - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM_SEEDED) { + 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); @@ -533,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 }); \ No newline at end of file diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 223001711..e624515b4 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2011,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 diff --git a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c index 3797adafe..831e11b8c 100644 --- a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c +++ b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c @@ -507,7 +507,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) { this->collider2.base.ocFlags1 &= ~OC1_HIT; // "catch" osSyncPrintf(VT_FGCOL(GREEN) "キャッチ(%d)!!" VT_RST "\n", this->frameCount); - if (play->grabPlayer(play, player)) { + if (GameInteractor_Should(VB_LIKE_LIKE_GRAB_PLAYER, true, this) && play->grabPlayer(play, player)) { player->actor.parent = &this->actor; this->stopScroll = false; EnRr_SetupGrabPlayer(this, player); diff --git a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c index 4925e73cb..ceca597df 100644 --- a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c +++ b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c @@ -706,7 +706,7 @@ void EnTorch2_Update(Actor* thisx, PlayState* play2) { sStaggerCount = 0; } } - if (player->linearVelocity == -18.0f) { + if (GameInteractor_Should(VB_TORCH2_HANDLE_CLANKING, player->linearVelocity == -18.0f, this)) { if (this->actor.xzDistToPlayer > 80.0f) { player->linearVelocity = 1.2f; } else if (this->actor.xzDistToPlayer < 70.0f) { diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 6638893f5..b2ebcbb88 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4764,7 +4764,9 @@ s32 func_808382DC(Player* this, PlayState* play) { gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawnInfo->yaw; } - Play_TriggerVoidOut(play); + if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { + Play_TriggerVoidOut(play); + } } Player_PlayVoiceSfx(this, NA_SE_VO_LI_TAKEN_AWAY); @@ -5129,7 +5131,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol } if (exitIndex == 0) { - Play_TriggerVoidOut(play); + if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { + Play_TriggerVoidOut(play); + } Scene_SetTransitionForNextEntrance(play); } else { play->nextEntranceIndex = play->setupExitList[exitIndex - 1]; @@ -5163,7 +5167,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2, play->setupExitList[exitIndex - 1])) { gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = play->nextEntranceIndex; - Play_TriggerVoidOut(play); + if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { + Play_TriggerVoidOut(play); + } gSaveContext.respawnFlag = -2; } gSaveContext.retainWeatherMode = 1; @@ -5226,7 +5232,7 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol if (this->actor.bgCheckFlags & 1) { if (this->floorProperty == 5) { Play_TriggerRespawn(play); - } else { + } else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { Play_TriggerVoidOut(play); } play->transitionType = TRANS_TYPE_FADE_BLACK_FAST; @@ -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); }