From 3793e821c8be82c14dcfdc10921c20b483500370 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Sat, 8 Nov 2025 15:06:01 -0500 Subject: [PATCH 01/10] Autosave disabled when Ocarina of Time obtained but not Song of Time (#5936) * Autosave disabled when Ocarina of Time obtained but not Song of Time * Add explanation comment Added a condition to prevent autosaving between obtaining the Ocarina of Time and the Song of Time. --- soh/soh/Enhancements/{ => QoL}/Autosave.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) rename soh/soh/Enhancements/{ => QoL}/Autosave.cpp (81%) diff --git a/soh/soh/Enhancements/Autosave.cpp b/soh/soh/Enhancements/QoL/Autosave.cpp similarity index 81% rename from soh/soh/Enhancements/Autosave.cpp rename to soh/soh/Enhancements/QoL/Autosave.cpp index faa5ee8dd..01f85e0cd 100644 --- a/soh/soh/Enhancements/Autosave.cpp +++ b/soh/soh/Enhancements/QoL/Autosave.cpp @@ -7,6 +7,7 @@ extern "C" { extern PlayState* gPlayState; #include "functions.h" +#include "macros.h" #include "variables.h" } @@ -15,27 +16,29 @@ static uint64_t lastSaveTimestamp = GetUnixTimestamp(); #define CVAR_AUTOSAVE_NAME CVAR_ENHANCEMENT("Autosave") #define CVAR_AUTOSAVE_DEFAULT AUTOSAVE_OFF #define CVAR_AUTOSAVE_VALUE CVarGetInteger(CVAR_AUTOSAVE_NAME, CVAR_AUTOSAVE_DEFAULT) -#define THREE_MINUTES_IN_UNIX 3 * 60000 +static constexpr uint64_t THREE_MINUTES_IN_UNIX = 3 * 60000; typedef enum { AUTOSAVE_OFF, AUTOSAVE_ON, } AutosaveOptions; -bool Autosave_CanSave() { +static bool Autosave_CanSave() { // Don't save when in title screen or debug file // Don't save the first 60 frames to not save the magic meter when it's still in the animation of filling it. // Don't save in Chamber of Sages and the Cutscene map because of remember save location and cutscene item gives. + // Don't save between obtaining Ocarina of Time and Song of Time because the latter would become unobtainable. if (!GameInteractor::IsSaveLoaded(false) || gPlayState->gameplayFrames < 60 || - gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES || gPlayState->sceneNum == SCENE_CUTSCENE_MAP) { + gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES || gPlayState->sceneNum == SCENE_CUTSCENE_MAP || + (!CHECK_QUEST_ITEM(QUEST_SONG_TIME) && (INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME))) { return false; } return true; } -void Autosave_PerformSave() { +static void Autosave_PerformSave() { Play_PerformSave(gPlayState); // Send notification @@ -44,7 +47,7 @@ void Autosave_PerformSave() { }); } -void Autosave_IntervalSave() { +static void Autosave_IntervalSave() { // Check if the interval has passed in minutes. uint64_t currentTimestamp = GetUnixTimestamp(); if ((currentTimestamp - lastSaveTimestamp) < THREE_MINUTES_IN_UNIX) { @@ -64,13 +67,13 @@ void Autosave_IntervalSave() { } } -void Autosave_SoftResetSave() { +static void Autosave_SoftResetSave() { if (Autosave_CanSave()) { Autosave_PerformSave(); } } -void RegisterAutosave() { +static void RegisterAutosave() { COND_HOOK(GameInteractor::OnGameFrameUpdate, CVAR_AUTOSAVE_VALUE, Autosave_IntervalSave); COND_HOOK(GameInteractor::OnExitGame, CVAR_AUTOSAVE_VALUE, [](int32_t fileNum) { Autosave_SoftResetSave(); }); } From 0e162cbb7c21f142d3a5cb97d8aa48281171f5bc Mon Sep 17 00:00:00 2001 From: Garrett Cox Date: Sat, 8 Nov 2025 15:49:35 -0600 Subject: [PATCH 02/10] Fix skybox being manipulated outside of appropriate scenes, other tweaks to disable 2d backgrounds impl (#5925) --- .../Graphics/Disable2DBackgrounds.cpp | 85 +++++++------------ soh/soh/z_scene_otr.cpp | 5 +- 2 files changed, 32 insertions(+), 58 deletions(-) diff --git a/soh/soh/Enhancements/Graphics/Disable2DBackgrounds.cpp b/soh/soh/Enhancements/Graphics/Disable2DBackgrounds.cpp index 253e0abd2..9fbc3375d 100644 --- a/soh/soh/Enhancements/Graphics/Disable2DBackgrounds.cpp +++ b/soh/soh/Enhancements/Graphics/Disable2DBackgrounds.cpp @@ -13,7 +13,7 @@ extern PlayState* gPlayState; #define CVAR_NAME CVAR_ENHANCEMENT("3DSceneRender") #define CVAR_VALUE CVarGetInteger(CVAR_NAME, 0) -std::vector fogControlList = { +std::set fogControlList = { SCENE_MARKET_ENTRANCE_DAY, SCENE_MARKET_ENTRANCE_NIGHT, SCENE_MARKET_ENTRANCE_RUINS, @@ -46,7 +46,7 @@ std::vector fogControlList = { SCENE_GRAVEKEEPERS_HUT, }; -std::vector skyboxSceneControlList = { +std::set skyboxSceneControlList = { SCENE_MARKET_ENTRANCE_DAY, SCENE_MARKET_ENTRANCE_NIGHT, SCENE_MARKET_ENTRANCE_RUINS, @@ -62,7 +62,7 @@ std::vector skyboxSceneControlList = { SCENE_FOREST_TEMPLE, }; -std::vector skyboxIdControlList = { +std::set skyboxIdControlList = { SKYBOX_BAZAAR, SKYBOX_HOUSE_LINK, SKYBOX_MARKET_ADULT, @@ -88,74 +88,49 @@ std::vector skyboxIdControlList = { void Register3DPreRenderedScenes() { COND_HOOK(AfterSceneCommands, CVAR_VALUE, [](int16_t sceneNum) { - // Check if this scene is in the skyboxControlList - bool shouldControlSkybox = false; - for (const auto& scene : skyboxSceneControlList) { - if (sceneNum == scene) { - shouldControlSkybox = true; - break; - } + if (!skyboxSceneControlList.contains(static_cast(sceneNum))) { + return; } - if (shouldControlSkybox) { - // Add a skybox on scenes from skyboxSceneControlList - gPlayState->envCtx.skyboxDisabled = false; + // Add a skybox on scenes from skyboxSceneControlList + gPlayState->envCtx.skyboxDisabled = false; - // Replace skybox with normal sky - gPlayState->skyboxId = SKYBOX_NORMAL_SKY; - // Apply the always cloudy skybox as an adult for Temple of Time and the Market - if (sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS || sceneNum == SCENE_MARKET_RUINS || - sceneNum == SCENE_MARKET_ENTRANCE_RUINS) { - gWeatherMode = 3; - } + // Replace skybox with normal sky + gPlayState->skyboxId = SKYBOX_NORMAL_SKY; + // Apply the always cloudy skybox as an adult for Temple of Time and the Market + if (sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS || sceneNum == SCENE_MARKET_RUINS || + sceneNum == SCENE_MARKET_ENTRANCE_RUINS) { + gWeatherMode = 3; } }); COND_HOOK(OnPlayDrawBegin, CVAR_VALUE, []() { - if (!CVarGetInteger(CVAR_ENHANCEMENT("3DSceneRender"), 0)) { + if (!fogControlList.contains(static_cast(gPlayState->sceneNum))) { return; } - for (auto& scene : fogControlList) { - if (scene == gPlayState->sceneNum) { - if ((HREG(80) != 10) || (HREG(82) != 0)) { - // Furthest possible fog and zFar - gPlayState->view.zFar = 12800; - gPlayState->lightCtx.fogNear = 996; // Set to 1000 to complete disable fog entirely - gPlayState->lightCtx.fogFar = 12800; - // General gray fog color - gPlayState->lightCtx.fogColor[0] = 100; - gPlayState->lightCtx.fogColor[1] = 100; - gPlayState->lightCtx.fogColor[2] = 100; - } - break; - } - } - }); - REGISTER_VB_SHOULD(VB_DRAW_2D_BACKGROUND, { - if (CVAR_VALUE) { - *should = false; - return; + if ((HREG(80) != 10) || (HREG(82) != 0)) { + // Furthest possible fog and zFar + gPlayState->view.zFar = 12800; + gPlayState->lightCtx.fogNear = 996; // Set to 1000 to complete disable fog entirely + gPlayState->lightCtx.fogFar = 12800; + // General gray fog color + gPlayState->lightCtx.fogColor[0] = 100; + gPlayState->lightCtx.fogColor[1] = 100; + gPlayState->lightCtx.fogColor[2] = 100; } }); - REGISTER_VB_SHOULD(VB_LOAD_SKYBOX, { - if (!gPlayState) { + COND_VB_SHOULD(VB_DRAW_2D_BACKGROUND, CVAR_VALUE, { *should = false; }); + + COND_VB_SHOULD(VB_LOAD_SKYBOX, CVAR_VALUE, { + if (!gPlayState || !skyboxIdControlList.contains(static_cast(gPlayState->skyboxCtx.skyboxId))) { return; } - if (!CVAR_VALUE) { - return; - } - - for (auto& skybox : skyboxIdControlList) { - if (gPlayState->skyboxCtx.skyboxId == skybox) { - gPlayState->skyboxCtx.unk_140 = 0; - *should = false; - return; - } - } + gPlayState->skyboxCtx.unk_140 = 0; + *should = false; }); } -static RegisterShipInitFunc initFunc(Register3DPreRenderedScenes, { CVAR_NAME }); \ No newline at end of file +static RegisterShipInitFunc initFunc(Register3DPreRenderedScenes, { CVAR_NAME }); diff --git a/soh/soh/z_scene_otr.cpp b/soh/soh/z_scene_otr.cpp index d49367024..1da9d729a 100644 --- a/soh/soh/z_scene_otr.cpp +++ b/soh/soh/z_scene_otr.cpp @@ -472,13 +472,12 @@ extern "C" s32 OTRfunc_800973FC(PlayState* play, RoomContext* roomCtx) { gSegments[3] = VIRTUAL_TO_PHYSICAL(roomCtx->unk_34); OTRScene_ExecuteCommands(play, (SOH::Scene*)roomCtx->roomToLoad); - if (!GameInteractor_Should(VB_DRAW_2D_BACKGROUND, true)) { - play->envCtx.skyboxDisabled = false; - } Player_SetBootData(play, GET_PLAYER(play)); Actor_SpawnTransitionActors(play, &play->actorCtx); + GameInteractor_ExecuteAfterSceneCommands(play->sceneNum); + return 1; } From 3d525d7eb1aef655be745724f3c1bedf518456bb Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 9 Nov 2025 15:58:18 +0000 Subject: [PATCH 03/10] Fix excluded locations getting bombchus when bombchu bag is off (#5943) * Fix bombchus being consistently added to excluded locations * change to be a random junk item --- soh/soh/Enhancements/randomizer/3drando/fill.cpp | 2 +- .../Enhancements/randomizer/3drando/item_pool.cpp | 13 ------------- .../Enhancements/randomizer/3drando/item_pool.hpp | 1 - 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 3204ace65..99deb9b70 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -1018,7 +1018,7 @@ static void FillExcludedLocations() { FilterFromPool(ctx->allLocations, [ctx](const auto loc) { return ctx->GetItemLocation(loc)->IsExcluded(); }); for (RandomizerCheck loc : excludedLocations) { - PlaceJunkInExcludedLocation(loc); + ctx->PlaceItemInLocation(loc, GetJunkItem()); } } diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index e8cab933f..ea6123b24 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -350,19 +350,6 @@ static void ReplaceMaxItem(const RandomizerGet itemToReplace, int max) { } } -void PlaceJunkInExcludedLocation(const RandomizerCheck il) { - // place a non-advancement item in this location - auto ctx = Rando::Context::GetInstance(); - for (size_t i = 0; i < ItemPool.size(); i++) { - if (Rando::StaticData::RetrieveItem(ItemPool[i]).GetCategory() == ITEM_CATEGORY_JUNK) { - ctx->PlaceItemInLocation(il, ItemPool[i]); - ItemPool.erase(ItemPool.begin() + i); - return; - } - } - SPDLOG_ERROR("ERROR: No Junk to Place!!!"); -} - static void PlaceVanillaMapsAndCompasses() { auto ctx = Rando::Context::GetInstance(); for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) { diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.hpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.hpp index 095b417b4..a437099c8 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.hpp @@ -9,7 +9,6 @@ class ItemLocation; void AddItemToPool(std::vector& pool, const RandomizerGet item, size_t count = 1); RandomizerGet GetJunkItem(); -void PlaceJunkInExcludedLocation(const RandomizerCheck il); void GenerateItemPool(); extern std::vector ItemPool; From 7ff93a396017d72c6fef3fc6f3477a0d4d534351 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Sun, 9 Nov 2025 20:27:09 -0500 Subject: [PATCH 04/10] Dampe's Hut open all night when Dampe is out (#5937) --- .../Enhancements/TimeSavers/DampeAllNight.cpp | 37 +++++++++++++++++++ .../Enhancements/timesaver_hook_handlers.cpp | 5 --- 2 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 soh/soh/Enhancements/TimeSavers/DampeAllNight.cpp diff --git a/soh/soh/Enhancements/TimeSavers/DampeAllNight.cpp b/soh/soh/Enhancements/TimeSavers/DampeAllNight.cpp new file mode 100644 index 000000000..683e7fbca --- /dev/null +++ b/soh/soh/Enhancements/TimeSavers/DampeAllNight.cpp @@ -0,0 +1,37 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/OTRGlobals.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "src/overlays/actors/ovl_En_Door/z_en_door.h" +extern SaveContext gSaveContext; +extern PlayState* gPlayState; +} + +static constexpr int32_t CVAR_DAMPE_ALL_NIGHT_DEFAULT = 0; +#define CVAR_DAMPE_ALL_NIGHT_NAME CVAR_ENHANCEMENT("DampeAllNight") +#define CVAR_DAMPE_ALL_NIGHT_VALUE CVarGetInteger(CVAR_DAMPE_ALL_NIGHT_NAME, CVAR_DAMPE_ALL_NIGHT_DEFAULT) + +static constexpr s16 DAMPE_HUT_DOOR_OPEN = 447; +static constexpr s16 DAMPE_HUT_DOOR_CLOSED = 774; + +static bool DampeIsResting() { + return LINK_IS_ADULT || gPlayState->sceneNum != SCENE_GRAVEYARD; +} + +static void OpenDampeHutDoor(void* refActor) { + EnDoor* enDoor = static_cast(refActor); + s16* params = &enDoor->actor.params; + + if (*params == DAMPE_HUT_DOOR_CLOSED && !DampeIsResting()) { + *params = DAMPE_HUT_DOOR_OPEN; + EnDoor_SetupType(enDoor, gPlayState); + } +} + +static void RegisterDampeAllNight() { + COND_VB_SHOULD(VB_DAMPE_IN_GRAVEYARD_DESPAWN, CVAR_DAMPE_ALL_NIGHT_VALUE, { *should = DampeIsResting(); }); + COND_ID_HOOK(OnActorInit, ACTOR_EN_DOOR, CVAR_DAMPE_ALL_NIGHT_VALUE, OpenDampeHutDoor); +} + +static RegisterShipInitFunc initFunc(RegisterDampeAllNight, { CVAR_DAMPE_ALL_NIGHT_NAME }); diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 8bcd19903..89bef13c5 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -709,11 +709,6 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; } break; - case VB_DAMPE_IN_GRAVEYARD_DESPAWN: - if (CVarGetInteger(CVAR_ENHANCEMENT("DampeAllNight"), 0)) { - *should = LINK_IS_ADULT || gPlayState->sceneNum != SCENE_GRAVEYARD; - } - break; case VB_BE_VALID_GRAVEDIGGING_SPOT: if (CVarGetInteger(CVAR_ENHANCEMENT("DampeWin"), IS_RANDO)) { EnTk* enTk = va_arg(args, EnTk*); From 6b830703434489669a4d05b01fae1160872ec628 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sun, 9 Nov 2025 18:27:19 -0700 Subject: [PATCH 05/10] Logging bump (#5938) * Bump LUS, call `InitLogging` with both values at `trace`. Set up proper conditional default log level in Dev Tools. * Update LUS ref. --- CMake/logging.cmake | 20 ------------------- CMakeLists.txt | 4 +++- libultraship | 2 +- soh/CMakeLists.txt | 32 ++++++++++++++++++------------ soh/soh/OTRGlobals.cpp | 20 +++++++++---------- soh/soh/SohGui/SohMenuDevTools.cpp | 11 ++++++++-- 6 files changed, 41 insertions(+), 48 deletions(-) delete mode 100644 CMake/logging.cmake diff --git a/CMake/logging.cmake b/CMake/logging.cmake deleted file mode 100644 index 268ad27b4..000000000 --- a/CMake/logging.cmake +++ /dev/null @@ -1,20 +0,0 @@ -set(SPDLOG_LEVEL_TRACE 0) -set(SPDLOG_LEVEL_DEBUG 1) -set(SPDLOG_LEVEL_INFO 2) -set(SPDLOG_LEVEL_WARN 3) -set(SPDLOG_LEVEL_ERROR 4) -set(SPDLOG_LEVEL_CRITICAL 5) -set(SPDLOG_LEVEL_OFF 6) -set(LOG_LEVELS "SPDLOG_LEVEL_TRACE;SPDLOG_LEVEL_DEBUG;SPDLOG_LEVEL_INFO;SPDLOG_LEVEL_WARN;SPDLOG_LEVEL_ERROR;SPDLOG_LEVEL_CRITICAL;SPDLOG_LEVEL_OFF") -set(LOG_LEVEL SPDLOG_LEVEL_TRACE CACHE STRING "The spdlog level that prints will be logged out. Overridden to SPDLOG_LEVEL_ERROR on Release builds.") -set_property(CACHE LOG_LEVEL PROPERTY STRINGS ${LOG_LEVELS}) -if(NOT LOG_LEVEL IN_LIST LOG_LEVELS) - message(FATAL_ERROR "LOG_LEVEL must be one of ${LOG_LEVELS}") -endif() -set(SPDLOG_ACTIVE_LEVEL ${${LOG_LEVEL}}) -set(LOG_LEVEL_GAME_PRINTS ${SPDLOG_LEVEL_OFF}) - -add_compile_definitions( - LOG_LEVEL_GAME_PRINTS=${LOG_LEVEL_GAME_PRINTS} - SPDLOG_ACTIVE_LEVEL=${SPDLOG_ACTIVE_LEVEL} -) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9608da56f..c6c6788b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,9 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment ve project(Ship VERSION 9.1.1 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) -include(CMake/logging.cmake) +set(SPDLOG_LEVEL_TRACE 0) +set(SPDLOG_LEVEL_OFF 6) +set(SPDLOG_MIN_CUTOFF SPDLOG_LEVEL_TRACE CACHE STRING "cutoff at trace") option(SUPPRESS_WARNINGS "Suppress warnings in LUS and src (decomp)" ON) if(SUPPRESS_WARNINGS) diff --git a/libultraship b/libultraship index 17a0b7939..a8bdcab36 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 17a0b7939bd05f5e617cef89457ca43774fc9a9f +Subproject commit a8bdcab363571038bb71f195f21ec3e9033a220d diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 711dd8cb2..b0630e841 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -337,13 +337,15 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") "ENABLE_DX11;" ">" "$<$:" - "NDEBUG" + "NDEBUG;" ">" "$<$:ENABLE_REMOTE_CONTROL>" "INCLUDE_GAME_PRINTF;" "F3DEX_GBI_2" "UNICODE;" "_UNICODE" + SPDLOG_ACTIVE_LEVEL=${SPDLOG_MIN_CUTOFF} + LOG_LEVEL_GAME_PRINTS=${SPDLOG_LEVEL_OFF} STORMLIB_NO_AUTO_LINK "_CRT_SECURE_NO_WARNINGS;" NOMINMAX @@ -354,7 +356,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") "NOINCLUDE_GAME_PRINTF;" "_DEBUG;" "_CRT_SECURE_NO_WARNINGS;" - "ENABLE_OPENGL" + "ENABLE_OPENGL;" ">" "$<$:" "NDEBUG;" @@ -363,7 +365,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") "F3DEX_GBI_2" "WIN32;" "UNICODE;" - "_UNICODE" + "_UNICODE;" + SPDLOG_ACTIVE_LEVEL=${SPDLOG_MIN_CUTOFF} + LOG_LEVEL_GAME_PRINTS=${SPDLOG_LEVEL_OFF} STORMLIB_NO_AUTO_LINK NOMINMAX ) @@ -371,33 +375,35 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS") target_compile_definitions(${PROJECT_NAME} PRIVATE "$<$:" - "_DEBUG" + "_DEBUG;" ">" "$<$:" - "NDEBUG" + "NDEBUG;" ">" - "F3DEX_GBI_2" - "SPDLOG_ACTIVE_LEVEL=3;" + "F3DEX_GBI_2;" "SPDLOG_NO_THREAD_ID;" "SPDLOG_NO_TLS;" "STBI_NO_THREAD_LOCALS;" + SPDLOG_ACTIVE_LEVEL=${SPDLOG_MIN_CUTOFF} + LOG_LEVEL_GAME_PRINTS=${SPDLOG_LEVEL_OFF} ) elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang") target_compile_definitions(${PROJECT_NAME} PRIVATE "$<$:" - "_DEBUG" + "_DEBUG;" ">" "$<$:" - "NDEBUG" + "NDEBUG;" ">" - "F3DEX_GBI_2" - "$<$:ENABLE_REMOTE_CONTROL>" - "SPDLOG_ACTIVE_LEVEL=0;" + "F3DEX_GBI_2;" + "$<$:ENABLE_REMOTE_CONTROL>;" "_CONSOLE;" "_CRT_SECURE_NO_WARNINGS;" "ENABLE_OPENGL;" "UNICODE;" - "_UNICODE" + "_UNICODE;" + SPDLOG_ACTIVE_LEVEL=${SPDLOG_MIN_CUTOFF} + LOG_LEVEL_GAME_PRINTS=${SPDLOG_LEVEL_OFF} ) endif() ################################################################################ diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 222c7ad8c..c6e2ac4b8 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -292,10 +292,17 @@ void OTRGlobals::Initialize() { OOT_NTSC_JP_GC, OOT_NTSC_US_GC, OOT_PAL_GC, OOT_PAL_GC_DBG1, OOT_PAL_GC_DBG2, }; - context->InitLogging(); - context->InitGfxDebugger(); +#if (_DEBUG) + auto defaultLogLevel = spdlog::level::trace; +#else + auto defaultLogLevel = spdlog::level::info; +#endif + context->InitLogging(defaultLogLevel, defaultLogLevel); context->InitConfiguration(); context->InitConsoleVariables(); + Ship::Context::GetInstance()->GetLogger()->set_pattern("[%H:%M:%S.%e] [%s:%#] [%l] %v"); + + context->InitGfxDebugger(); context->InitFileDropMgr(); // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save) @@ -320,15 +327,6 @@ void OTRGlobals::Initialize() { context->InitCrashHandler(); context->InitConsole(); -#if (_DEBUG) - int defaultLogLevel = 0; -#else - int defaultLogLevel = 2; -#endif - Ship::Context::GetInstance()->GetLogger()->set_level( - (spdlog::level::level_enum)CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), defaultLogLevel)); - Ship::Context::GetInstance()->GetLogger()->set_pattern("[%H:%M:%S.%e] [%s:%#] [%l] %v"); - auto sohInputEditorWindow = std::make_shared(CVAR_WINDOW("ControllerConfiguration"), "Configure Controller"); auto sohFast3dWindow = diff --git a/soh/soh/SohGui/SohMenuDevTools.cpp b/soh/soh/SohGui/SohMenuDevTools.cpp index f9e113b55..288d002f2 100644 --- a/soh/soh/SohGui/SohMenuDevTools.cpp +++ b/soh/soh/SohGui/SohMenuDevTools.cpp @@ -11,6 +11,12 @@ static const std::unordered_map logLevels = { { DEBUG_LOG_OFF, "Off" }, }; +#ifdef _DEBUG +DebugLogOption defaultLogLevel = DEBUG_LOG_TRACE; +#else +DebugLogOption defaultLogLevel = DEBUG_LOG_INFO; +#endif + static const std::unordered_map debugSaveFileModes = { { 0, "Off" }, { 1, "Vanilla" }, @@ -110,10 +116,11 @@ void SohMenu::AddMenuDevTools() { .Options(ComboboxOptions() .Tooltip("The log level determines which messages are printed to the console." " This does not affect the log file output") - .ComboMap(logLevels)) + .ComboMap(logLevels) + .DefaultIndex(defaultLogLevel)) .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetLogger()->set_level( - (spdlog::level::level_enum)CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), DEBUG_LOG_DEBUG)); + (spdlog::level::level_enum)CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), defaultLogLevel)); }) .PreFunc([](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; }); From 3eade8133ebfdc6f026715f7053293cf8f7dcdbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Mon, 10 Nov 2025 02:16:33 +0000 Subject: [PATCH 06/10] Fix GF entrance tracker & generation issues with cratesanity (#5934) --- soh/soh/Enhancements/randomizer/entrance.cpp | 12 +-- .../overworld/thieves_hideout.cpp | 2 +- .../randomizer_entrance_tracker.cpp | 80 +++++++++---------- .../randomizer/randomizer_entrance_tracker.h | 1 + 4 files changed, 48 insertions(+), 47 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index d96eb8045..b2ff497db 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -670,8 +670,7 @@ std::vector EntranceShuffler::AssumeEntrancePool(std::vectorGetOption(RSK_SHUFFLE_OVERWORLD_ENTRANCES) || ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL)))) { auto type = entrance->GetType(); - if (((type == EntranceType::Dungeon || type == EntranceType::ThievesHideout || - type == EntranceType::GrottoGrave) && + if (((type == EntranceType::Dungeon || type == EntranceType::GrottoGrave) && entrance->GetReverse()->GetName() != "Spirit Temple Entryway -> Desert Colossus From Spirit Entryway") || (type == EntranceType::Interior && @@ -796,10 +795,11 @@ static bool ValidateWorld(Entrance* entrancePlaced) { bool checkOtherEntranceAccess = (ctx->GetOption(RSK_SHUFFLE_OVERWORLD_ENTRANCES) || ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL) || - ctx->GetOption(RSK_SHUFFLE_OVERWORLD_SPAWNS)) && + ctx->GetOption(RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES) || ctx->GetOption(RSK_SHUFFLE_OVERWORLD_SPAWNS)) && (entrancePlaced == nullptr || ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS) || - type == EntranceType::SpecialInterior || type == EntranceType::Overworld || type == EntranceType::Spawn || - type == EntranceType::WarpSong || type == EntranceType::OwlDrop); + type == EntranceType::SpecialInterior || type == EntranceType::Overworld || + type == EntranceType::ThievesHideout || type == EntranceType::Spawn || type == EntranceType::WarpSong || + type == EntranceType::OwlDrop); // Search the world to verify that all necessary conditions are still being held // Conditions will be checked during the search and any that fail will be figured out @@ -1333,6 +1333,7 @@ int EntranceShuffler::ShuffleAllEntrances() { int totalMixedPools = (ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES) ? 1 : 0) + + (ctx->GetOption(RSK_MIX_THIEVES_HIDEOUT_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES) ? 1 : 0); if (totalMixedPools < 2) { ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS).Set(RO_GENERIC_OFF); @@ -1340,6 +1341,7 @@ int EntranceShuffler::ShuffleAllEntrances() { ctx->GetOption(RSK_MIX_BOSS_ENTRANCES).Set(RO_GENERIC_OFF); ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES).Set(RO_GENERIC_OFF); ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES).Set(RO_GENERIC_OFF); + ctx->GetOption(RSK_MIX_THIEVES_HIDEOUT_ENTRANCES).Set(RO_GENERIC_OFF); ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES).Set(RO_GENERIC_OFF); } if (ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS)) { diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/thieves_hideout.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/thieves_hideout.cpp index 77d67824f..69c99ea5c 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/thieves_hideout.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/thieves_hideout.cpp @@ -4,7 +4,7 @@ using namespace Rando; // clang-format off -// When Thieve's hideout entrances are shuffled, getting caught by guards should behave like void outs to avoid logic headaches. +// When Thieves' Hideout entrances are shuffled, getting caught by guards should behave like void outs to avoid logic headaches. void RegionTable_Init_ThievesHideout() { areaTable[RR_TH_1_TORCH_CELL] = Region("Thieves Hideout 1 Torch Cell", SCENE_THIEVES_HIDEOUT, { //Events diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp index 0fc9c0279..33b41a3db 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp @@ -3,7 +3,6 @@ #include "soh/cvar_prefixes.h" #include "soh/SohGui/SohGui.hpp" -#include #include #include #include @@ -68,7 +67,7 @@ static std::string spoilerEntranceGroupNames[] = { }; static std::string groupTypeNames[] = { - "One Way", "Overworld", "Interior", "Grotto", "Dungeon", + "One Way", "Overworld", "Interior", "Fortress", "Grotto", "Dungeon", }; // Entrance data for the tracker taken from the 3ds rando entrance tracker, and supplemented with scene/spawn info and @@ -328,32 +327,32 @@ const EntranceData entranceData[] = { { ENTR_GERUDO_TRAINING_GROUND_ENTRANCE, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Outside Training Ground", "Gerudo Training Ground Entrance", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON, "gtg", 1}, { ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET), ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET), {{ SCENE_FAIRYS_FOUNTAIN, 0x00 }}, "GF Fairy Grotto", "GF Storms Grotto Entry", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_GROTTO, ""}, { ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE, SINGLE_SCENE_INFO(SCENE_GERUDO_TRAINING_GROUND), "Gerudo Training Ground Entrance", "GF Outside Training Ground", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON, "gtg"}, - { ENTR_GERUDOS_FORTRESS_1, ENTR_THIEVES_HIDEOUT_0, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Outskirts", "TH 1 Torch Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_2, ENTR_THIEVES_HIDEOUT_1, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Near Grotto", "TH 1 Torch Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_3, ENTR_THIEVES_HIDEOUT_2, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Near Grotto", "TH Kitchen Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_4, ENTR_THIEVES_HIDEOUT_3, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Above GTG", "TH Kitchen Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_5, ENTR_THIEVES_HIDEOUT_4, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Near Grotto", "TH Steep Slope Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_6, ENTR_THIEVES_HIDEOUT_5, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Bottom of Lower Vines", "TH Steep Slope Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_7, ENTR_THIEVES_HIDEOUT_6, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Above GTG", "TH Double Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_8, ENTR_THIEVES_HIDEOUT_7, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Top of Lower Vines", "TH Double Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_9, ENTR_THIEVES_HIDEOUT_8, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Top of Lower Vines", "TH Kitchen By Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_10, ENTR_THIEVES_HIDEOUT_9, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Near GS", "TH Kitchen Opposite Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_11, ENTR_THIEVES_HIDEOUT_10, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Below Chest", "TH Break Room", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_12, ENTR_THIEVES_HIDEOUT_11, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Above Jail", "TH Break Room Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_GERUDOS_FORTRESS_13, ENTR_THIEVES_HIDEOUT_12, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Below GS", "TH Dead End Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_0, ENTR_GERUDOS_FORTRESS_1, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH 1 Torch Cell", "GF Outskirts", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_1, ENTR_GERUDOS_FORTRESS_2, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH 1 Torch Cell", "GF Near Grotto", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_2, ENTR_GERUDOS_FORTRESS_3, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Kitchen Corridor", "GF Near Grotto", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_3, ENTR_GERUDOS_FORTRESS_4, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Kitchen Corridor", "GF Above GTG", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_4, ENTR_GERUDOS_FORTRESS_5, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Steep Slope Cell", "GF Near Grotto", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_5, ENTR_GERUDOS_FORTRESS_6, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Steep Slope Cell", "GF Bottom of Lower Vines", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_6, ENTR_GERUDOS_FORTRESS_7, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Double Cell", "GF Above GTG", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_7, ENTR_GERUDOS_FORTRESS_8, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Double Cell", "GF Top of Lower Vines", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_8, ENTR_GERUDOS_FORTRESS_9, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Kitchen By Corridor", "GF Top of Lower Vines", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_9, ENTR_GERUDOS_FORTRESS_10, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Kitchen Opposite Corridor", "GF Near GS", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_10, ENTR_GERUDOS_FORTRESS_11, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Break Room", "GF Below Chest", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_11, ENTR_GERUDOS_FORTRESS_12, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Break Room Corridor", "GF Above Jail", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, - { ENTR_THIEVES_HIDEOUT_12, ENTR_GERUDOS_FORTRESS_13, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Dead End Cell", "GF Below GS", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON}, + { ENTR_GERUDOS_FORTRESS_1, ENTR_THIEVES_HIDEOUT_0, {{ SCENE_THIEVES_HIDEOUT, 2 }}, "TH 1 Torch Cell Turn", "GF Outskirts", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_2, ENTR_THIEVES_HIDEOUT_1, {{ SCENE_THIEVES_HIDEOUT, 2 }}, "TH 1 Torch Cell", "GF Near Grotto East", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_3, ENTR_THIEVES_HIDEOUT_2, {{ SCENE_THIEVES_HIDEOUT, 3 }}, "TH Kitchen Corridor Lower", "GF Near Grotto North", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_4, ENTR_THIEVES_HIDEOUT_3, {{ SCENE_THIEVES_HIDEOUT, 3 }}, "TH Kitchen Corridor Upper", "GF Above GTG", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_5, ENTR_THIEVES_HIDEOUT_4, {{ SCENE_THIEVES_HIDEOUT, 4 }}, "TH Steep Slope Cell", "GF Near Grotto", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_6, ENTR_THIEVES_HIDEOUT_5, {{ SCENE_THIEVES_HIDEOUT, 4 }}, "TH Steep Slope Cell Two Ramps", "GF Bottom of Lower Vines", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_7, ENTR_THIEVES_HIDEOUT_6, {{ SCENE_THIEVES_HIDEOUT, 5 }}, "TH Double Cell Lower", "GF Above GTG Directly", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_8, ENTR_THIEVES_HIDEOUT_7, {{ SCENE_THIEVES_HIDEOUT, 5 }}, "TH Double Cell Upper", "GF Top of Lower Vines Across", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_9, ENTR_THIEVES_HIDEOUT_8, {{ SCENE_THIEVES_HIDEOUT, 3 }}, "TH Kitchen By Corridor", "GF Top of Lower Vines Near", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_10, ENTR_THIEVES_HIDEOUT_9, {{ SCENE_THIEVES_HIDEOUT, 3 }}, "TH Kitchen Opposite Corridor", "GF Near GS", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_11, ENTR_THIEVES_HIDEOUT_10, {{ SCENE_THIEVES_HIDEOUT, 0 }}, "TH Break Room", "GF Below Chest", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_12, ENTR_THIEVES_HIDEOUT_11, {{ SCENE_THIEVES_HIDEOUT, 0 }}, "TH Break Room Corridor", "GF Above Jail", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_GERUDOS_FORTRESS_13, ENTR_THIEVES_HIDEOUT_12, {{ SCENE_THIEVES_HIDEOUT, 1 }}, "TH Dead End Cell", "GF Below GS", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_0, ENTR_GERUDOS_FORTRESS_1, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Outskirts", "TH 1 Torch Cell Turn", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_1, ENTR_GERUDOS_FORTRESS_2, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Near Grotto East", "TH 1 Torch Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_2, ENTR_GERUDOS_FORTRESS_3, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Near Grotto North", "TH Kitchen Corridor Lower", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_3, ENTR_GERUDOS_FORTRESS_4, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Above GTG", "TH Kitchen Corridor Upper", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_4, ENTR_GERUDOS_FORTRESS_5, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Near Grotto", "TH Steep Slope Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_5, ENTR_GERUDOS_FORTRESS_6, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Bottom of Lower Vines", "TH Steep Slope Cell Two Ramps", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_6, ENTR_GERUDOS_FORTRESS_7, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Above GTG Directly", "TH Double Cell Lower", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_7, ENTR_GERUDOS_FORTRESS_8, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Top of Lower Vines Across", "TH Double Cell Upper", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_8, ENTR_GERUDOS_FORTRESS_9, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Top of Lower Vines Near", "TH Kitchen By Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_9, ENTR_GERUDOS_FORTRESS_10, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Near GS", "TH Kitchen Opposite Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_10, ENTR_GERUDOS_FORTRESS_11, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Below Chest", "TH Break Room", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_11, ENTR_GERUDOS_FORTRESS_12, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Above Jail", "TH Break Room Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, + { ENTR_THIEVES_HIDEOUT_12, ENTR_GERUDOS_FORTRESS_13, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Below GS", "TH Dead End Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS}, // The Wasteland { ENTR_GERUDOS_FORTRESS_GATE_EXIT, ENTR_HAUNTED_WASTELAND_EAST_EXIT, SINGLE_SCENE_INFO(SCENE_HAUNTED_WASTELAND), "Haunted Wasteland East Exit", "Gerudo Fortress Gate Exit", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_OVERWORLD, "hw,gf"}, @@ -433,11 +432,13 @@ int16_t LinkIsInArea(const EntranceData* entrance) { // Otherwise check all scenes/spawns // Not all areas require a spawn position to differeniate between another area for (auto info : entrance->scenes) { - // When a spawn position is specified, check that combination - if (info.spawn != -1) { - result = Entrance_SceneAndSpawnAre(info.scene, info.spawn); - } else { // Otherwise just check the current scene + // only check current scene when spawn info missing + if (info.spawn == -1) { result = gPlayState->sceneNum == info.scene; + } else if (gPlayState->sceneNum == SCENE_THIEVES_HIDEOUT) { // group by rooms, not spawn + result = info.scene == SCENE_THIEVES_HIDEOUT && gPlayState->roomCtx.curRoom.num == info.spawn; + } else { // Otherwise just check scene & spawn + result = Entrance_SceneAndSpawnAre(info.scene, info.spawn); } // Return the scene for tracking @@ -514,7 +515,7 @@ void SortEntranceListByType(EntranceOverride* entranceList, u8 byDest) { break; } - size_t entranceIndex = byDest ? tempList[j].override : tempList[j].index; + int16_t entranceIndex = byDest ? tempList[j].override : tempList[j].index; if (entranceData[i].type == k && entranceIndex == entranceData[i].index) { entranceList[idx] = tempList[j]; @@ -838,7 +839,7 @@ void EntranceTrackerWindow::DrawElement() { // Combine destToggle and groupToggle to get a range of 0-3 uint8_t groupType = destToggle + (groupToggle * 2); - size_t groupCount = groupToggle ? ENTRANCE_TYPE_COUNT : SPOILER_ENTRANCE_GROUP_COUNT; + size_t groupCount = groupToggle ? (size_t)ENTRANCE_TYPE_COUNT : (size_t)SPOILER_ENTRANCE_GROUP_COUNT; auto groupNames = groupToggle ? groupTypeNames : spoilerEntranceGroupNames; EntranceOverride* entranceList; @@ -874,7 +875,7 @@ void EntranceTrackerWindow::DrawElement() { uint16_t startIndex = gEntranceTrackingData.GroupOffsets[groupType][i]; bool doAreaScroll = false; - size_t undiscovered = 0; + int undiscovered = 0; std::vector displayEntrances = {}; // Loop over entrances first for filtering @@ -934,10 +935,8 @@ void EntranceTrackerWindow::DrawElement() { } displayEntrances.push_back(entrance); - } else { - if (!isDiscovered) { - undiscovered++; - } + } else if (!isDiscovered) { + undiscovered++; } } @@ -990,8 +989,7 @@ void EntranceTrackerWindow::DrawElement() { ImGui::PushStyleColor(ImGuiCol_Text, color); // Use a non-breaking space to keep the arrow from wrapping to a newline by itself - auto nbsp = u8"\u00A0"; - ImGui::TextWrapped("%s%s-> %s", origSrcName, nbsp, rplcDstName); + ImGui::TextWrapped("%s\u00A0-> %s", origSrcName, rplcDstName); ImGui::PopStyleColor(); } diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.h index 2d2f7143c..31592cd08 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.h @@ -37,6 +37,7 @@ typedef enum { ENTRANCE_TYPE_ONE_WAY, ENTRANCE_TYPE_OVERWORLD, ENTRANCE_TYPE_INTERIOR, + ENTRANCE_TYPE_FORTRESS, ENTRANCE_TYPE_GROTTO, ENTRANCE_TYPE_DUNGEON, ENTRANCE_TYPE_COUNT, From 5629d033c274f6335a552e80ba90a17246ffd5cd Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 11 Nov 2025 10:03:14 -0700 Subject: [PATCH 07/10] Forgot to get CVar value with the default specified in OTRGlobals to feed into InitLogging. (#5952) --- soh/soh/OTRGlobals.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index c6e2ac4b8..213b99820 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -297,10 +297,12 @@ void OTRGlobals::Initialize() { #else auto defaultLogLevel = spdlog::level::info; #endif - context->InitLogging(defaultLogLevel, defaultLogLevel); + auto logLevel = + static_cast(CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), defaultLogLevel)); + context->InitLogging(logLevel, logLevel); + Ship::Context::GetInstance()->GetLogger()->set_pattern("[%H:%M:%S.%e] [%s:%#] [%l] %v"); context->InitConfiguration(); context->InitConsoleVariables(); - Ship::Context::GetInstance()->GetLogger()->set_pattern("[%H:%M:%S.%e] [%s:%#] [%l] %v"); context->InitGfxDebugger(); context->InitFileDropMgr(); From dadc2e521866ef79f04f296fb69c7821dab82ac8 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Thu, 13 Nov 2025 16:29:48 -0500 Subject: [PATCH 08/10] "Assignable Tunic and Boots" setting now allows shields also (#5953) --- soh/include/z64item.h | 3 + .../Enhancements/AssignableTunicsAndBoots.cpp | 85 +++++++++++++++---- .../GameInteractor_HookTable.h | 1 + .../game-interactor/GameInteractor_Hooks.cpp | 5 ++ .../game-interactor/GameInteractor_Hooks.h | 1 + soh/soh/SohGui/SohMenuEnhancements.cpp | 4 +- soh/src/code/z_inventory.c | 15 +--- .../ovl_kaleido_scope/z_kaleido_equipment.c | 13 ++- 8 files changed, 94 insertions(+), 33 deletions(-) diff --git a/soh/include/z64item.h b/soh/include/z64item.h index 214d82711..b1fa897fe 100644 --- a/soh/include/z64item.h +++ b/soh/include/z64item.h @@ -146,6 +146,9 @@ typedef enum { /* 0x1B */ SLOT_BOOTS_KOKIRI, /* 0x1C */ SLOT_BOOTS_IRON, /* 0x1D */ SLOT_BOOTS_HOVER, + /* 0x1E */ SLOT_SHIELD_DEKU, + /* 0x1F */ SLOT_SHIELD_HYLIAN, + /* 0x20 */ SLOT_SHIELD_MIRROR, /* 0xFF */ SLOT_NONE = 0xFF } InventorySlot; diff --git a/soh/soh/Enhancements/AssignableTunicsAndBoots.cpp b/soh/soh/Enhancements/AssignableTunicsAndBoots.cpp index 1ca5a7c19..535cd1400 100644 --- a/soh/soh/Enhancements/AssignableTunicsAndBoots.cpp +++ b/soh/soh/Enhancements/AssignableTunicsAndBoots.cpp @@ -14,7 +14,7 @@ extern PlayState* gPlayState; static u16 sItemButtons[] = { BTN_B, BTN_CLEFT, BTN_CDOWN, BTN_CRIGHT, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT }; -void UseTunicBoots(Player* player, PlayState* play, Input* input) { +static void UseTunicBoots(Player* player, PlayState* play, Input* input) { // Boots and tunics equip despite state if (player->stateFlags1 & (PLAYER_STATE1_INPUT_DISABLED | PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE | PLAYER_STATE1_TALKING | PLAYER_STATE1_DEAD) || @@ -30,7 +30,7 @@ void UseTunicBoots(Player* player, PlayState* play, Input* input) { } } - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { if (item >= ITEM_BOOTS_KOKIRI) { u16 bootsValue = item - ITEM_BOOTS_KOKIRI + 1; if (CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) == bootsValue) { @@ -41,7 +41,7 @@ void UseTunicBoots(Player* player, PlayState* play, Input* input) { Player_SetEquipmentData(play, player); func_808328EC(player, CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) == EQUIP_VALUE_BOOTS_IRON ? NA_SE_PL_WALK_HEAVYBOOTS : NA_SE_PL_CHANGE_ARMS); - } else { + } else if (item >= ITEM_TUNIC_KOKIRI) { u16 tunicValue = item - ITEM_TUNIC_KOKIRI + 1; if (CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC) == tunicValue) { Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_KOKIRI); @@ -50,45 +50,93 @@ void UseTunicBoots(Player* player, PlayState* play, Input* input) { } Player_SetEquipmentData(play, player); func_808328EC(player, NA_SE_PL_CHANGE_ARMS); + } else { + u16 shieldValue = item - ITEM_SHIELD_DEKU + 1; + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) != shieldValue) { + Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, shieldValue); + Player_SetEquipmentData(play, player); + func_808328EC(player, NA_SE_PL_CHANGE_ARMS); + } } } } -void ClearAssignedTunicsBoots(int32_t unused = 0) { +static void ClearAssignedTunicsBoots(int32_t unused = 0) { for (int32_t buttonIndex = 0; buttonIndex < 8; buttonIndex++) { int32_t item = gSaveContext.equips.buttonItems[buttonIndex]; - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { gSaveContext.equips.buttonItems[buttonIndex] = ITEM_NONE; } } } +static void ClearDeletedAssignedEquipment(int16_t equipmentType, uint16_t equipValue) { + ItemID itemToRemove = ITEM_NONE; + + if (equipmentType == EQUIP_TYPE_TUNIC) { + switch (equipValue) { + case EQUIP_VALUE_TUNIC_KOKIRI: + break; + case EQUIP_VALUE_TUNIC_GORON: + itemToRemove = ITEM_TUNIC_GORON; + break; + case EQUIP_VALUE_TUNIC_ZORA: + itemToRemove = ITEM_TUNIC_ZORA; + break; + } + } else if (equipmentType == EQUIP_TYPE_SHIELD) { + switch (equipValue) { + case EQUIP_VALUE_SHIELD_DEKU: + itemToRemove = ITEM_SHIELD_DEKU; + break; + case EQUIP_VALUE_SHIELD_HYLIAN: + itemToRemove = ITEM_SHIELD_HYLIAN; + break; + case EQUIP_VALUE_SHIELD_MIRROR: + itemToRemove = ITEM_SHIELD_MIRROR; + break; + } + } + + if (itemToRemove == ITEM_NONE) { + return; + } + + for (int i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) { + if (gSaveContext.equips.buttonItems[i] == itemToRemove) { + gSaveContext.equips.buttonItems[i] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[i - 1] = SLOT_NONE; + } + } +} + #define CVAR_TUNICBOOTS_NAME CVAR_ENHANCEMENT("AssignableTunicsAndBoots") #define CVAR_TUNICBOOTS_DEFAULT 0 #define CVAR_TUNICBOOTS_VALUE CVarGetInteger(CVAR_TUNICBOOTS_NAME, CVAR_TUNICBOOTS_DEFAULT) +#define CVAR_TUNICBOOTS_SET (CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT) -void RegisterAssignableTunicsBoots() { - // make sure we don't change our held/equipped item when changing tunics/boots - COND_VB_SHOULD(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, { +static void RegisterAssignableTunicsBoots() { + // make sure we don't change our held/equipped item when changing shield/tunic/boots + COND_VB_SHOULD(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, CVAR_TUNICBOOTS_SET, { int32_t item = va_arg(args, int32_t); - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { *should = false; } }); // make sure we don't crash because tunics/boots don't have assoicated item actions - COND_VB_SHOULD(VB_ITEM_ACTION_BE_NONE, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, { + COND_VB_SHOULD(VB_ITEM_ACTION_BE_NONE, CVAR_TUNICBOOTS_SET, { int32_t item = va_arg(args, int32_t); - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { *should = true; } }); - // don't throw items when the pressed button is a tunic or boots - COND_VB_SHOULD(VB_THROW_OR_PUT_DOWN_HELD_ITEM, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, { + // don't throw items when the pressed button is a shield, tunic or boots + COND_VB_SHOULD(VB_THROW_OR_PUT_DOWN_HELD_ITEM, CVAR_TUNICBOOTS_SET, { // if the vanilla condition doesn't want us to throw/put down the item, early return if (!*should) { return; @@ -104,13 +152,13 @@ void RegisterAssignableTunicsBoots() { } } - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { *should = false; } }); // do something when the player presses a button to use the tunics/boots - COND_VB_SHOULD(VB_EXECUTE_PLAYER_ACTION_FUNC, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, { + COND_VB_SHOULD(VB_EXECUTE_PLAYER_ACTION_FUNC, CVAR_TUNICBOOTS_SET, { // if the vanilla condition doesn't want us to run the actionFunc, don't do any of this if (!*should) { return; @@ -133,13 +181,16 @@ void RegisterAssignableTunicsBoots() { UseTunicBoots(player, gPlayState, input); }); + // remove assigned equipment when it gets deleted + COND_HOOK(OnEquipmentDelete, CVAR_TUNICBOOTS_SET, ClearDeletedAssignedEquipment); + // clear out assigned tunics/boots when the enhancement is toggled off - if (GameInteractor::IsSaveLoaded(true) && CVAR_TUNICBOOTS_VALUE == CVAR_TUNICBOOTS_DEFAULT) { + if (GameInteractor::IsSaveLoaded(true) && !CVAR_TUNICBOOTS_SET) { ClearAssignedTunicsBoots(); } // clear out assigned tunics/boots when loading a save with enhancement turned off - COND_HOOK(OnLoadGame, CVAR_TUNICBOOTS_VALUE == CVAR_TUNICBOOTS_DEFAULT, ClearAssignedTunicsBoots); + COND_HOOK(OnLoadGame, !CVAR_TUNICBOOTS_SET, ClearAssignedTunicsBoots); } static RegisterShipInitFunc initFunc(RegisterAssignableTunicsBoots, { CVAR_TUNICBOOTS_NAME }); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 0f694660c..8ca003329 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -12,6 +12,7 @@ DEFINE_HOOK(OnExitGame, (int32_t fileNum)); DEFINE_HOOK(OnGameStateMainStart, ()); DEFINE_HOOK(OnGameFrameUpdate, ()); DEFINE_HOOK(OnItemReceive, (GetItemEntry itemEntry)); +DEFINE_HOOK(OnEquipmentDelete, (int16_t equipmentType, uint16_t equipValue)); DEFINE_HOOK(OnSaleEnd, (GetItemEntry itemEntry)); DEFINE_HOOK(OnTransitionEnd, (int16_t sceneNum)); DEFINE_HOOK(OnSceneInit, (int16_t sceneNum)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 7d5a243d2..ed2b288c1 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -34,6 +34,11 @@ void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry) { GameInteractor::Instance->ExecuteHooksForFilter(itemEntry); } +void GameInteractor_ExecuteOnEquipmentDelete(int16_t equipmentType, uint16_t equipValue) { + GameInteractor::Instance->ExecuteHooks(equipmentType, equipValue); + GameInteractor::Instance->ExecuteHooksForFilter(equipmentType, equipValue); +} + void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry) { GameInteractor::Instance->ExecuteHooks(itemEntry); GameInteractor::Instance->ExecuteHooksForFilter(itemEntry); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 4c3a600e7..b114ac84a 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -15,6 +15,7 @@ void GameInteractor_ExecuteOnExitGame(int32_t fileNum); void GameInteractor_ExecuteOnGameStateMainStart(); void GameInteractor_ExecuteOnGameFrameUpdate(); void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry); +void GameInteractor_ExecuteOnEquipmentDelete(int16_t equipmentType, uint16_t equipValue); void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum); void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index e81478df9..4df596405 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -732,9 +732,9 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip( "Equip items and equipment on the D-pad. If used with \"D-pad on Pause Screen\", you must " "hold C-Up to equip instead of navigate.")); - AddWidget(path, "Assignable Tunics and Boots", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Assignable Shields, Tunics and Boots", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("AssignableTunicsAndBoots")) - .Options(CheckboxOptions().Tooltip("Allows equipping the Tunics and Boots to C-Buttons/D-pad.")); + .Options(CheckboxOptions().Tooltip("Allows equipping Shields, Tunics and Boots to C-Buttons/D-pad.")); // TODO: Revist strength toggle, it's currently separate but should probably be locked behind the // Equipment toggle settings or be absorbed by it completely. AddWidget(path, "Equipment Toggle", WIDGET_CVAR_CHECKBOX) diff --git a/soh/src/code/z_inventory.c b/soh/src/code/z_inventory.c index 8a51c6263..f2b329ed5 100644 --- a/soh/src/code/z_inventory.c +++ b/soh/src/code/z_inventory.c @@ -1,4 +1,5 @@ #include "global.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "textures/icon_item_static/icon_item_static.h" #include "textures/icon_item_24_static/icon_item_24_static.h" #include "textures/parameter_static/parameter_static.h" @@ -204,20 +205,10 @@ u8 Inventory_DeleteEquipment(PlayState* play, s16 equipment) { if (equipment == EQUIP_TYPE_TUNIC) { gSaveContext.equips.equipment |= EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4); - // non-vanilla: remove goron and zora tunics from item buttons if assignable tunics is on - if (CVarGetInteger(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) && - equipValue != EQUIP_VALUE_TUNIC_KOKIRI) { - ItemID item = (equipValue == EQUIP_VALUE_TUNIC_GORON ? ITEM_TUNIC_GORON : ITEM_TUNIC_ZORA); - for (int i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) { - if (gSaveContext.equips.buttonItems[i] == item) { - gSaveContext.equips.buttonItems[i] = ITEM_NONE; - gSaveContext.equips.cButtonSlots[i - 1] = SLOT_NONE; - } - } - } - // end non-vanilla } + GameInteractor_ExecuteOnEquipmentDelete(equipment, equipValue); + if (equipment == EQUIP_TYPE_SWORD) { gSaveContext.equips.buttonItems[0] = ITEM_NONE; gSaveContext.infTable[29] = 1; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c index cec05af72..59dfffb1f 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -642,8 +642,8 @@ void KaleidoScope_DrawEquipment(PlayState* play) { pauseCtx->unk_1E4 = 7; sEquipTimer = 10; } else if (CVarGetInteger(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) != 0) { - // Only allow assigning tunic and boots to c-buttons - if (pauseCtx->cursorY[PAUSE_EQUIP] > 1) { + // Only allow assigning shield, tunic and boots to c-buttons + if (pauseCtx->cursorY[PAUSE_EQUIP] > 0) { if (CHECK_OWNED_EQUIP(pauseCtx->cursorY[PAUSE_EQUIP], pauseCtx->cursorX[PAUSE_EQUIP] - 1)) { u16 slot = 0; switch (cursorItem) { @@ -665,6 +665,15 @@ void KaleidoScope_DrawEquipment(PlayState* play) { case ITEM_BOOTS_HOVER: slot = SLOT_BOOTS_HOVER; break; + case ITEM_SHIELD_DEKU: + slot = SLOT_SHIELD_DEKU; + break; + case ITEM_SHIELD_HYLIAN: + slot = SLOT_SHIELD_HYLIAN; + break; + case ITEM_SHIELD_MIRROR: + slot = SLOT_SHIELD_MIRROR; + break; default: break; } From 14fb9e60d8a1065ecb50d30308ff04850aa90ec6 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Thu, 13 Nov 2025 17:31:57 -0500 Subject: [PATCH 09/10] Autosave no longer works after save file is deleted (#5959) --- soh/soh/Enhancements/QoL/Autosave.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/QoL/Autosave.cpp b/soh/soh/Enhancements/QoL/Autosave.cpp index 01f85e0cd..49ab2996d 100644 --- a/soh/soh/Enhancements/QoL/Autosave.cpp +++ b/soh/soh/Enhancements/QoL/Autosave.cpp @@ -26,11 +26,13 @@ typedef enum { static bool Autosave_CanSave() { // Don't save when in title screen or debug file + // Don't save a file that doesn't exist (e.g. it was deleted on death by user option) // Don't save the first 60 frames to not save the magic meter when it's still in the animation of filling it. // Don't save in Chamber of Sages and the Cutscene map because of remember save location and cutscene item gives. // Don't save between obtaining Ocarina of Time and Song of Time because the latter would become unobtainable. - if (!GameInteractor::IsSaveLoaded(false) || gPlayState->gameplayFrames < 60 || - gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES || gPlayState->sceneNum == SCENE_CUTSCENE_MAP || + if (!SaveManager::Instance->SaveFile_Exist(gSaveContext.fileNum) || !GameInteractor::IsSaveLoaded(false) || + gPlayState->gameplayFrames < 60 || gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES || + gPlayState->sceneNum == SCENE_CUTSCENE_MAP || (!CHECK_QUEST_ITEM(QUEST_SONG_TIME) && (INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME))) { return false; } From 6ccdfc6051274fbe160f5bc08e1ab1b0359ada3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Fri, 14 Nov 2025 02:24:40 +0000 Subject: [PATCH 10/10] Fix: ground jump for haunted wasteland GS is hard (#5843) * Fix: ground jump for haunted wasteland GS is hard, & can't be done with hammer Pulled this logic from zootr where sword is assumed, was not able to execute myself * Pepper0ni took a stab at this, wasn't able to do it, but didn't feel it wasn't doable * AGreenSpoon figured it out with BGS/hammer --- .../randomizer/location_access/overworld/haunted_wasteland.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/haunted_wasteland.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/haunted_wasteland.cpp index b1cefc144..20ae61555 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/haunted_wasteland.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/haunted_wasteland.cpp @@ -24,7 +24,7 @@ void RegionTable_Init_HauntedWasteland() { //Locations LOCATION(RC_WASTELAND_CHEST, logic->HasFireSource()), LOCATION(RC_WASTELAND_BOMBCHU_SALESMAN, logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)), - LOCATION(RC_WASTELAND_GS, logic->HookshotOrBoomerang() || (logic->IsAdult && logic->CanGroundJump() && logic->CanJumpslash())), + LOCATION(RC_WASTELAND_GS, logic->HookshotOrBoomerang() || (logic->IsAdult && ctx->GetTrickOption(RT_GROUND_JUMP_HARD) && logic->CanGroundJump() && logic->CanJumpslash())), // need to jumpslash immediately with two handed weapons LOCATION(RC_WASTELAND_NEAR_GS_POT_1, logic->CanBreakPots()), LOCATION(RC_WASTELAND_NEAR_GS_POT_2, logic->CanBreakPots()), LOCATION(RC_WASTELAND_NEAR_GS_POT_3, logic->CanBreakPots()),