From c61c1c0fece30802e79fb2d2d0085074e1fd52ee Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 23 Mar 2026 15:45:48 +0000 Subject: [PATCH 01/17] fix oversight in well logic (#6397) --- .../location_access/dungeons/bottom_of_the_well.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7fdfa1954..9a10a8a15 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 @@ -31,8 +31,8 @@ void RegionTable_Init_BottomOfTheWell() { EVENT_ACCESS(LOGIC_BOTW_LOWERED_WATER, logic->CanUse(RG_ZELDAS_LULLABY)), }, { //Locations - LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_LEFT_FAKE_WALL_CHEST, ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH) && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_BOTTOM_OF_THE_WELL_RIGHT_BOTTOM_FAKE_WALL_CHEST, ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_LEFT_FAKE_WALL_CHEST, (ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_BOTTOM_OF_THE_WELL_RIGHT_BOTTOM_FAKE_WALL_CHEST, (ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasItem(RG_OPEN_CHEST)), LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_CENTER_BOMBABLE_CHEST, logic->HasExplosives() && logic->HasItem(RG_OPEN_CHEST)), LOCATION(RC_BOTTOM_OF_THE_WELL_BACK_LEFT_BOMBABLE_CHEST, logic->HasExplosives() && (ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasItem(RG_OPEN_CHEST)), LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_FRONT_CHEST, (logic->Get(LOGIC_BOTW_LOWERED_WATER) && logic->HasItem(RG_OPEN_CHEST)) || logic->CanOpenUnderwaterChest()), From 2cfb0f73dc993c3d3d19e84aea92954408632cc8 Mon Sep 17 00:00:00 2001 From: A Green Spoon <121978037+A-Green-Spoon@users.noreply.github.com> Date: Tue, 24 Mar 2026 00:46:03 +0900 Subject: [PATCH 02/17] make non-cmc pot model major for shuffle clarity (#6398) --- soh/soh/Enhancements/randomizer/ShufflePots.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/ShufflePots.cpp b/soh/soh/Enhancements/randomizer/ShufflePots.cpp index dbf278db8..08b7a7c0c 100644 --- a/soh/soh/Enhancements/randomizer/ShufflePots.cpp +++ b/soh/soh/Enhancements/randomizer/ShufflePots.cpp @@ -54,7 +54,7 @@ extern "C" void ObjTsubo_RandomizerDraw(Actor* thisx, PlayState* play) { break; } } else { - gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gPotStandardDL); + gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gPotMajorDL); } } else { gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gPotDL); From c439d6213780df715feb6c7773d6f39bad2107a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Mon, 23 Mar 2026 16:27:20 +0000 Subject: [PATCH 03/17] Enemy rando: don't mutate ActorEntry (#6400) --- .../ExtraModes/EnemyRandomizer.cpp | 19 +++++++++++++------ .../vanilla-behavior/GIVanillaBehavior.h | 2 +- soh/src/code/z_actor.c | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp b/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp index e7b93484f..e3d311535 100644 --- a/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp +++ b/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp @@ -5,12 +5,10 @@ #include "soh/Enhancements/enhancementTypes.h" #include "soh/ObjectExtension/ObjectExtension.h" #include "variables.h" -#include "soh/OTRGlobals.h" #include "soh/cvar_prefixes.h" #include "soh/ResourceManagerHelpers.h" #include "soh/SohGui/MenuTypes.h" #include "soh/SohGui/SohMenu.h" -#include "soh/SohGui/SohGui.hpp" extern "C" { #include @@ -627,11 +625,20 @@ void RegisterEnemyRandomizer() { ActorContext* actorCtx = va_arg(args, ActorContext*); ActorEntry* actorEntry = va_arg(args, ActorEntry*); PlayState* play = va_arg(args, PlayState*); - Actor* actor = va_arg(args, Actor*); + Actor** actor = va_arg(args, Actor**); - if (!GetRandomizedEnemy(play, &actorEntry->id, &actorEntry->pos.x, &actorEntry->pos.y, &actorEntry->pos.z, - &actorEntry->rot.x, &actorEntry->rot.y, &actorEntry->rot.z, &actorEntry->params)) { - *should = false; + s16 actorId = actorEntry->id; + s16 posX = actorEntry->pos.x; + s16 posY = actorEntry->pos.y; + s16 posZ = actorEntry->pos.z; + s16 rotX = actorEntry->rot.x; + s16 rotY = actorEntry->rot.y; + s16 rotZ = actorEntry->rot.z; + s16 params = actorEntry->params; + + *should = false; + if (GetRandomizedEnemy(play, &actorId, &posX, &posY, &posZ, &rotX, &rotY, &rotZ, ¶ms)) { + *actor = Actor_Spawn(actorCtx, play, actorId, posX, posY, posZ, rotX, rotY, rotZ, params); } }); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 2065c929d..aae11fe86 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2663,7 +2663,7 @@ typedef enum { // - `*ActorContext` // - `*ActorEntry` // - `*PlayState` - // - `*Actor` + // - `**Actor` VB_SPAWN_ACTOR_ENTRY, // #### `result` diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index e999788a4..f0ca7c0cd 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -3471,7 +3471,7 @@ Actor* Actor_SpawnEntry(ActorContext* actorCtx, ActorEntry* actorEntry, PlayStat gMapLoading = 1; Actor* ret; - if (GameInteractor_Should(VB_SPAWN_ACTOR_ENTRY, true, actorCtx, actorEntry, play, ret)) { + if (GameInteractor_Should(VB_SPAWN_ACTOR_ENTRY, true, actorCtx, actorEntry, play, &ret)) { ret = Actor_Spawn(actorCtx, play, actorEntry->id, actorEntry->pos.x, actorEntry->pos.y, actorEntry->pos.z, actorEntry->rot.x, actorEntry->rot.y, actorEntry->rot.z, actorEntry->params); } From 5fe4680a20946ec7fa09299af3e7ad9d114eae24 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Mon, 23 Mar 2026 20:07:45 +0000 Subject: [PATCH 04/17] Adds a more compact Clear button to Check Tracker search (#6401) --- .../randomizer/randomizer_check_tracker.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index fdd97f669..5c681d307 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -1082,13 +1082,21 @@ void CheckTrackerWindow::DrawElement() { } UIWidgets::PushStyleCombobox(THEME_COLOR); if (CVarGetInteger(CVAR_TRACKER_CHECK("SearchInputVisible"), 1)) { - if (checkSearch.Draw("", ImGui::GetContentRegionAvail().x - 6)) { + if (checkSearch.Draw("", ImGui::GetContentRegionAvail().x - 42)) { UpdateFilters(); } - std::string checkSearchText = ""; - checkSearchText = checkSearch.InputBuf; + std::string checkSearchText = checkSearch.InputBuf; checkSearchText.erase(std::remove(checkSearchText.begin(), checkSearchText.end(), ' '), checkSearchText.end()); + ImGui::SameLine(); + if (UIWidgets::Button(ICON_FA_ERASER, UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Color(THEME_COLOR) + .Padding(ImVec2(10.f, 6.f)))) { + checkSearch.Clear(); + UpdateFilters(); + doAreaScroll = true; + } if (checkSearchText.length() < 1) { ImGui::SameLine(20.0f); ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Search..."); From 5e13570b8375a3ad5d85120deacaa879027c7b48 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Tue, 24 Mar 2026 19:37:21 +0000 Subject: [PATCH 05/17] Fix leak of shuffled fairy bottle-swipe behavior to other actors (#6405) --- soh/soh/Enhancements/randomizer/ShuffleFairies.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/ShuffleFairies.cpp b/soh/soh/Enhancements/randomizer/ShuffleFairies.cpp index 37e1d07be..730366949 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleFairies.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleFairies.cpp @@ -106,11 +106,13 @@ void RegisterShuffleFairies() { COND_VB_SHOULD(VB_BOTTLE_ACTOR, shouldRegister, { Actor* actor = va_arg(args, Actor*); - const auto fairyIdentity = ObjectExtension::GetInstance().Get(actor); - if (fairyIdentity != nullptr && fairyIdentity->randomizerInf != RAND_INF_MAX) { - Flags_SetRandomizerInf(fairyIdentity->randomizerInf); - actor->parent = &GET_PLAYER(gPlayState)->actor; - *should = false; + if (actor->id == ACTOR_EN_ELF) { + const auto fairyIdentity = ObjectExtension::GetInstance().Get(actor); + if (fairyIdentity != nullptr && fairyIdentity->randomizerInf != RAND_INF_MAX) { + Flags_SetRandomizerInf(fairyIdentity->randomizerInf); + actor->parent = &GET_PLAYER(gPlayState)->actor; + *should = false; + } } }); From 57269c8e46abbac83e2ab4a52a8fbcc72bc00ade Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:23:20 +0000 Subject: [PATCH 06/17] fix volv FTR logic (#6403) --- .../randomizer/location_access/dungeons/fire_temple.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3ff361b3d..c77d43cfb 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -1062,7 +1062,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_BOSS_ROOM] = Region("Fire Temple Boss Room", SCENE_FIRE_TEMPLE_BOSS, { // Events - EVENT_ACCESS(LOGIC_FIRE_TEMPLE_CLEAR, logic->FireTimer() >= 64 && logic->CanKillEnemy(RE_VOLVAGIA)), + EVENT_ACCESS(LOGIC_FIRE_TEMPLE_CLEAR, logic->CanUse(RG_GORON_TUNIC) && logic->CanKillEnemy(RE_VOLVAGIA)), }, { // Locations LOCATION(RC_FIRE_TEMPLE_VOLVAGIA_HEART, logic->Get(LOGIC_FIRE_TEMPLE_CLEAR)), From 94b650ec67dfa9a3b13f937ae0ba25a427147d21 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 26 Mar 2026 03:02:54 +0000 Subject: [PATCH 07/17] Fix several logic errors, create Hooskshot Bridge trick. (#6414) --- .../location_access/dungeons/bottom_of_the_well.cpp | 11 +++++------ .../location_access/dungeons/ice_cavern.cpp | 3 ++- .../location_access/overworld/castle_grounds.cpp | 2 +- .../overworld/death_mountain_trail.cpp | 3 ++- .../location_access/overworld/gerudo_valley.cpp | 2 +- .../randomizer/randomizerEnums/RandomizerTrick.h | 1 + soh/soh/Enhancements/randomizer/settings.cpp | 4 ++++ 7 files changed, 16 insertions(+), 10 deletions(-) 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 9a10a8a15..c63ca54a4 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 @@ -265,11 +265,10 @@ void RegionTable_Init_BottomOfTheWell() { // Fairies are in slingshot wonder item, & pot behind grate. Pot can also be broken with boomerang trick EVENT_ACCESS(LOGIC_FAIRY_ACCESS, (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT)) || (AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets()) || - //Item extension can get a fairy in 1 of 2 ways: we can either shoot the pot through the grate and let the fairy fly through the wall - //or we can shoot the eye target through the boulder, but not as adult with bow. - //The former cannot be done if the pot has an item in it, as it cannot be collected this way. - (ctx->GetTrickOption(RT_ITEM_EXTENSION) && - (logic->IsChild || ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OFF) || ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OVERWORLD)) ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))), + //Item extension can get a fairy by either shooting the pot through the grate and letting the fairy fly through the wall + //This cannot be done if the pot has an item in it, as it cannot be collected this way. + (ctx->GetTrickOption(RT_ITEM_EXTENSION) && (ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OFF) || ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OVERWORLD)) && logic->CanHitEyeTargets()) || + (ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))), //It is possible to hit the water switch with a pot from RR_BOTW_MQ_MIDDLE, however the hitbox for making it activate is very unintuitive //You have to throw the pot from further back to hit the switch from the front instead of the top, trying to hit the "fingers" directly //This unintuitiveness means it should be a trick. ZL is needed to get a clear path to carry the pot @@ -282,7 +281,7 @@ void RegionTable_Init_BottomOfTheWell() { //Not even bow extension seems to get adult's bow to work //this would be a trick LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_OUTER_LOBBY_POT, (AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets()) || - (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))), + (ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))), LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_BOMB_LEFT_HEART, logic->HasExplosives()), LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_BOMB_RIGHT_HEART, logic->HasExplosives()), }, { 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 d5c9c4637..24e905aa9 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp @@ -50,7 +50,8 @@ void RegionTable_Init_IceCavern() { }, { //Locations LOCATION(RC_ICE_CAVERN_MAP_CHEST, logic->BlueFire() && logic->HasItem(RG_OPEN_CHEST)), - // very easy to break pot through ice + // very easy to break pot through ice with most weapons + // Bow extesnion is possible, but very precise: X = 403, Z = 2062-3, Rot = -11475, needs a setup and is its own trick LOCATION(RC_ICE_CAVERN_FROZEN_POT_1, (logic->CanBreakPots() && logic->BlueFire()) || logic->HasExplosives() || (ctx->GetTrickOption(RT_VISIBLE_COLLISION) && ((logic->CanStandingShield() && logic->CanUse(RG_KOKIRI_SWORD)) || logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_MEGATON_HAMMER))) || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT))), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp index 14291b212..40ba46fcf 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp @@ -95,7 +95,7 @@ void RegionTable_Init_CastleGrounds() { //Exits ENTRANCE(RR_HC_GATE, true), ENTRANCE(RR_HC_STORMS_GROTTO, logic->CanOpenStormsGrotto()), - ENTRANCE(RR_HC_GARDEN, (logic->CanUse(RG_WEIRD_EGG) && logic->HasItem(RG_POWER_BRACELET) && logic->HasItem(RG_SPEAK_HYLIAN)) || + ENTRANCE(RR_HC_DRAIN_LEDGE, (logic->CanUse(RG_WEIRD_EGG) && logic->HasItem(RG_POWER_BRACELET) && logic->HasItem(RG_SPEAK_HYLIAN)) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->TakeDamage() && logic->HasExplosives() && logic->CanJumpslash())), }); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_trail.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_trail.cpp index 0a1890ea5..78d406403 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_trail.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_trail.cpp @@ -37,7 +37,8 @@ void RegionTable_Init_DeathMountainTrail() { //Locations LOCATION(RC_DMT_GS_FALLING_ROCKS_PATH, logic->IsAdult && logic->CanGetNightTimeGS() && (logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) && logic->CanUse(RG_LONGSHOT)) || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT)) || - (ctx->GetTrickOption(RT_DMT_UPPER_GS) && (logic->CanJumpslash() || logic->CanUse(RG_DINS_FIRE) || logic->HasExplosives() || ((ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) || ctx->GetTrickOption(RT_ITEM_EXTENSION)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT))))))), + (ctx->GetTrickOption(RT_DMT_UPPER_GS) && (logic->CanJumpslash() || logic->CanUse(RG_DINS_FIRE) || logic->HasExplosives() || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_FAIRY_SLINGSHOT)) || + (ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_LONGSHOT)))))), }, { //Exits ENTRANCE(RR_DEATH_MOUNTAIN_TRAIL, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp index a94cecadd..d93b877d4 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp @@ -19,7 +19,7 @@ void RegionTable_Init_GerudoValley() { ENTRANCE(RR_GV_CRATE_LEDGE, (logic->IsChild && logic->HasItem(RG_POWER_BRACELET)) || logic->CanUse(RG_LONGSHOT)), ENTRANCE(RR_GV_GROTTO_LEDGE, true), ENTRANCE(RR_GV_FORTRESS_SIDE, (logic->IsAdult && (logic->SummonEpona() || logic->CanUse(RG_LONGSHOT) || ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FREE) || logic->Get(LOGIC_TH_RESCUED_ALL_CARPENTERS))) || (ctx->GetTrickOption(RT_HOVER_BOOST_SIMPLE) && logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_HOVER_BOOTS)) || - ((logic->IsChild || ctx->GetTrickOption(RT_ITEM_EXTENSION)) && logic->CanUse(RG_HOOKSHOT)) || (logic->IsChild && ctx->GetTrickOption(RT_GV_CHILD_CUCCO_JUMP) && logic->HasItem(RG_POWER_BRACELET) && logic->CanJumpslash())), + ((logic->IsChild || ctx->GetTrickOption(RT_GV_HOOKSHOT_BRIDGE)) && logic->CanUse(RG_HOOKSHOT)) || (logic->IsChild && ctx->GetTrickOption(RT_GV_CHILD_CUCCO_JUMP) && logic->HasItem(RG_POWER_BRACELET) && logic->CanJumpslash())), ENTRANCE(RR_GV_WATERFALL_ALCOVE, logic->IsChild && logic->HasItem(RG_POWER_BRACELET)), ENTRANCE(RR_GV_LOWER_STREAM, logic->IsChild && logic->HasItem(RG_POWER_BRACELET)), }); diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerTrick.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerTrick.h index 687ba9d73..42ecc2bb9 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerTrick.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerTrick.h @@ -76,6 +76,7 @@ RANDO_ENUM_ITEM(RT_LH_WATER_HOOKSHOT) RANDO_ENUM_ITEM(RT_GV_CRATE_HOVERS) RANDO_ENUM_ITEM(RT_GV_CHILD_TENT) RANDO_ENUM_ITEM(RT_GV_CHILD_CUCCO_JUMP) +RANDO_ENUM_ITEM(RT_GV_HOOKSHOT_BRIDGE) RANDO_ENUM_ITEM(RT_PASS_GUARDS_WITH_NOTHING) RANDO_ENUM_ITEM(RT_GF_WASTELAND_GATE_SIDEHOP_SKIP) RANDO_ENUM_ITEM(RT_GF_ADULT_SKIP_WASTELAND_GATE) diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index bfa0d2ad1..35a37451b 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1677,6 +1677,10 @@ void Settings::CreateOptions() { OPT_TRICK(RT_GV_CHILD_CUCCO_JUMP, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::INTERMEDIATE }, "Gerudo Valley Jump Fence with Cucco", "GVCUC", "Using cucco as child, it's possible to jumpslash over the gate."); + OPT_TRICK(RT_GV_HOOKSHOT_BRIDGE, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::ADVANCED }, + "Gerudo Valley Bridge with only Hookshot", "GVHSBrg", + "Using Hookshot Extension and a precise setup, you can cross the broken bridge in Gerudo Valley with " + "only a Hookshot."); OPT_TRICK(RT_PASS_GUARDS_WITH_NOTHING, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, "Sneak Past Moving Gerudo Guards with No Items", "Guards", "The logic normally guarantees Bow or Hookshot to stun them from a distance," From 4aa6e2ec280de14cecd79cdb3de607d7bc9cf1f3 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Thu, 26 Mar 2026 14:18:01 +0000 Subject: [PATCH 08/17] Move retrieval of dbEntry after Actor_Destroy (#6410) Before I had the change back to the placeholder actor id the Dummy Player actors were spawned with, but since we grabbed the actorDB entry before Actor_Destroy was called it didn't matter. Move it and the requisite log statement to after Actor_Destroy. --- soh/src/code/z_actor.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index f0ca7c0cd..54c33e2cf 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -3491,12 +3491,6 @@ Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play) { // Execute before actor memory is freed GameInteractor_ExecuteOnActorDestroy(actor); - dbEntry = ActorDB_Retrieve(actor->id); - - if (HREG(20) != 0) { - osSyncPrintf("アクタークラス削除 [%s]\n", dbEntry->name); // "Actor class deleted [%s]" - } - if ((player != NULL) && (actor == player->focusActor)) { Player_ReleaseLockOn(player); Camera_ChangeMode(Play_GetCamera(play, Play_GetActiveCamId(play)), 0); @@ -3517,6 +3511,12 @@ Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play) { Audio_StopSfxByPos(&actor->projectedPos); Actor_Destroy(actor, play); + dbEntry = ActorDB_Retrieve(actor->id); + + if (HREG(20) != 0) { + osSyncPrintf("アクタークラス削除 [%s]\n", dbEntry->name); // "Actor class deleted [%s]" + } + newHead = Actor_RemoveFromCategory(play, actorCtx, actor); // #region SOH [ObjectExtension] From 69e03dcc52a141c8833b0e98490e5df0fed300ac Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Thu, 26 Mar 2026 15:11:22 +0000 Subject: [PATCH 09/17] LUS bump to fix texture pack performance (#6416) --- libultraship | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libultraship b/libultraship index 956f10821..fdcaf6336 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 956f1082122f219d536c20483df89469f4b807dc +Subproject commit fdcaf6336776d24a6408d016b0a52243f108f250 From 99c1f23d5b2b881681a0b01e35a1ccd1aa41333b Mon Sep 17 00:00:00 2001 From: Ali Date: Thu, 26 Mar 2026 11:12:51 -0400 Subject: [PATCH 10/17] fix: Fix macOS crashing on first install use (#6412) With the new imgui OTR generation flow, macOS would crash when Ship was run for the first time unless com.shipofharkinian.soh had already been created. Move the call to CheckAndCreateModFolder() earlier in execution to prevent crashing. --- soh/soh/OTRGlobals.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 7cd67c266..7cfdfc92f 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -445,6 +445,11 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) { std::shared_ptr threadPool = std::make_shared(1); std::optional> extractionTask; + +#if not defined(__SWITCH__) && not defined(__WIIU__) + CheckAndCreateModFolder(); +#endif + while (!extractDone) { if (SohGui::PopupsQueued() > 0 || extractionTask.has_value()) { goto render; @@ -760,10 +765,6 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) { #elif defined(__WIIU__) Ship::WiiU::Init(appShortName); #endif - -#if not defined(__SWITCH__) && not defined(__WIIU__) - CheckAndCreateModFolder(); -#endif } void OTRGlobals::Initialize() { From 5f139ef3115a31dd069575d83bd8738736b796b0 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 26 Mar 2026 22:39:17 +0000 Subject: [PATCH 11/17] Hookshot ladder fixes (#6417) --- .../randomizer/location_access/overworld/kokiri_forest.cpp | 4 +++- .../randomizer/location_access/overworld/zoras_river.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp index 43bb878d8..d0e78f824 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp @@ -70,7 +70,9 @@ void RegionTable_Init_KokiriForest() { }, { //Exits ENTRANCE(RR_KF_BOULDER_LOOP, logic->CanUse(RG_CRAWL)), - ENTRANCE(RR_KF_LINKS_PORCH, logic->IsChild ? logic->CanClimbLadder() : logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOVER_BOOTS)), + //The Deku Baba blocks the setup as Adult, and stunning doesn't last long enough to perform it. + ENTRANCE(RR_KF_LINKS_PORCH, logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOVER_BOOTS) || + ((logic->IsChild || logic->CanKillEnemy(RE_DEKU_BABA) || logic->Get(LOGIC_FOREST_TEMPLE_CLEAR)) && logic->CanClimbLadder())), ENTRANCE(RR_KF_MIDOS_HOUSE, true), ENTRANCE(RR_KF_SARIAS_HOUSE, true), ENTRANCE(RR_KF_HOUSE_OF_TWINS, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp index e20cf5ea6..6eea34727 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp @@ -59,7 +59,7 @@ void RegionTable_Init_ZoraRiver() { }, { //Exits ENTRANCE(RR_ZR_FRONT, logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_POWER_BRACELET) || logic->BlastOrSmash() || logic->HasItem(RG_HOVER_BOOTS)), - ENTRANCE(RR_ZR_ATOP_LADDER, (logic->IsAdult || logic->HasItem(RG_POWER_BRACELET)) && (logic->CanClimbLadder() || CanPlantBean(RR_ZORAS_RIVER, RG_ZORAS_RIVER_BEAN_SOUL))), + ENTRANCE(RR_ZR_ATOP_LADDER, ((logic->IsAdult || logic->HasItem(RG_POWER_BRACELET)) && logic->CanClimbLadder()) || (logic->IsAdult && CanPlantBean(RR_ZORAS_RIVER, RG_ZORAS_RIVER_BEAN_SOUL))), ENTRANCE(RR_ZR_PILLAR, (logic->IsChild && logic->HasItem(RG_POWER_BRACELET)) || logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && ctx->GetTrickOption(RT_ZR_LOWER))), ENTRANCE(RR_ZR_FROM_SHORTCUT, logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS)), ENTRANCE(RR_ZR_STORMS_GROTTO, logic->CanOpenStormsGrotto()), From 53dc7f43ba1bb2ae813ad239623d7090dd7a3f78 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Fri, 27 Mar 2026 01:43:36 +0000 Subject: [PATCH 12/17] Fix DMC oversight (#6419) --- .../overworld/death_mountain_crater.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_crater.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_crater.cpp index 2ece82c25..d0194ac67 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_crater.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_crater.cpp @@ -92,13 +92,13 @@ void RegionTable_Init_DeathMountainCrater() { ENTRANCE(RR_DMC_POTS, logic->FireTimer() >= 8 || logic->Hearts() >= 2), ENTRANCE(RR_DMC_POT_GROTTO_EXIT, logic->FireTimer() >= 16 || logic->Hearts() >= 3), ENTRANCE(RR_DMC_CENTRAL, ((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad()) || - (logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))), + (logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage() && logic->CanClimbLadder())), ENTRANCE(RR_DMC_FAR_PLATFORM, (logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->DMCPotsToPad() && logic->ReachDistantScarecrow()) || - ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT))) || + ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && logic->CanClimbLadder()) || (logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->DMCPotsToPad() && CanPlantBean(RR_DMC_CENTRAL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL)) || ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && ctx->GetTrickOption(RT_DMC_HOVER_BEAN_POH) && logic->CanUse(RG_HOVER_BOOTS))), ENTRANCE(RR_DMC_TEMPLE_EXIT, ((logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->DMCPotsToPad()) || - (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))), + (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && logic->CanClimbLadder())), }); areaTable[RR_DMC_POTS_ENTRY] = Region("DMC Pots Entry", SCENE_DEATH_MOUNTAIN_CRATER, {}, { @@ -124,11 +124,11 @@ void RegionTable_Init_DeathMountainCrater() { ENTRANCE(RR_DMC_POTS, true), ENTRANCE(RR_DMC_POT_GROTTO_EXIT, logic->FireTimer() >= 8 || logic->Hearts() >= 2), ENTRANCE(RR_DMC_CENTRAL, ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->DMCPotsToPad()) || - (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))), + (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && logic->CanClimbLadder())), ENTRANCE(RR_DMC_FAR_PLATFORM, (logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad() && logic->ReachDistantScarecrow()) || - ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))), + ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && logic->CanClimbLadder())), ENTRANCE(RR_DMC_TEMPLE_EXIT, ((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad()) || - (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))), + (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && logic->CanClimbLadder())), }); areaTable[RR_DMC_POT_GROTTO_ENTRY] = Region("DMC Pot Grotto Entry", SCENE_DEATH_MOUNTAIN_CRATER, {}, { @@ -154,11 +154,11 @@ void RegionTable_Init_DeathMountainCrater() { ENTRANCE(RR_DMC_POTS, logic->FireTimer() >= 8 || logic->Hearts() >= 2), ENTRANCE(RR_DMC_POT_GROTTO_EXIT, true), ENTRANCE(RR_DMC_CENTRAL, ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->DMCPotsToPad()) || - (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))), + (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && logic->CanClimbLadder())), ENTRANCE(RR_DMC_FAR_PLATFORM, (logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad() && logic->ReachDistantScarecrow()) || - ((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->TakeDamage() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))), + ((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->TakeDamage() && logic->CanClimbLadder())), ENTRANCE(RR_DMC_TEMPLE_EXIT, ((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad()) || - (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))), + (logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && logic->CanClimbLadder())), }); areaTable[RR_DMC_PAD_ENTRY] = Region("DMC Pad Entry", SCENE_DEATH_MOUNTAIN_CRATER, {}, { From 7edf44e1709f2537b99ae5c6ae1df108e8462ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Fri, 27 Mar 2026 02:07:11 +0000 Subject: [PATCH 13/17] add tts for Roc's Feather (#6418) --- .../accessibility/texts/kaleidoscope_eng.json | 1 + .../accessibility/texts/kaleidoscope_fra.json | 1 + .../accessibility/texts/kaleidoscope_ger.json | 1 + soh/soh/Enhancements/tts/tts.cpp | 29 +++++-------------- 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/soh/assets/custom/accessibility/texts/kaleidoscope_eng.json b/soh/assets/custom/accessibility/texts/kaleidoscope_eng.json index 04fc1f8f2..8409287b5 100644 --- a/soh/assets/custom/accessibility/texts/kaleidoscope_eng.json +++ b/soh/assets/custom/accessibility/texts/kaleidoscope_eng.json @@ -172,6 +172,7 @@ "153": "STICK UPGRADE 30", "154": "NUT UPGRADE 30", "155": "NUT UPGRADE 40", + "157": "Roc's Feather", "255": "", "256": "Haunted Wasteland", "257": "Gerudos Fortress", diff --git a/soh/assets/custom/accessibility/texts/kaleidoscope_fra.json b/soh/assets/custom/accessibility/texts/kaleidoscope_fra.json index 93e0058dc..0e26e8e93 100644 --- a/soh/assets/custom/accessibility/texts/kaleidoscope_fra.json +++ b/soh/assets/custom/accessibility/texts/kaleidoscope_fra.json @@ -172,6 +172,7 @@ "153": "AMÉLIORATION BÂTON MOJO 30", "154": "AMÉLIORATION NOIX MOJO 30", "155": "AMÉLIORATION NOIX MOJO 40", + "157": "Plume de Roc", "255": "", "256": "Désert Hanté", "257": "Forteresse Gerudo", diff --git a/soh/assets/custom/accessibility/texts/kaleidoscope_ger.json b/soh/assets/custom/accessibility/texts/kaleidoscope_ger.json index 95fb567b6..cafb22d54 100644 --- a/soh/assets/custom/accessibility/texts/kaleidoscope_ger.json +++ b/soh/assets/custom/accessibility/texts/kaleidoscope_ger.json @@ -172,6 +172,7 @@ "153": "DEKU-STAB-KAPAZITÄT 30", "154": "DEKU-NUẞ-KAPAZITÄT 30", "155": "DEKU-NUẞ-KAPAZITÄT 40", + "157": "Greifenfeder", "255": "", "256": "Gespensterwüste", "257": "Gerudo-Festung", diff --git a/soh/soh/Enhancements/tts/tts.cpp b/soh/soh/Enhancements/tts/tts.cpp index b1cb15812..fddcb77ce 100644 --- a/soh/soh/Enhancements/tts/tts.cpp +++ b/soh/soh/Enhancements/tts/tts.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include "soh/ShipInit.hpp" #include "message_data_static.h" @@ -37,11 +36,10 @@ nlohmann::json fileChooseMap = nullptr; std::string GetParameritizedText(std::string key, TextBank bank, const char* arg) { switch (bank) { case TEXT_BANK_SCENES: { - return sceneMap[key].get(); - break; + return sceneMap.value(key, "unknown"); } case TEXT_BANK_MISC: { - auto value = miscMap[key].get(); + auto value = miscMap.value(key, "unknown"); std::string searchString = "$0"; size_t index = value.find(searchString); @@ -49,15 +47,11 @@ std::string GetParameritizedText(std::string key, TextBank bank, const char* arg if (index != std::string::npos) { assert(arg != nullptr); value.replace(index, searchString.size(), std::string(arg)); - return value; - } else { - return value; } - - break; + return value; } case TEXT_BANK_KALEIDO: { - auto value = kaleidoMap[key].get(); + auto value = kaleidoMap.value(key, "unknown"); std::string searchString = "$0"; size_t index = value.find(searchString); @@ -65,15 +59,11 @@ std::string GetParameritizedText(std::string key, TextBank bank, const char* arg if (index != std::string::npos) { assert(arg != nullptr); value.replace(index, searchString.size(), std::string(arg)); - return value; - } else { - return value; } - - break; + return value; } case TEXT_BANK_FILECHOOSE: { - auto value = fileChooseMap[key].get(); + auto value = fileChooseMap.value(key, "unknown"); std::string searchString = "$0"; size_t index = value.find(searchString); @@ -81,14 +71,11 @@ std::string GetParameritizedText(std::string key, TextBank bank, const char* arg if (index != std::string::npos) { assert(arg != nullptr); value.replace(index, searchString.size(), std::string(arg)); - return value; - } else { - return value; } - - break; + return value; } } + return "unknown"; } const char* GetLanguageCode() { From d1643aa19646a5483e2c90ef7bf5f565594727db Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Fri, 27 Mar 2026 21:40:34 -0500 Subject: [PATCH 14/17] LACS-Dungeons Check Availability (#6202) * Fixes #5433, LACS-Dungeons beatable vs beat. * Use CalculatingAvailableChecks in Logic::DungeonCount to check flags. --- soh/soh/Enhancements/randomizer/logic.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 427a6c4c2..18366f479 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1396,9 +1396,20 @@ uint8_t Logic::Hearts() { } uint8_t Logic::DungeonCount() { - return Get(LOGIC_DEKU_TREE_CLEAR) + Get(LOGIC_DODONGOS_CAVERN_CLEAR) + Get(LOGIC_JABU_JABUS_BELLY_CLEAR) + - Get(LOGIC_FOREST_TEMPLE_CLEAR) + Get(LOGIC_FIRE_TEMPLE_CLEAR) + Get(LOGIC_WATER_TEMPLE_CLEAR) + - Get(LOGIC_SPIRIT_TEMPLE_CLEAR) + Get(LOGIC_SHADOW_TEMPLE_CLEAR); + if (CalculatingAvailableChecks) { + return CheckEventChkInf(EVENTCHKINF_USED_DEKU_TREE_BLUE_WARP) + + CheckEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP) + + CheckEventChkInf(EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP) + + CheckEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) + + CheckEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP) + + CheckEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP) + + CheckRandoInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE) + + CheckRandoInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE); + } else { + return Get(LOGIC_DEKU_TREE_CLEAR) + Get(LOGIC_DODONGOS_CAVERN_CLEAR) + Get(LOGIC_JABU_JABUS_BELLY_CLEAR) + + Get(LOGIC_FOREST_TEMPLE_CLEAR) + Get(LOGIC_FIRE_TEMPLE_CLEAR) + Get(LOGIC_WATER_TEMPLE_CLEAR) + + Get(LOGIC_SPIRIT_TEMPLE_CLEAR) + Get(LOGIC_SHADOW_TEMPLE_CLEAR); + } } uint8_t Logic::StoneCount() { From 42ba25449ee39ed280821f328275c569ea41e975 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sat, 28 Mar 2026 02:58:41 +0000 Subject: [PATCH 15/17] Make Infinite Ammo Cheat respect Progressive Bombchu Bag Capacities (#6421) Cap infinite ammo for chus according to rando settings Add bombchuUpgradeLevel to save editor --- soh/soh/Enhancements/Cheats/Infinite/Ammo.cpp | 7 +++- .../Enhancements/debugger/debugSaveEditor.cpp | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/Cheats/Infinite/Ammo.cpp b/soh/soh/Enhancements/Cheats/Infinite/Ammo.cpp index b54b330bf..c5a0297de 100644 --- a/soh/soh/Enhancements/Cheats/Infinite/Ammo.cpp +++ b/soh/soh/Enhancements/Cheats/Infinite/Ammo.cpp @@ -1,5 +1,6 @@ #include #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/OTRGlobals.h" #include "soh/ShipInit.hpp" #include "z64save.h" @@ -24,7 +25,11 @@ void OnGameFrameUpdateInfiniteAmmo() { AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); if (INV_CONTENT(ITEM_BOMBCHU) != ITEM_NONE) { - AMMO(ITEM_BOMBCHU) = 50; + int chuCapacity = 50; + if (IS_RANDO && RAND_GET_OPTION(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_PROGRESSIVE)) { + chuCapacity = OTRGlobals::Instance->gRandoContext->GetBombchuCapacity(); + } + AMMO(ITEM_BOMBCHU) = chuCapacity; } } diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index f823d99f6..3c99ea19e 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -1,4 +1,5 @@ #include "debugSaveEditor.h" +#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/util.h" #include "soh/SohGui/ImGuiUtils.h" #include "soh/OTRGlobals.h" @@ -1392,6 +1393,39 @@ void DrawEquipmentTab() { "40", }; DrawUpgrade("Deku Nut Capacity", UPG_NUTS, nutNames); + + if (IS_RANDO && + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOMBCHU_BAG) == RO_BOMBCHU_BAG_PROGRESSIVE) { + const std::vector bombchuNames = { + "None", + "20", + "30", + "50", + }; + ImGui::Text("%s", "Bombchu Bag Capacity"); + ImGui::SameLine(); + ImGui::PushID("Bombchu Bag Capacity"); + PushStyleCombobox(THEME_COLOR); + ImGui::AlignTextToFramePadding(); + auto value = gSaveContext.ship.quest.data.randomizer.bombchuUpgradeLevel; + auto name = value < bombchuNames.size() ? bombchuNames[value].c_str() : "Glitched"; + if (ImGui::BeginCombo("##upgrade", name)) { + for (size_t i = 0; i < bombchuNames.size(); i++) { + if (ImGui::Selectable(bombchuNames[i].c_str())) { + gSaveContext.ship.quest.data.randomizer.bombchuUpgradeLevel = i; + if (i > 0) { + INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU; + } else { + INV_CONTENT(ITEM_BOMBCHU) = ITEM_NONE; + } + } + } + ImGui::EndCombo(); + } + PopStyleCombobox(); + ImGui::PopID(); + UIWidgets::Tooltip("Bombchu Bag Capapcity"); + } } // Draws a toggleable icon for a quest item that is faded when disabled From 6fea1821a1cf5abd0a5fc6652b562c9a1d8e8a61 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Fri, 27 Mar 2026 20:43:55 -0700 Subject: [PATCH 16/17] Changed name of scarecrow's song enhancement and added to the description to clarify that it only skips song playback without the rando setting. (#6423) --- soh/soh/SohGui/SohMenuEnhancements.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 8e9a9059b..c8e6c34d4 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -491,16 +491,18 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Skip Tower Escape", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipTowerEscape")) .Options(CheckboxOptions().Tooltip("Skip the tower escape sequence between Ganondorf and Ganon.")); - AddWidget(path, "Skip Scarecrow's Song", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Skip Playing Scarecrow's Song", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("InstantScarecrow")) .PreFunc([](WidgetInfo& info) { info.options->disabled = IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_SKIP_SCARECROWS_SONG); - info.options->disabledTooltip = "This setting is forcefully enabled because a randomized " - "save file with the option \"Skip Scarecrow Song\" is currently loaded."; + info.options->disabledTooltip = "This setting is forcefully enabled because a randomized save " + "file with the option \"Skip Scarecrow's Song\" is currently loaded."; }) .Options(CheckboxOptions().Tooltip( - "Pierre appears when an Ocarina is pulled out. Requires learning the Scarecrow's Song first.")); + "Pierre appears when an Ocarina is pulled out. Requires learning the Scarecrow's Song first.\n" + "Without the randomizer option \"Skip Scarecrow's Song\" enabled for a seed, this still requires you " + "to teach the scarecrow the song as both ages before summoning.")); AddWidget(path, "Faster Rupee Accumulator", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FasterRupeeAccumulator")) .Options(CheckboxOptions().Tooltip("Causes your Wallet to fill and empty faster when you gain or lose money.")); From d1b7edfa7c1fed2eb641abb1a3b2ce0d18819277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Sat, 28 Mar 2026 04:15:06 +0000 Subject: [PATCH 17/17] 9.2.1 Ackbar Bravo (#6424) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14cf4b201..f22967641 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 23 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.2.0 LANGUAGES C CXX) +project(Ship VERSION 9.2.1 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) set(SPDLOG_LEVEL_TRACE 0)