From dfa10383e400d06f1100082c1ca786a743f6ac79 Mon Sep 17 00:00:00 2001 From: OtherBlue <93625085+OtherBlue@users.noreply.github.com> Date: Thu, 3 Jul 2025 01:35:49 -0300 Subject: [PATCH 01/23] Split Skip Pickup Messages (#5648) * Split Skip Pickup Messages following ZFG's comment https://youtube.com/clip/UgkxF-LDaR-zyTkqSkqtP3dkLrCca_KGScIw?si=RJY9SIG8QKSiWgVl splits the Skip Pickup Messages time saver for consumables and bottle pickups * CVar updates I missed * another cvar I missed --- soh/soh/SohGui/SohMenuEnhancements.cpp | 7 +++++-- soh/src/overlays/actors/ovl_player_actor/z_player.c | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 8e1e94c08..c08e9c7ba 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -353,9 +353,12 @@ void SohMenu::AddMenuEnhancements() { "Door Switch CS, Water Temple Dragon Switch CS, and the Box Skip One Point in Jabu.")); AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT); - AddWidget(path, "Skip Pickup Messages", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Skip Bottle Pickup Messages", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FastBottles")) + .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for Bottle Swipes.")); + AddWidget(path, "Skip Consumable Item Pickup Messages", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FastDrops")) - .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for new Consumable Items and Bottle Swipes.")); + .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for new Consumable Items.")); AddWidget(path, "Skip Forced Dialog", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipForcedDialog")) .Options(ComboboxOptions() diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index b2ebcbb88..777de3e59 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -14697,7 +14697,7 @@ void Player_Action_SwingBottle(Player* this, PlayState* play) { if (LinkAnimation_Update(play, &this->skelAnime)) { if (this->av1.bottleCatchType != BOTTLE_CATCH_NONE) { if (!this->av2.startedTextbox) { - if (CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) { + if (CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { this->av1.bottleCatchType = BOTTLE_CATCH_NONE; } else { // 1 is subtracted because `sBottleCatchInfo` does not have an entry for `BOTTLE_CATCH_NONE` @@ -14740,13 +14740,13 @@ void Player_Action_SwingBottle(Player* this, PlayState* play) { this->av1.bottleCatchType = i + 1; this->av2.startedTextbox = false; - if (!CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { this->stateFlags1 |= PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE; } this->interactRangeActor->parent = &this->actor; Player_UpdateBottleHeld(play, this, catchInfo->itemId, ABS(catchInfo->itemAction)); - if (!CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { Player_AnimPlayOnceAdjusted(play, this, swingEntry->catchAnimation); func_80835EA4(play, 4); } From a93b484cf38d02f49932776cefda5292960c6019 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 10:44:53 -0700 Subject: [PATCH 02/23] Add Clear Devices button that will erase the controllers block. (#5683) --- soh/soh/SohGui/SohGui.cpp | 6 ++---- soh/soh/SohGui/SohMenuSettings.cpp | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/soh/soh/SohGui/SohGui.cpp b/soh/soh/SohGui/SohGui.cpp index 7eb3fc6c7..67fb99f0a 100644 --- a/soh/soh/SohGui/SohGui.cpp +++ b/soh/soh/SohGui/SohGui.cpp @@ -71,7 +71,6 @@ std::shared_ptr mSohMenuBar; std::shared_ptr mConsoleWindow; std::shared_ptr mStatsWindow; std::shared_ptr mGfxDebuggerWindow; -std::shared_ptr mInputEditorWindow; std::shared_ptr mSohMenu; std::shared_ptr mAudioEditorWindow; @@ -131,10 +130,10 @@ void SetupGuiElements() { mStatsWindow = std::make_shared(CVAR_WINDOW("SohStats"), "Stats##Soh", ImVec2(400, 100)); gui->AddGuiWindow(mStatsWindow); - mInputEditorWindow = gui->GetGuiWindow("Controller Configuration"); + /*mInputEditorWindow = gui->GetGuiWindow("Controller Configuration"); if (mInputEditorWindow == nullptr) { SPDLOG_ERROR("Could not find input editor window"); - } + }*/ mAudioEditorWindow = std::make_shared(CVAR_WINDOW("AudioEditor"), "Audio Editor", ImVec2(820, 630)); gui->AddGuiWindow(mAudioEditorWindow); @@ -228,7 +227,6 @@ void Destroy() { mActorViewerWindow = nullptr; mCosmeticsEditorWindow = nullptr; mAudioEditorWindow = nullptr; - mInputEditorWindow = nullptr; mStatsWindow = nullptr; mConsoleWindow = nullptr; mGfxDebuggerWindow = nullptr; diff --git a/soh/soh/SohGui/SohMenuSettings.cpp b/soh/soh/SohGui/SohMenuSettings.cpp index 909c647bd..6fb9e466c 100644 --- a/soh/soh/SohGui/SohMenuSettings.cpp +++ b/soh/soh/SohGui/SohMenuSettings.cpp @@ -1,5 +1,7 @@ #include "SohMenu.h" #include "soh/Notification/Notification.h" +#include "soh/Enhancements/controls/SohInputEditorWindow.h" +#include "SohModals.h" #include #include "soh/ResourceManagerHelpers.h" #include "UIWidgets.hpp" @@ -13,6 +15,7 @@ extern "C" { namespace SohGui { extern std::shared_ptr mSohMenu; +extern std::shared_ptr mModalWindow; using namespace UIWidgets; static std::unordered_map imguiScaleOptions = { @@ -373,6 +376,20 @@ void SohMenu::AddMenuSettings() { path.sidebarName = "Controls"; path.column = SECTION_COLUMN_1; AddSidebarEntry("Settings", "Controls", 2); + AddWidget(path, "Clear Devices", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { + SohGui::mModalWindow->RegisterPopup( + "Clear Config", + "This will completely erase the controls config, including registered devices.\nContinue?", "Clear", + "Cancel", + []() { + Ship::Context::GetInstance()->GetConsoleVariables()->ClearBlock(CVAR_PREFIX_SETTING ".Controllers"); + uint8_t bits = 0; + Ship::Context::GetInstance()->GetControlDeck()->Init(&bits); + }, + nullptr); + }) + .Options(ButtonOptions().Size(Sizes::Inline)); AddWidget(path, "Controller Bindings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Bindings Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("ControllerConfiguration")) From 681e8dda2636f47b6bf6b55d03b3cd723948d7e7 Mon Sep 17 00:00:00 2001 From: Shishu the Dragon <183069616+ShishuTheDragon@users.noreply.github.com> Date: Tue, 22 Jul 2025 05:47:03 +1200 Subject: [PATCH 03/23] Ivan: collect deku seeds (#5654) --- soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c index ab8ca1a6f..ab379267e 100644 --- a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c +++ b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c @@ -654,7 +654,7 @@ void EnPartner_Update(Actor* thisx, PlayState* play) { itemActor->params == ITEM00_ARROWS_MEDIUM || itemActor->params == ITEM00_ARROWS_LARGE || itemActor->params == ITEM00_BOMBCHU || itemActor->params == ITEM00_MAGIC_SMALL || itemActor->params == ITEM00_MAGIC_LARGE || itemActor->params == ITEM00_NUTS || - itemActor->params == ITEM00_STICK) { + itemActor->params == ITEM00_STICK || itemActor->params == ITEM00_SEEDS) { f32 distanceToObject = Actor_WorldDistXYZToActor(&this->actor, itemActor); if (distanceToObject <= 20.0f) { itemActor->world.pos = GET_PLAYER(play)->actor.world.pos; From 37fb25d2b337cc65e0cc0174f1daf40065efdf7a Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 21 Jul 2025 19:00:39 +0100 Subject: [PATCH 04/23] Fix logic for items given at run start due to settings (#5665) * initial changes * submodules * fix master sword, skip zelda and skip epona logic * I for one welcome our new clang overlords... --- soh/soh/Enhancements/randomizer/3drando/hints.cpp | 3 +++ soh/soh/Enhancements/randomizer/location_access.cpp | 3 +++ .../randomizer/location_access/overworld/lon_lon_ranch.cpp | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index c38d57d8b..bf71ef023 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -648,6 +648,9 @@ void CreateStoneHints() { if (ctx->GetOption(RSK_SKIP_CHILD_ZELDA)) { ctx->GetItemLocation(RC_SONG_FROM_IMPA)->SetHintAccesible(); } + if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT)) { + ctx->GetItemLocation(RC_TOT_MASTER_SWORD)->SetHintAccesible(); + } // Add 'always' location hints std::vector alwaysHintLocations = {}; diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index cdd684f04..26ec51f27 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -382,11 +382,14 @@ void RegionTable_Init() { //The big poes bottle softlock safety check does not account for the guard house lock if the guard house is not shuffled, so the key is needed before we can safely allow bottle use in logic //RANDOTODO a setting that lets you drink/dump big poes so we don't need this logic EventAccess(&logic->CouldEmptyBigPoes, []{return !ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF) || logic->CanOpenOverworldDoor(RG_GUARD_HOUSE_KEY);}), + EventAccess(&logic->FreedEpona, []{return (bool)ctx->GetOption(RSK_SKIP_EPONA_RACE);}), }, { //Locations LOCATION(RC_LINKS_POCKET, true), LOCATION(RC_TRIFORCE_COMPLETED, logic->GetSaveContext()->ship.quest.data.randomizer.triforcePiecesCollected >= ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).Get() + 1;), LOCATION(RC_SARIA_SONG_HINT, logic->CanUse(RG_SARIAS_SONG)), + LOCATION(RC_SONG_FROM_IMPA, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)), + LOCATION(RC_TOT_MASTER_SWORD, (bool)ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT)), }, { //Exits Entrance(RR_ROOT_EXITS, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp index fae41664e..6fe85afc7 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp @@ -7,8 +7,8 @@ void RegionTable_Init_LonLonRanch() { // clang-format off areaTable[RR_LON_LON_RANCH] = Region("Lon Lon Ranch", "Lon Lon Ranch", {RA_LON_LON_RANCH}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->FreedEpona, []{return logic->FreedEpona || ((logic->HasItem(RG_CHILD_WALLET) || ctx->GetOption(RSK_SKIP_EPONA_RACE)) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay);}), - EventAccess(&logic->LinksCow, []{return logic->LinksCow || (logic->HasItem(RG_CHILD_WALLET) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay);}), + EventAccess(&logic->FreedEpona, []{return logic->HasItem(RG_CHILD_WALLET) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay;}), + EventAccess(&logic->LinksCow, []{return logic->HasItem(RG_CHILD_WALLET) && logic->CanUse(RG_EPONA) && logic->IsAdult && logic->AtDay;}), }, { //Locations LOCATION(RC_SONG_FROM_MALON, logic->IsChild && logic->HasItem(RG_ZELDAS_LETTER) && logic->HasItem(RG_FAIRY_OCARINA) && logic->AtDay), From 47c5a7f30841045c833bbf4712d8c85ffd6f0000 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 14:31:48 -0700 Subject: [PATCH 05/23] Bump version to Blair Delta 9.0.3, and fix a typo. (#5687) --- CMakeLists.txt | 2 +- soh/soh/SohGui/SohMenuEnhancements.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9a5afe4c..39cad6d8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.0.2 LANGUAGES C CXX) +project(Ship VERSION 9.0.3 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index c08e9c7ba..41cbbd904 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -272,7 +272,7 @@ void SohMenu::AddMenuEnhancements() { info.options->disabled = IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_JABU_OPEN).Is(RO_JABU_OPEN); info.options->disabledTooltip = - "This setting is disabled because a randomizer savefile with \"Jabu-Jaby: Open\" is loaded."; + "This setting is disabled because a randomizer savefile with \"Jabu-Jabu: Open\" is loaded."; }) .Options(CheckboxOptions().Tooltip("Allow Link to enter Jabu-Jabu without feeding him a fish.")); From 5066fbf82c541ce3bf861a52e9892f5a3719f59e Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 17:57:40 -0700 Subject: [PATCH 06/23] Fix old file deletion. (#5688) --- soh/soh/SaveManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 43bd25b23..b62dbfee6 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1163,7 +1163,6 @@ void SaveManager::LoadFile(int fileNum) { " " + newFileName + "\n" + "If this was not in error, the file should be deleted."); saveMtx.unlock(); - SaveFile(fileNum); return; } } From 35ad68578e6df5c51acd77546691c7ee98d14ac5 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 22:19:04 -0700 Subject: [PATCH 07/23] Prevent empty randomizer blocks from triggering the rando version flow. (#5689) Clear SpoilerLog CVar when unsupported spoiler log is discovered on load. --- soh/soh/Enhancements/randomizer/randomizer.cpp | 2 ++ soh/soh/SaveManager.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 581861158..ac1716fc3 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -406,6 +406,8 @@ bool Randomizer::SpoilerFileExists(const char* spoilerFileName) { "The spoiler file located at\n" + std::string(spoilerFileName) + "\nwas made by a version that doesn't match the currently running version.\n" + "Loading for this file has been cancelled."); + CVarClear(CVAR_GENERAL("SpoilerLog")); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } // Update cache diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index b62dbfee6..2e2f05f72 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1111,6 +1111,7 @@ void SaveManager::LoadFile(int fileNum) { std::ifstream input(fileName); try { + bool deleteRando = false; saveBlock = nlohmann::json::object(); input >> saveBlock; if (!saveBlock.contains("version")) { @@ -1122,6 +1123,10 @@ void SaveManager::LoadFile(int fileNum) { for (auto& block : saveBlock["sections"].items()) { std::string sectionName = block.key(); if (sectionName == "randomizer") { + if (block.value()["data"].empty()) { + deleteRando = true; + continue; + } bool hasStats = saveBlock["sections"].contains("sohStats"); if (block.value()["data"].contains("aat0") || !hasStats) { // Rachael rando data SohGui::RegisterPopup( @@ -1201,6 +1206,11 @@ void SaveManager::LoadFile(int fileNum) { assert(false); break; } + if (deleteRando) { + saveBlock["sections"].erase(saveBlock["sections"].find("randomizer")); + SaveFile(fileNum); + deleteRando = false; + } InitMeta(fileNum); GameInteractor::Instance->ExecuteHooks(fileNum); } catch (const std::exception& e) { From c588d48672b56b329f07aa181af3554ab38914cb Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 22:21:59 -0700 Subject: [PATCH 08/23] Bump version to Blair Echo. (#5690) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39cad6d8f..d7db400c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.0.3 LANGUAGES C CXX) +project(Ship VERSION 9.0.4 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) From d51e88b9722f99096033a90080039d7d44e68e91 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 23 Jul 2025 20:45:22 -0700 Subject: [PATCH 09/23] Rando Versioning (Again) (#5691) * Adds Sulu/Spock rando block check (data not empty, but all sub-entries null), and put that and data being empty to the old file flow. Also moves the `SaveFile` call to after everything else is loaded to preserve sohStats block. * Add check for blank buildVersion in sohStats block for determining old saves. * clang --- soh/soh/SaveManager.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 2e2f05f72..bdb2800e3 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1121,25 +1121,24 @@ void SaveManager::LoadFile(int fileNum) { switch (saveBlock["version"].get()) { case 1: for (auto& block : saveBlock["sections"].items()) { + bool oldVanilla = + block.value()["data"].empty() || block.value()["data"].contains("aat0") || + block.value()["data"]["entrances"].empty() || + SohUtils::IsStringEmpty(saveBlock["sections"]["sohStats"]["data"]["buildVersion"]); std::string sectionName = block.key(); if (sectionName == "randomizer") { - if (block.value()["data"].empty()) { - deleteRando = true; - continue; - } bool hasStats = saveBlock["sections"].contains("sohStats"); - if (block.value()["data"].contains("aat0") || !hasStats) { // Rachael rando data + if (oldVanilla || !hasStats) { // Vanilla "rando" data SohGui::RegisterPopup( "Loading old file", "The file in slot " + std::to_string(fileNum + 1) + - " appears to contain randomizer data, but is a very old format.\n" + + " appears to contain randomizer data, but is a very old format or is empty.\n" + "The randomizer data has been removed, and this file will be treated as a vanilla " - "file.\n" + + "file.\nIf this was a vanilla file, it still is, and you shouldn't see this " + "message again.\n" + "If this was a randomizer file, the file will not work, and should be deleted."); - input.close(); - saveMtx.unlock(); - SaveFile(fileNum); - return; + deleteRando = true; + continue; } s16 major = saveBlock["sections"]["sohStats"]["data"]["buildVersionMajor"]; s16 minor = saveBlock["sections"]["sohStats"]["data"]["buildVersionMinor"]; @@ -1206,6 +1205,7 @@ void SaveManager::LoadFile(int fileNum) { assert(false); break; } + input.close(); if (deleteRando) { saveBlock["sections"].erase(saveBlock["sections"].find("randomizer")); SaveFile(fileNum); From d06cf6bf1032037d67b46bad37b8320bec9643d6 Mon Sep 17 00:00:00 2001 From: AltoXorg <56553686+Alto1772@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:45:46 +0800 Subject: [PATCH 10/23] `timesplitdata.json` obey app directory (#5693) --- soh/soh/Enhancements/timesplits/TimeSplits.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/timesplits/TimeSplits.cpp b/soh/soh/Enhancements/timesplits/TimeSplits.cpp index f08235970..d590bbd07 100644 --- a/soh/soh/Enhancements/timesplits/TimeSplits.cpp +++ b/soh/soh/Enhancements/timesplits/TimeSplits.cpp @@ -4,6 +4,7 @@ #include "soh/util.h" #include #include "include/z64item.h" +#include "Context.h" #include #include @@ -363,7 +364,7 @@ void TimeSplitsSkipSplit(uint32_t index) { } void TimeSplitsFileManagement(uint32_t action, const char* listEntry, std::vector listData) { - std::string filename = "timesplitdata.json"; + std::string filename = Ship::Context::GetPathRelativeToAppDirectory("timesplitdata.json"); json saveFile; json listArray = nlohmann::json::array(); @@ -948,9 +949,10 @@ void TimeSplitsDrawManageList() { } void InitializeSplitDataFile() { - if (!std::filesystem::exists("timesplitdata.json")) { + std::string filename = Ship::Context::GetPathRelativeToAppDirectory("timesplitdata.json"); + if (!std::filesystem::exists(filename)) { json j; - std::ofstream file("timesplitdata.json"); + std::ofstream file(filename); file << j.dump(4); file.close(); } From ccf3d4b6a0e6a4e2276da6336fea86b30c32428a Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Wed, 23 Jul 2025 23:48:01 -0400 Subject: [PATCH 11/23] Add playing icon to improve Audio Editor indicator (#5686) --- soh/soh/Enhancements/audio/AudioEditor.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index 4cf1f389d..6660bee7d 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -298,9 +298,12 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::TextColored( - UIWidgets::ColorValues.at(isCurrentlyPlaying ? UIWidgets::Colors::Yellow : UIWidgets::Colors::White), "%s", - seqData.label.c_str()); + if (isCurrentlyPlaying) { + ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::Yellow), "%s %s", ICON_FA_PLAY, + seqData.label.c_str()); + } else { + ImGui::Text("%s", seqData.label.c_str()); + } ImGui::TableNextColumn(); ImGui::PushItemWidth(-FLT_MIN); const int initialValue = map.contains(currentValue) ? currentValue : defaultValue; From 7d7072f717c853678c4aed7cce39aa1eba592994 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 23 Jul 2025 21:31:13 -0700 Subject: [PATCH 12/23] Bump version to 9.0.5 (#5694) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7db400c5..89f32f74e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.0.4 LANGUAGES C CXX) +project(Ship VERSION 9.0.5 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) From 1d20000411c120d81144c736f81e5552498a3e77 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 28 Jul 2025 08:54:31 -0700 Subject: [PATCH 13/23] MPQ Support LUS Bump (#5570) * Adapt to changes to LUS regarding including MPQ support. * CMake compile definitions are stupid. * Don't manually close O2RArchive. * Finish LUS bump, including SoH-side fix for font free crash. --- CMakeLists.txt | 4 ++++ libultraship | 2 +- soh/soh/OTRGlobals.cpp | 5 +++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3324b7c00..ef00ae8f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,6 +155,9 @@ set(GFX_DEBUG_DISASSEMBLER ON) set(GBI_UCODE F3DEX_GBI_2) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") +# Enable MPQ and OTR support +set(INCLUDE_MPQ_SUPPORT ON) + ################################################################################ # Set CONTROLLERBUTTONS_T ################################################################################ @@ -165,6 +168,7 @@ add_compile_definitions(CONTROLLERBUTTONS_T=uint32_t) ################################################################################ add_subdirectory(libultraship ${CMAKE_BINARY_DIR}/libultraship) target_compile_options(libultraship PRIVATE "${WARNING_OVERRIDE}") +target_compile_definitions(libultraship PUBLIC INCLUDE_MPQ_SUPPORT) add_subdirectory(ZAPDTR/ZAPD ${CMAKE_BINARY_DIR}/ZAPD) add_subdirectory(OTRExporter) add_subdirectory(soh) diff --git a/libultraship b/libultraship index 6a3f6cd32..7f737f8be 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 6a3f6cd327b99f617b623e5b9a3afeae460aac2b +Subproject commit 7f737f8be9580980f5a1fe7784d6e1045f0309da diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index bc9b3106e..d59871d54 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -984,7 +984,6 @@ OTRVersion ReadPortVersionFromOTR(std::string otrPath) { version.minor = reader->ReadUInt16(); version.patch = reader->ReadUInt16(); } - archive->Close(); } return version; @@ -1679,7 +1678,9 @@ ImFont* OTRGlobals::CreateFontWithSize(float size, std::string fontPath) { initData->Path = fontPath; std::shared_ptr fontData = std::static_pointer_cast( Ship::Context::GetInstance()->GetResourceManager()->LoadResource(fontPath, false, initData)); - font = mImGuiIo->Fonts->AddFontFromMemoryTTF(fontData->Data, fontData->DataSize, size); + ImFontConfig fontConf; + fontConf.FontDataOwnedByAtlas = false; + font = mImGuiIo->Fonts->AddFontFromMemoryTTF(fontData->Data, fontData->DataSize, size, &fontConf); } // FontAwesome fonts need to have their sizes reduced by 2.0f/3.0f in order to align correctly float iconFontSize = size * 2.0f / 3.0f; From 429021d43471ce89b69c24028a8a3ea19eb73828 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 5 Aug 2025 18:28:19 -0700 Subject: [PATCH 14/23] Add `THRescuedAllCarpenters` to reset list to prevent seed bleed of bridge. (#5725) --- soh/soh/Enhancements/randomizer/logic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index eea79e92d..3c0c8345e 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2471,6 +2471,7 @@ void Logic::Reset(bool resetSaveContext /*= true*/) { THCouldFreeDoubleCellCarpenter = false; TH_CouldFreeDeadEndCarpenter = false; THCouldRescueSlopeCarpenter = false; + THRescuedAllCarpenters = false; GF_GateOpen = false; GtG_GateOpen = false; DampesWindmillAccess = false; From 6d4c6f8ea649d1e4857a275994e854c8a983dab8 Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Wed, 6 Aug 2025 05:43:41 +0200 Subject: [PATCH 15/23] Rando: Remove ice trap effect from vanilla ice trap chests (#5711) --- .../game-interactor/vanilla-behavior/GIVanillaBehavior.h | 9 +++++++++ soh/soh/Enhancements/randomizer/hook_handlers.cpp | 1 + soh/src/overlays/actors/ovl_En_Box/z_en_box.c | 6 ++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 186307aed..0ba910cf6 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2222,6 +2222,15 @@ typedef enum { // - `s32` limbCount // - `*Vec3s` frameTable VB_LOAD_PLAYER_ANIMATION_FRAME, + + // #### `result` + // ```c + // (this->dyna.actor.params >> 5 & 0x7F) == GI_ICE_TRAP && this->actionFunc == EnBox_Open && + // this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100 + // ``` + // #### `args` + // - `*EnBox` + VB_CHEST_USE_ICE_EFFECT, } GIVanillaBehavior; #endif diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index d73cebca7..447125ae1 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1713,6 +1713,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l case VB_GIVE_ITEM_WATER_MEDALLION: case VB_GIVE_ITEM_SPIRIT_MEDALLION: case VB_GIVE_ITEM_SHADOW_MEDALLION: + case VB_CHEST_USE_ICE_EFFECT: *should = false; break; case VB_GIVE_ITEM_SKULL_TOKEN: diff --git a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c index 9a3a8fb77..f52b4a458 100644 --- a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c +++ b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c @@ -567,8 +567,10 @@ void EnBox_Update(Actor* thisx, PlayState* play) { Actor_SetFocus(&this->dyna.actor, 40.0f); } - if ((this->dyna.actor.params >> 5 & 0x7F) == GI_ICE_TRAP && this->actionFunc == EnBox_Open && - this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100) { + if (GameInteractor_Should(VB_CHEST_USE_ICE_EFFECT, + (this->dyna.actor.params >> 5 & 0x7F) == GI_ICE_TRAP && this->actionFunc == EnBox_Open && + this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100, + this)) { EnBox_SpawnIceSmoke(this, play); } } From 3e39a940833eb0d984fe4bb361a8856e8a2d1c1e Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Wed, 13 Aug 2025 04:56:52 +0200 Subject: [PATCH 16/23] Turn song text into a notification (#5712) --- soh/soh/Enhancements/audio/AudioCollection.cpp | 9 +++++++++ soh/soh/Enhancements/audio/AudioCollection.h | 1 + soh/soh/Enhancements/mods.cpp | 1 - soh/soh/OTRGlobals.cpp | 9 --------- soh/soh/OTRGlobals.h | 2 -- soh/src/code/audio_load.c | 6 ++---- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/soh/soh/Enhancements/audio/AudioCollection.cpp b/soh/soh/Enhancements/audio/AudioCollection.cpp index d3f69dad8..a30d3f086 100644 --- a/soh/soh/Enhancements/audio/AudioCollection.cpp +++ b/soh/soh/Enhancements/audio/AudioCollection.cpp @@ -2,6 +2,7 @@ #include "sequence.h" #include "sfx.h" #include "soh/cvar_prefixes.h" +#include "soh/Notification/Notification.h" #include #include #include @@ -458,3 +459,11 @@ extern "C" bool AudioCollection_HasSequenceNum(uint16_t seqId) { extern "C" size_t AudioCollection_SequenceMapSize() { return AudioCollection::Instance->SequenceMapSize(); } + +extern "C" void AudioCollection_EmitSongNameNotification(s32 seqId) { + const char* sequenceName = AudioCollection_GetSequenceName(seqId); + if (sequenceName != NULL) { + Notification::Emit({ .message = "Currently playing: " + std::string(sequenceName), + .remainingTime = (float)CVarGetInteger(CVAR_AUDIO("SeqNameOverlayDuration"), 5) }); + } +} \ No newline at end of file diff --git a/soh/soh/Enhancements/audio/AudioCollection.h b/soh/soh/Enhancements/audio/AudioCollection.h index 2cdb02e21..51a6902cf 100644 --- a/soh/soh/Enhancements/audio/AudioCollection.h +++ b/soh/soh/Enhancements/audio/AudioCollection.h @@ -73,4 +73,5 @@ void AudioCollection_AddToCollection(char* otrPath, uint16_t seqNum); const char* AudioCollection_GetSequenceName(uint16_t seqId); bool AudioCollection_HasSequenceNum(uint16_t seqId); size_t AudioCollection_SequenceMapSize(); +void AudioCollection_EmitSongNameNotification(s32 seqId); #endif \ No newline at end of file diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 6c547db08..15f85e52a 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -49,7 +49,6 @@ extern "C" { extern SaveContext gSaveContext; extern PlayState* gPlayState; -extern void Overlay_DisplayText(float duration, const char* text); } // GreyScaleEndDlist diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index d59871d54..7d2194cca 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2580,15 +2580,6 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { return false; } -extern "C" void Overlay_DisplayText(float duration, const char* text) { - Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->TextDrawNotification(duration, true, text); -} - -extern "C" void Overlay_DisplayText_Seconds(int seconds, const char* text) { - float duration = seconds * OTRGlobals::Instance->GetInterpolationFPS() * 0.05; - Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->TextDrawNotification(duration, true, text); -} - extern "C" void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex) { SetCurrentGrottoIDForTracker(entranceIndex); } diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index cab7575cd..032686f8e 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -156,8 +156,6 @@ void Randomizer_SetSpoilerLoaded(bool spoilerLoaded); uint8_t Randomizer_GenerateRandomizer(); void Randomizer_ShowRandomizerMenu(); int CustomMessage_RetrieveIfExists(PlayState* play); -void Overlay_DisplayText(float duration, const char* text); -void Overlay_DisplayText_Seconds(int seconds, const char* text); GetItemEntry ItemTable_Retrieve(int16_t getItemID); GetItemEntry ItemTable_RetrieveEntry(s16 modIndex, s16 getItemID); void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex); diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c index 81f67f9c0..52efa4016 100644 --- a/soh/src/code/audio_load.c +++ b/soh/src/code/audio_load.c @@ -640,10 +640,8 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { if (gPlayState != NULL) { previousSceneNum = gPlayState->sceneNum; } - const char* sequenceName = AudioCollection_GetSequenceName(seqId); - if (sequenceName != NULL) { - Overlay_DisplayText_Seconds(CVarGetInteger(CVAR_AUDIO("SeqNameOverlayDuration"), 5), sequenceName); - } + + AudioCollection_EmitSongNameNotification(seqId); } } From 526d221c56d99b5ab0715784205b562037be1750 Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Wed, 13 Aug 2025 04:57:02 +0200 Subject: [PATCH 17/23] Add timestamps for remaining custom rando items (#5713) --- soh/soh/Enhancements/gameplaystats.h | 45 +++++++++++++- .../Enhancements/randomizer/randomizer.cpp | 58 +++++++++++++++++++ 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/gameplaystats.h b/soh/soh/Enhancements/gameplaystats.h index 1f89cb3a4..1d60b5dfc 100644 --- a/soh/soh/Enhancements/gameplaystats.h +++ b/soh/soh/Enhancements/gameplaystats.h @@ -51,9 +51,48 @@ typedef enum { /* 0xA9 */ TIMESTAMP_DEFEAT_GANON, // z_boss_ganon2.c /* 0xA9 */ TIMESTAMP_BOSSRUSH_FINISH, // z_boss_ganon2.c /* 0xAA */ TIMESTAMP_FOUND_GREG, // z_parameter.c - /* 0xAA */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c - /* 0xAB */ TIMESTAMP_MAX - + /* 0xAB */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c + /* 0xAC */ TIMESTAMP_FOUND_GOHMA_SOUL, + /* 0xAD */ TIMESTAMP_FOUND_KING_DODONGO_SOUL, + /* 0xAE */ TIMESTAMP_FOUND_BARINADE_SOUL, + /* 0xAF */ TIMESTAMP_FOUND_PHANTOM_GANON_SOUL, + /* 0xB0 */ TIMESTAMP_FOUND_VOLVAGIA_SOUL, + /* 0xB1 */ TIMESTAMP_FOUND_MORPHA_SOUL, + /* 0xB2 */ TIMESTAMP_FOUND_BONGO_BONGO_SOUL, + /* 0xB3 */ TIMESTAMP_FOUND_TWINROVA_SOUL, + /* 0xB5 */ TIMESTAMP_FOUND_GANON_SOUL, + /* 0xB6 */ TIMESTAMP_FOUND_BRONZE_SCALE, + /* 0xB7 */ TIMESTAMP_FOUND_OCARINA_A_BUTTON, + /* 0xB8 */ TIMESTAMP_FOUND_OCARINA_C_UP_BUTTON, + /* 0xB9 */ TIMESTAMP_FOUND_OCARINA_C_DOWN_BUTTON, + /* 0xBA */ TIMESTAMP_FOUND_OCARINA_C_LEFT_BUTTON, + /* 0xBB */ TIMESTAMP_FOUND_OCARINA_C_RIGHT_BUTTON, + /* 0xBC */ TIMESTAMP_FOUND_FISHING_POLE, + /* 0xBD */ TIMESTAMP_FOUND_GUARD_HOUSE_KEY, + /* 0xBE */ TIMESTAMP_FOUND_MARKET_BAZAAR_KEY, + /* 0xBF */ TIMESTAMP_FOUND_MARKET_POTION_SHOP_KEY, + /* 0xC0 */ TIMESTAMP_FOUND_MASK_SHOP_KEY, + /* 0xC1 */ TIMESTAMP_FOUND_MARKET_SHOOTING_GALLERY_KEY, + /* 0xC2 */ TIMESTAMP_FOUND_BOMBCHU_BOWLING_KEY, + /* 0xC3 */ TIMESTAMP_FOUND_TREASURE_CHEST_GAME_BUILDING_KEY, + /* 0xC4 */ TIMESTAMP_FOUND_BOMBCHU_SHOP_KEY, + /* 0xC5 */ TIMESTAMP_FOUND_RICHARDS_HOUSE_KEY, + /* 0xC6 */ TIMESTAMP_FOUND_ALLEY_HOUSE_KEY, + /* 0xC7 */ TIMESTAMP_FOUND_KAK_BAZAAR_KEY, + /* 0xC8 */ TIMESTAMP_FOUND_KAK_POTION_SHOP_KEY, + /* 0xC9 */ TIMESTAMP_FOUND_BOSS_HOUSE_KEY, + /* 0xCA */ TIMESTAMP_FOUND_GRANNYS_POTION_SHOP_KEY, + /* 0xCB */ TIMESTAMP_FOUND_SKULLTULA_HOUSE_KEY, + /* 0xCC */ TIMESTAMP_FOUND_IMPAS_HOUSE_KEY, + /* 0xCD */ TIMESTAMP_FOUND_WINDMILL_KEY, + /* 0xCE */ TIMESTAMP_FOUND_KAK_SHOOTING_GALLERY_KEY, + /* 0xCF */ TIMESTAMP_FOUND_DAMPES_HUT_KEY, + /* 0xD0 */ TIMESTAMP_FOUND_TALONS_HOUSE_KEY, + /* 0xD1 */ TIMESTAMP_FOUND_STABLES_KEY, + /* 0xD2 */ TIMESTAMP_FOUND_BACK_TOWER_KEY, + /* 0xD3 */ TIMESTAMP_FOUND_HYLIA_LAB_KEY, + /* 0xD4 */ TIMESTAMP_FOUND_FISHING_HOLE_KEY, + /* 0xD5 */ TIMESTAMP_MAX } GameplayStatTimestamp; typedef enum { diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 5f9027d82..a5e265142 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -5783,6 +5783,53 @@ void RandomizerSettingsWindow::InitElement() { mSettings->UpdateOptionProperties(); } +static std::unordered_map randomizerGetToStatsTimeStamp = { + { RG_GOHMA_SOUL, TIMESTAMP_FOUND_GOHMA_SOUL }, + { RG_KING_DODONGO_SOUL, TIMESTAMP_FOUND_KING_DODONGO_SOUL }, + { RG_BARINADE_SOUL, TIMESTAMP_FOUND_BARINADE_SOUL }, + { RG_PHANTOM_GANON_SOUL, TIMESTAMP_FOUND_PHANTOM_GANON_SOUL }, + { RG_VOLVAGIA_SOUL, TIMESTAMP_FOUND_VOLVAGIA_SOUL }, + { RG_MORPHA_SOUL, TIMESTAMP_FOUND_MORPHA_SOUL }, + { RG_BONGO_BONGO_SOUL, TIMESTAMP_FOUND_BONGO_BONGO_SOUL }, + { RG_TWINROVA_SOUL, TIMESTAMP_FOUND_TWINROVA_SOUL }, + { RG_GANON_SOUL, TIMESTAMP_FOUND_GANON_SOUL }, + + { RG_BRONZE_SCALE, TIMESTAMP_FOUND_BRONZE_SCALE }, + + { RG_OCARINA_A_BUTTON, TIMESTAMP_FOUND_OCARINA_A_BUTTON }, + { RG_OCARINA_C_UP_BUTTON, TIMESTAMP_FOUND_OCARINA_C_UP_BUTTON }, + { RG_OCARINA_C_DOWN_BUTTON, TIMESTAMP_FOUND_OCARINA_C_DOWN_BUTTON }, + { RG_OCARINA_C_LEFT_BUTTON, TIMESTAMP_FOUND_OCARINA_C_LEFT_BUTTON }, + { RG_OCARINA_C_RIGHT_BUTTON, TIMESTAMP_FOUND_OCARINA_C_RIGHT_BUTTON }, + + { RG_FISHING_POLE, TIMESTAMP_FOUND_FISHING_POLE }, + + { RG_GUARD_HOUSE_KEY, TIMESTAMP_FOUND_GUARD_HOUSE_KEY }, + { RG_MARKET_BAZAAR_KEY, TIMESTAMP_FOUND_MARKET_BAZAAR_KEY }, + { RG_MARKET_POTION_SHOP_KEY, TIMESTAMP_FOUND_MARKET_POTION_SHOP_KEY }, + { RG_MASK_SHOP_KEY, TIMESTAMP_FOUND_MASK_SHOP_KEY }, + { RG_MARKET_SHOOTING_GALLERY_KEY, TIMESTAMP_FOUND_MARKET_SHOOTING_GALLERY_KEY }, + { RG_BOMBCHU_BOWLING_KEY, TIMESTAMP_FOUND_BOMBCHU_BOWLING_KEY }, + { RG_TREASURE_CHEST_GAME_BUILDING_KEY, TIMESTAMP_FOUND_TREASURE_CHEST_GAME_BUILDING_KEY }, + { RG_BOMBCHU_SHOP_KEY, TIMESTAMP_FOUND_BOMBCHU_SHOP_KEY }, + { RG_RICHARDS_HOUSE_KEY, TIMESTAMP_FOUND_RICHARDS_HOUSE_KEY }, + { RG_ALLEY_HOUSE_KEY, TIMESTAMP_FOUND_ALLEY_HOUSE_KEY }, + { RG_KAK_BAZAAR_KEY, TIMESTAMP_FOUND_KAK_BAZAAR_KEY }, + { RG_KAK_POTION_SHOP_KEY, TIMESTAMP_FOUND_KAK_POTION_SHOP_KEY }, + { RG_BOSS_HOUSE_KEY, TIMESTAMP_FOUND_BOSS_HOUSE_KEY }, + { RG_GRANNYS_POTION_SHOP_KEY, TIMESTAMP_FOUND_GRANNYS_POTION_SHOP_KEY }, + { RG_SKULLTULA_HOUSE_KEY, TIMESTAMP_FOUND_SKULLTULA_HOUSE_KEY }, + { RG_IMPAS_HOUSE_KEY, TIMESTAMP_FOUND_IMPAS_HOUSE_KEY }, + { RG_WINDMILL_KEY, TIMESTAMP_FOUND_WINDMILL_KEY }, + { RG_KAK_SHOOTING_GALLERY_KEY, TIMESTAMP_FOUND_KAK_SHOOTING_GALLERY_KEY }, + { RG_DAMPES_HUT_KEY, TIMESTAMP_FOUND_DAMPES_HUT_KEY }, + { RG_TALONS_HOUSE_KEY, TIMESTAMP_FOUND_TALONS_HOUSE_KEY }, + { RG_STABLES_KEY, TIMESTAMP_FOUND_STABLES_KEY }, + { RG_BACK_TOWER_KEY, TIMESTAMP_FOUND_BACK_TOWER_KEY }, + { RG_HYLIA_LAB_KEY, TIMESTAMP_FOUND_HYLIA_LAB_KEY }, + { RG_FISHING_HOLE_KEY, TIMESTAMP_FOUND_FISHING_HOLE_KEY }, +}; + // Gameplay stat tracking: Update time the item was acquired // (special cases for rando items) void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { @@ -5797,6 +5844,12 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { // Use ITEM_KEY_BOSS to timestamp Ganon's boss key if (item == RG_GANONS_CASTLE_BOSS_KEY) { gSaveContext.ship.stats.itemTimestamp[ITEM_KEY_BOSS] = time; + return; + } + + if (randomizerGetToStatsTimeStamp.contains((RandomizerGet)item)) { + gSaveContext.ship.stats.itemTimestamp[randomizerGetToStatsTimeStamp[(RandomizerGet)item]] = time; + return; } // Count any bottled item as a bottle @@ -5806,6 +5859,7 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { } return; } + // Count any bombchu pack as bombchus if ((item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_20) || item == RG_PROGRESSIVE_BOMBCHUS) { if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] = 0) { @@ -5813,11 +5867,15 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { } return; } + if (item == RG_MAGIC_SINGLE) { gSaveContext.ship.stats.itemTimestamp[ITEM_SINGLE_MAGIC] = time; + return; } + if (item == RG_DOUBLE_DEFENSE) { gSaveContext.ship.stats.itemTimestamp[ITEM_DOUBLE_DEFENSE] = time; + return; } } From c597e1c4ce78c80c61325912117a918a3b7c03db Mon Sep 17 00:00:00 2001 From: PurpleHato Date: Wed, 13 Aug 2025 04:57:19 +0200 Subject: [PATCH 18/23] TWEAK: SoT Time Travel - Allow Swordless (#5716) * Magic number + more modular * tweak + switch * forgot basic oot * linebreak * c-lang --- soh/soh/Enhancements/enhancementTypes.h | 2 ++ soh/soh/Enhancements/mods.cpp | 23 ++++++++++++++++++++--- soh/soh/SohGui/SohMenuEnhancements.cpp | 6 ++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/soh/soh/Enhancements/enhancementTypes.h b/soh/soh/Enhancements/enhancementTypes.h index c1b91cffa..bd025115b 100644 --- a/soh/soh/Enhancements/enhancementTypes.h +++ b/soh/soh/Enhancements/enhancementTypes.h @@ -112,7 +112,9 @@ typedef enum { typedef enum { TIME_TRAVEL_DISABLED, TIME_TRAVEL_OOT, + TIME_TRAVEL_OOT_MS, TIME_TRAVEL_ANY, + TIME_TRAVEL_ANY_MS } TimeTravelType; typedef enum { diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 15f85e52a..5fef9befc 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -130,10 +130,27 @@ void RegisterOcarinaTimeTravel() { bool notNearAnySource = !nearbyTimeBlockEmpty && !nearbyTimeBlock && !nearbyOcarinaSpot && !nearbyDoorOfTime && !nearbyFrogs && !nearbyGossipStone; bool hasOcarinaOfTime = (INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME); - bool doesntNeedOcarinaOfTime = CVarGetInteger(CVAR_ENHANCEMENT("TimeTravel"), 0) == 2; bool hasMasterSword = CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER); - // TODO: Once Swordless Adult is fixed: Remove the Master Sword check - if (justPlayedSoT && notNearAnySource && (hasOcarinaOfTime || doesntNeedOcarinaOfTime) && hasMasterSword) { + int timeTravelSetting = CVarGetInteger(CVAR_ENHANCEMENT("TimeTravel"), 0); + bool meetsTimeTravelRequirements = false; + + switch (timeTravelSetting) { + case TIME_TRAVEL_ANY: + meetsTimeTravelRequirements = true; + break; + case TIME_TRAVEL_ANY_MS: + meetsTimeTravelRequirements = hasMasterSword; + break; + case TIME_TRAVEL_OOT_MS: + meetsTimeTravelRequirements = hasMasterSword && hasOcarinaOfTime; + break; + case TIME_TRAVEL_OOT: + default: + meetsTimeTravelRequirements = hasOcarinaOfTime; + break; + } + + if (justPlayedSoT && notNearAnySource && meetsTimeTravelRequirements) { SwitchAge(); } }); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index bdc6f5344..488aeb720 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -56,7 +56,9 @@ static const std::unordered_map chestStyleMatchesContentsO static const std::unordered_map timeTravelOptions = { { TIME_TRAVEL_DISABLED, "Disabled" }, { TIME_TRAVEL_OOT, "Ocarina of Time" }, + { TIME_TRAVEL_OOT_MS, "Ocarina of Time + Master Sword" }, { TIME_TRAVEL_ANY, "Any Ocarina" }, + { TIME_TRAVEL_ANY_MS, "Any Ocarina + Master Sword" }, }; static const std::unordered_map sleepingWaterfallOptions = { @@ -783,9 +785,9 @@ void SohMenu::AddMenuEnhancements() { .Tooltip("Allows Link to freely change age by playing the Song of Time.\n" "Time Blocks can still be used properly.\n\n" "Requirements:\n" - " - Obtained the Ocarina of Time (depends on selection)\n" " - Obtained the Song of Time\n" - " - Obtained the Master Sword\n" + " - Obtained the Ocarina of Time (depends on selection)\n" + " - Obtained the Master Sword (depends on selection)\n" " - Not within range of a Time Block\n" " - Not within range of Ocarina Playing spots")); From 9cdffc080bb3ea8317df1aec8da6b2c38ca6389f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Wed, 13 Aug 2025 03:36:51 +0000 Subject: [PATCH 19/23] small typo fixes (#5680) incorrect RR was tripping up my code generator --- soh/soh/CrashHandlerExt.cpp | 2 +- soh/soh/Enhancements/randomizer/ShuffleCrates.cpp | 2 +- .../randomizer/location_access/overworld/gerudo_fortress.cpp | 2 +- soh/soh/Enhancements/randomizer/randomizer.cpp | 2 +- soh/soh/Enhancements/randomizer/randomizerTypes.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/soh/soh/CrashHandlerExt.cpp b/soh/soh/CrashHandlerExt.cpp index cc9c7b1ee..3ca821460 100644 --- a/soh/soh/CrashHandlerExt.cpp +++ b/soh/soh/CrashHandlerExt.cpp @@ -19,7 +19,7 @@ static std::array sCatToStrArray{ "SWITCH", "BG", "PLAYER", "EXPLOSIVE", "NPC", "ENEMY", "PROP", "ITEMACTION", "MISC", "BOSS", "DOOR", "CHEST", }; -#define DEFINE_SCENE(_1, _2, enumName, _4, _5, _6) #enumName +#define DEFINE_SCENE(_1, _2, enumName, _4, _5, _6) #enumName, static std::array sSceneIdToStrArray{ #include "tables/scene_table.h" diff --git a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp index 53a65df22..6962b290d 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp @@ -336,7 +336,7 @@ void Rando::StaticData::RegisterCrateLocations() { locationTable[RC_GF_SOUTHMOST_CENTER_CRATE] = Location::Crate(RC_GF_SOUTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(315, -1534), "Southmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_SOUTHMOST_CENTER_CRATE)); locationTable[RC_GF_MID_SOUTH_CENTER_CRATE] = Location::Crate(RC_GF_MID_SOUTH_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(315, -1594), "Middle South Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_MID_SOUTH_CENTER_CRATE)); locationTable[RC_GF_MID_NORTH_CENTER_CRATE] = Location::Crate(RC_GF_MID_NORTH_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1782), "Middle North Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_MID_NORTH_CENTER_CRATE)); - locationTable[RR_GF_NORTHMOST_CENTER_CRATE] = Location::Crate(RR_GF_NORTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1842), "Northmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_NORTHMOST_CENTER_CRATE)); + locationTable[RC_GF_NORTHMOST_CENTER_CRATE] = Location::Crate(RC_GF_NORTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1842), "Northmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_NORTHMOST_CENTER_CRATE)); locationTable[RC_GF_OUTSKIRTS_NE_CRATE] = Location::Crate(RC_GF_OUTSKIRTS_NE_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-60, -2210), "Outskirts Northeast Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_OUTSKIRTS_NE_CRATE)); locationTable[RC_GF_OUTSKIRTS_NW_CRATE] = Location::Crate(RC_GF_OUTSKIRTS_NW_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-120, -2210), "Outskirts Northwest Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_OUTSKIRTS_NW_CRATE)); locationTable[RC_GF_HBA_RANGE_CRATE_2] = Location::Crate(RC_GF_HBA_RANGE_CRATE_2, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(4090, -1780), "Horseback Archery Range Crate 2", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_HBA_RANGE_CRATE_2)); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp index 3929faf86..17e28d5eb 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp @@ -30,7 +30,7 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_SOUTHMOST_CENTER_CRATE, logic->CanBreakCrates()), LOCATION(RC_GF_MID_SOUTH_CENTER_CRATE, logic->CanBreakCrates()), LOCATION(RC_GF_MID_NORTH_CENTER_CRATE, logic->CanBreakCrates()), - LOCATION(RR_GF_NORTHMOST_CENTER_CRATE, logic->CanBreakCrates()), + LOCATION(RC_GF_NORTHMOST_CENTER_CRATE, logic->CanBreakCrates()), }, { //Exits Entrance(RR_TH_1_TORCH_CELL, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index a5e265142..b8a4820d8 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -2332,7 +2332,7 @@ std::map rcToRandomizerInf = { RAND_INF_GF_MID_NORTH_CENTER_CRATE, }, { - RR_GF_NORTHMOST_CENTER_CRATE, + RC_GF_NORTHMOST_CENTER_CRATE, RAND_INF_GF_NORTHMOST_CENTER_CRATE, }, { diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 15a2fbd37..c2b9cbbdd 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -2396,7 +2396,7 @@ typedef enum { RC_GF_SOUTHMOST_CENTER_CRATE, RC_GF_MID_SOUTH_CENTER_CRATE, RC_GF_MID_NORTH_CENTER_CRATE, - RR_GF_NORTHMOST_CENTER_CRATE, + RC_GF_NORTHMOST_CENTER_CRATE, RC_GF_OUTSKIRTS_NE_CRATE, RC_GF_OUTSKIRTS_NW_CRATE, RC_GF_HBA_RANGE_CRATE_1, From ce38e035625e6c0012ea6331c859daae09f6493a Mon Sep 17 00:00:00 2001 From: nclok1405 <155463060+nclok1405@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:38:44 +0900 Subject: [PATCH 20/23] Allow console commands in debug save (#5675) --- .../game-interactor/GameInteractionEffect.cpp | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp index 03667dd86..76e026206 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -50,7 +50,7 @@ namespace GameInteractionEffect { // MARK: - Flags GameInteractionEffectQueryResult SetSceneFlag::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } @@ -62,7 +62,7 @@ void SetSceneFlag::_Apply() { } GameInteractionEffectQueryResult UnsetSceneFlag::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } @@ -74,7 +74,7 @@ void UnsetSceneFlag::_Apply() { } GameInteractionEffectQueryResult SetFlag::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } @@ -86,7 +86,7 @@ void SetFlag::_Apply() { } GameInteractionEffectQueryResult UnsetFlag::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } @@ -99,7 +99,7 @@ void UnsetFlag::_Apply() { // MARK: - ModifyHeartContainers GameInteractionEffectQueryResult ModifyHeartContainers::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ((parameters[0] > 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) > 0x140)) || (parameters[0] < 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) < 0x10))) { @@ -115,7 +115,7 @@ void ModifyHeartContainers::_Apply() { // MARK: - FillMagic GameInteractionEffectQueryResult FillMagic::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if (!gSaveContext.isMagicAcquired || gSaveContext.magic >= ((gSaveContext.isDoubleMagicAcquired + 1) * 48)) { return GameInteractionEffectQueryResult::NotPossible; @@ -129,7 +129,7 @@ void FillMagic::_Apply() { // MARK: - EmptyMagic GameInteractionEffectQueryResult EmptyMagic::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if (!gSaveContext.isMagicAcquired || gSaveContext.magic <= 0) { return GameInteractionEffectQueryResult::NotPossible; @@ -143,7 +143,7 @@ void EmptyMagic::_Apply() { // MARK: - ModifyRupees GameInteractionEffectQueryResult ModifyRupees::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ((parameters[0] < 0 && gSaveContext.rupees <= 0) || (parameters[0] > 0 && gSaveContext.rupees >= CUR_CAPACITY(UPG_WALLET))) { @@ -158,7 +158,7 @@ void ModifyRupees::_Apply() { // MARK: - NoUI GameInteractionEffectQueryResult NoUI::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -173,7 +173,7 @@ void NoUI::_Remove() { // MARK: - ModifyGravity GameInteractionEffectQueryResult ModifyGravity::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -188,7 +188,7 @@ void ModifyGravity::_Remove() { // MARK: - ModifyHealth GameInteractionEffectQueryResult ModifyHealth::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity) || (parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0))) { @@ -204,7 +204,7 @@ void ModifyHealth::_Apply() { // MARK: - SetPlayerHealth GameInteractionEffectQueryResult SetPlayerHealth::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -217,7 +217,7 @@ void SetPlayerHealth::_Apply() { // MARK: - FreezePlayer GameInteractionEffectQueryResult FreezePlayer::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -230,7 +230,7 @@ void FreezePlayer::_Apply() { // MARK: - BurnPlayer GameInteractionEffectQueryResult BurnPlayer::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -243,7 +243,7 @@ void BurnPlayer::_Apply() { // MARK: - ElectrocutePlayer GameInteractionEffectQueryResult ElectrocutePlayer::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -256,7 +256,7 @@ void ElectrocutePlayer::_Apply() { // MARK: - KnockbackPlayer GameInteractionEffectQueryResult KnockbackPlayer::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || player->stateFlags2 & PLAYER_STATE2_CRAWLING) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { @@ -269,7 +269,7 @@ void KnockbackPlayer::_Apply() { // MARK: - ModifyLinkSize GameInteractionEffectQueryResult ModifyLinkSize::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -284,7 +284,7 @@ void ModifyLinkSize::_Remove() { // MARK: - InvisibleLink GameInteractionEffectQueryResult InvisibleLink::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -299,7 +299,7 @@ void InvisibleLink::_Remove() { // MARK: - PacifistMode GameInteractionEffectQueryResult PacifistMode::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -314,7 +314,7 @@ void PacifistMode::_Remove() { // MARK: - DisableZTargeting GameInteractionEffectQueryResult DisableZTargeting::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -329,7 +329,7 @@ void DisableZTargeting::_Remove() { // MARK: - WeatherRainstorm GameInteractionEffectQueryResult WeatherRainstorm::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -344,7 +344,7 @@ void WeatherRainstorm::_Remove() { // MARK: - ReverseControls GameInteractionEffectQueryResult ReverseControls::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -359,7 +359,7 @@ void ReverseControls::_Remove() { // MARK: - ForceEquipBoots GameInteractionEffectQueryResult ForceEquipBoots::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -374,7 +374,7 @@ void ForceEquipBoots::_Remove() { // MARK: - ModifyMovementSpeedMultiplier GameInteractionEffectQueryResult ModifyMovementSpeedMultiplier::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -393,7 +393,7 @@ void ModifyMovementSpeedMultiplier::_Remove() { // MARK: - OneHitKO GameInteractionEffectQueryResult OneHitKO::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -408,7 +408,7 @@ void OneHitKO::_Remove() { // MARK: - ModifyDefenseModifier GameInteractionEffectQueryResult ModifyDefenseModifier::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -423,7 +423,7 @@ void ModifyDefenseModifier::_Remove() { // MARK: - GiveOrTakeShield GameInteractionEffectQueryResult GiveOrTakeShield::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ((parameters[0] > 0 && ((gBitFlags[parameters[0] - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_TYPE_SHIELD]) & gSaveContext.inventory.equipment)) || @@ -441,7 +441,7 @@ void GiveOrTakeShield::_Apply() { // MARK: - TeleportPlayer GameInteractionEffectQueryResult TeleportPlayer::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -453,7 +453,7 @@ void TeleportPlayer::_Apply() { // MARK: - ClearAssignedButtons GameInteractionEffectQueryResult ClearAssignedButtons::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -465,7 +465,7 @@ void ClearAssignedButtons::_Apply() { // MARK: - SetTimeOfDay GameInteractionEffectQueryResult SetTimeOfDay::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -477,7 +477,7 @@ void SetTimeOfDay::_Apply() { // MARK: - SetCollisionViewer GameInteractionEffectQueryResult SetCollisionViewer::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -492,7 +492,7 @@ void SetCollisionViewer::_Remove() { // MARK: - RandomizeCosmetics GameInteractionEffectQueryResult RandomizeCosmetics::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -504,7 +504,7 @@ void RandomizeCosmetics::_Apply() { // MARK: - PressButton GameInteractionEffectQueryResult PressButton::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -516,7 +516,7 @@ void PressButton::_Apply() { // MARK: - PressRandomButton GameInteractionEffectQueryResult PressRandomButton::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -528,7 +528,7 @@ void PressRandomButton::_Apply() { // MARK: - AddOrTakeAmmo GameInteractionEffectQueryResult AddOrTakeAmmo::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if (!GameInteractor::CanAddOrTakeAmmo(parameters[0], parameters[1])) { return GameInteractionEffectQueryResult::NotPossible; @@ -542,7 +542,7 @@ void AddOrTakeAmmo::_Apply() { // MARK: - RandomBombFuseTimer GameInteractionEffectQueryResult RandomBombFuseTimer::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -557,7 +557,7 @@ void RandomBombFuseTimer::_Remove() { // MARK: - DisableLedgeGrabs GameInteractionEffectQueryResult DisableLedgeGrabs::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -572,7 +572,7 @@ void DisableLedgeGrabs::_Remove() { // MARK: - RandomWind GameInteractionEffectQueryResult RandomWind::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -587,7 +587,7 @@ void RandomWind::_Remove() { // MARK: - RandomBonks GameInteractionEffectQueryResult RandomBonks::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -602,7 +602,7 @@ void RandomBonks::_Remove() { // MARK: - PlayerInvincibility GameInteractionEffectQueryResult PlayerInvincibility::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -617,7 +617,7 @@ void PlayerInvincibility::_Remove() { // MARK: - SlipperyFloor GameInteractionEffectQueryResult SlipperyFloor::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -632,7 +632,7 @@ void SlipperyFloor::_Remove() { // MARK: - SpawnEnemyWithOffset GameInteractionEffectQueryResult SpawnEnemyWithOffset::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } return GameInteractor::RawAction::SpawnEnemyWithOffset(parameters[0], parameters[1]); @@ -644,7 +644,7 @@ void SpawnEnemyWithOffset::_Apply() { // MARK: - SpawnActor GameInteractionEffectQueryResult SpawnActor::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } return GameInteractor::RawAction::SpawnActor(parameters[0], parameters[1]); From ee20f7f76222cf63f929f8d5f57764266f89ca7e Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Wed, 13 Aug 2025 05:39:31 +0200 Subject: [PATCH 21/23] Fix max count for TH keys (#5670) --- .../randomizer/randomizer_item_tracker.cpp | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index fcace1017..04bc783d9 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -449,7 +449,8 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) { result.currentCapacity = IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) ? 0 : CUR_CAPACITY(UPG_WALLET); result.maxCapacity = - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_INCLUDE_TYCOON_WALLET) ? 999 : 500; + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_INCLUDE_TYCOON_WALLET) ? 999 + : 500; result.currentAmmo = gSaveContext.rupees; break; case ITEM_BOMBCHU: @@ -502,7 +503,28 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) { result.maxCapacity = GERUDO_TRAINING_GROUND_SMALL_KEY_MAX; break; case SCENE_THIEVES_HIDEOUT: - result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX; + if (IS_RANDO) { + switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS)) { + case RO_GF_CARPENTERS_NORMAL: + result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX; + break; + case RO_GF_CARPENTERS_FAST: + result.maxCapacity = 1; + break; + case RO_GF_CARPENTERS_FREE: + result.maxCapacity = 0; + break; + default: + result.maxCapacity = 0; + SPDLOG_ERROR( + "Invalid value for RSK_GERUDO_FORTRESS: " + + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS)); + assert(false); + break; + } + } else { + result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX; + } break; case SCENE_INSIDE_GANONS_CASTLE: result.maxCapacity = GANONS_CASTLE_SMALL_KEY_MAX; From fadae4997740d6073d6ce01db2950c8a24b3cbb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Wed, 13 Aug 2025 16:46:36 +0000 Subject: [PATCH 22/23] shipinit bonk damage (#5653) * shipinit bonk damage * no initFunc --- soh/soh/Enhancements/BonkDamage.cpp | 51 +++++++++++++++++++++++++++++ soh/soh/Enhancements/mods.cpp | 44 ------------------------- 2 files changed, 51 insertions(+), 44 deletions(-) create mode 100644 soh/soh/Enhancements/BonkDamage.cpp diff --git a/soh/soh/Enhancements/BonkDamage.cpp b/soh/soh/Enhancements/BonkDamage.cpp new file mode 100644 index 000000000..e11f46b78 --- /dev/null +++ b/soh/soh/Enhancements/BonkDamage.cpp @@ -0,0 +1,51 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" +#include "./enhancementTypes.h" + +extern "C" { +#include "functions.h" +#include "macros.h" +extern PlayState* gPlayState; +extern SaveContext gSaveContext; +} + +void RegisterBonkDamage() { + COND_HOOK(OnPlayerBonk, CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE) != BONK_DAMAGE_NONE, + [] { + uint16_t bonkDamage = 0; + switch (CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE)) { + case BONK_DAMAGE_NONE: + return; + case BONK_DAMAGE_OHKO: + gSaveContext.health = 0; + return; + case BONK_DAMAGE_QUARTER_HEART: + bonkDamage = 4; + break; + case BONK_DAMAGE_HALF_HEART: + bonkDamage = 8; + break; + case BONK_DAMAGE_1_HEART: + bonkDamage = 16; + break; + case BONK_DAMAGE_2_HEARTS: + bonkDamage = 32; + break; + case BONK_DAMAGE_4_HEARTS: + bonkDamage = 64; + break; + case BONK_DAMAGE_8_HEARTS: + bonkDamage = 128; + break; + default: + break; + } + + Health_ChangeBy(gPlayState, -bonkDamage); + // Set invincibility to make Link flash red as a visual damage indicator. + Player* player = GET_PLAYER(gPlayState); + player->invincibilityTimer = 28; + }); +} + +static RegisterShipInitFunc registerBonkDamage(RegisterBonkDamage, { CVAR_ENHANCEMENT("BonkDamageMult") }); diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 5fef9befc..f276e19d3 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -299,49 +299,6 @@ void UpdateHyperEnemiesState() { } } -void RegisterBonkDamage() { - GameInteractor::Instance->RegisterGameHook([]() { - uint8_t bonkOption = CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE); - if (bonkOption == BONK_DAMAGE_NONE) { - return; - } - - if (bonkOption == BONK_DAMAGE_OHKO) { - gSaveContext.health = 0; - return; - } - - uint16_t bonkDamage = 0; - switch (bonkOption) { - case BONK_DAMAGE_QUARTER_HEART: - bonkDamage = 4; - break; - case BONK_DAMAGE_HALF_HEART: - bonkDamage = 8; - break; - case BONK_DAMAGE_1_HEART: - bonkDamage = 16; - break; - case BONK_DAMAGE_2_HEARTS: - bonkDamage = 32; - break; - case BONK_DAMAGE_4_HEARTS: - bonkDamage = 64; - break; - case BONK_DAMAGE_8_HEARTS: - bonkDamage = 128; - break; - default: - break; - } - - Health_ChangeBy(gPlayState, -bonkDamage); - // Set invincibility to make Link flash red as a visual damage indicator. - Player* player = GET_PLAYER(gPlayState); - player->invincibilityTimer = 28; - }); -} - void UpdateDirtPathFixState(int32_t sceneNum) { switch (sceneNum) { case SCENE_HYRULE_FIELD: @@ -982,7 +939,6 @@ void InitMods() { RegisterDeleteFileOnDeath(); RegisterHyperBosses(); UpdateHyperEnemiesState(); - RegisterBonkDamage(); RegisterMenuPathFix(); RegisterMirrorModeHandler(); RegisterResetNaviTimer(); From 7b4df9bdb2ebde2bdd6581c10ffdc1aea4ee1573 Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:08:32 +0200 Subject: [PATCH 23/23] Boss Rush cleanup (#5652) * Reduce stuff in the `.h`s & use `COND_HOOK` + variants * Move some boss rush stuff from `z_file_choose.c` * clang * Update BossRush.cpp --- soh/include/z64.h | 2 +- soh/include/z64save.h | 2 +- soh/soh/Enhancements/boss-rush/BossRush.cpp | 339 +++++++++++++++--- soh/soh/Enhancements/boss-rush/BossRush.h | 33 +- .../Enhancements/boss-rush/BossRushTypes.h | 91 ----- .../vanilla-behavior/GIVanillaBehavior.h | 14 + soh/soh/Enhancements/mods.cpp | 2 - soh/src/code/z_parameter.c | 10 +- .../actors/ovl_Door_Warp1/z_door_warp1.c | 8 +- .../ovl_file_choose/z_file_choose.c | 162 +-------- 10 files changed, 338 insertions(+), 325 deletions(-) delete mode 100644 soh/soh/Enhancements/boss-rush/BossRushTypes.h diff --git a/soh/include/z64.h b/soh/include/z64.h index 79ecdabcd..70b877f30 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -1522,7 +1522,7 @@ typedef struct { /* 0x34 */ s32 isEnabled; } StickDirectionPrompt; -typedef struct { +typedef struct FileChooseContext { /* 0x00000 */ GameState state; /* 0x000A4 */ Vtx* windowVtx; /* 0x000A8 */ u8* staticSegment; diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 63a9b1d10..bf1342360 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -7,7 +7,7 @@ #include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/gameplaystats.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h" -#include "soh/Enhancements/boss-rush/BossRushTypes.h" +#include "soh/Enhancements/boss-rush/BossRush.h" typedef enum { /* 0x0 */ MAGIC_STATE_IDLE, // Regular gameplay diff --git a/soh/soh/Enhancements/boss-rush/BossRush.cpp b/soh/soh/Enhancements/boss-rush/BossRush.cpp index 979f0ad6a..af5c04dd2 100644 --- a/soh/soh/Enhancements/boss-rush/BossRush.cpp +++ b/soh/soh/Enhancements/boss-rush/BossRush.cpp @@ -2,6 +2,8 @@ #include "soh/OTRGlobals.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh_assets.h" +#include "soh/frame_interpolation.h" #include #include @@ -14,14 +16,87 @@ extern "C" { #include "src/overlays/actors/ovl_Boss_Goma/z_boss_goma.h" #include "src/overlays/actors/ovl_Boss_Mo/z_boss_mo.h" #include "src/overlays/actors/ovl_Door_Warp1/z_door_warp1.h" -extern PlayState* gPlayState; - -Gfx* KaleidoScope_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, u16 point); +#include "src/overlays/gamestates/ovl_file_choose/file_choose.h" +#include "objects/gameplay_keep/gameplay_keep.h" #include "textures/icon_item_nes_static/icon_item_nes_static.h" #include "textures/icon_item_ger_static/icon_item_ger_static.h" #include "textures/icon_item_fra_static/icon_item_fra_static.h" + +extern PlayState* gPlayState; + +Gfx* KaleidoScope_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, u16 point); +void FileChoose_UpdateStickDirectionPromptAnim(GameState* thisx); +void FileChoose_DrawTextRec(GraphicsContext* gfxCtx, s32 r, s32 g, s32 b, s32 a, f32 x, f32 y, f32 z, s32 s, s32 t, + f32 dx, f32 dy); } +typedef enum { + BR_CHOICE_BOSSES_ALL, + BR_CHOICE_BOSSES_CHILD, + BR_CHOICE_BOSSES_ADULT, + BR_CHOICE_BOSSES_GANONDORF_GANON +} BossRushBossesChoices; + +typedef enum { + BR_CHOICE_HEARTS_10, + BR_CHOICE_HEARTS_15, + BR_CHOICE_HEARTS_20, + BR_CHOICE_HEARTS_3, + BR_CHOICE_HEARTS_5, + BR_CHOICE_HEARTS_7 +} BossRushHeartsChoices; + +typedef enum { + BR_CHOICE_AMMO_LIMITED, + BR_CHOICE_AMMO_FULL, + BR_CHOICE_AMMO_MAXED, +} BossRushAmmoChoices; + +typedef enum { + BR_CHOICE_HEAL_GANONDORF, + BR_CHOICE_HEAL_EVERYBOSS, + BR_CHOICE_HEAL_NEVER, +} BossRushHealChoices; + +typedef enum { + BR_CHOICE_MAGIC_SINGLE, + BR_CHOICE_MAGIC_DOUBLE, +} BossRushMagicChoices; + +typedef enum { + BR_CHOICE_BGS_NO, + BR_CHOICE_BGS_YES, +} BossRushBgsChoices; + +typedef enum { + BR_CHOICE_BOTTLE_NO, + BR_CHOICE_BOTTLE_EMPTY, + BR_CHOICE_BOTTLE_FAIRY, + BR_CHOICE_BOTTLE_REDPOTION, + BR_CHOICE_BOTTLE_GREENPOTION, + BR_CHOICE_BOTTLE_BLUEPOTION +} BossRushBottleChoices; + +typedef enum { + BR_CHOICE_LONGSHOT_NO, + BR_CHOICE_LONGSHOT_YES, +} BossRushLongshotChoices; + +typedef enum { + BR_CHOICE_HOVERBOOTS_NO, + BR_CHOICE_HOVERBOOTS_YES, +} BossRushHoverBootsChoices; + +typedef enum { + BR_CHOICE_BUNNYHOOD_NO, + BR_CHOICE_BUNNYHOOD_YES, +} BossRushBunnyHoodChoices; + +typedef enum { + BR_CHOICE_TIMER_YES, + BR_CHOICE_TIMER_NO, +} BossRushTimerChoices; + typedef struct BossRushSetting { std::array name; std::vector> choices; @@ -114,6 +189,180 @@ u8 BossRush_GetSettingOptionsAmount(u8 optionIndex) { return static_cast(BossRushOptions[optionIndex].choices.size()); } +void FileChoose_UpdateBossRushMenu(GameState* gameState) { + static s8 sLastBossRushOptionIndex = -1; + static s8 sLastBossRushOptionValue = -1; + + FileChoose_UpdateStickDirectionPromptAnim(gameState); + FileChooseContext* fileChooseContext = (FileChooseContext*)gameState; + Input* input = &fileChooseContext->state.input[0]; + bool dpad = CVarGetInteger(CVAR_SETTING("DpadInText"), 0); + + // Fade in elements after opening Boss Rush options menu + fileChooseContext->bossRushUIAlpha += 25; + if (fileChooseContext->bossRushUIAlpha > 255) { + fileChooseContext->bossRushUIAlpha = 255; + } + + // Animate up/down arrows. + fileChooseContext->bossRushArrowOffset += 1; + if (fileChooseContext->bossRushArrowOffset >= 30) { + fileChooseContext->bossRushArrowOffset = 0; + } + + // Move menu selection up or down. + if (ABS(fileChooseContext->stickRelY) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN | BTN_DUP))) { + // Move down + if (fileChooseContext->stickRelY < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN))) { + // When selecting past the last option, cycle back to the first option. + if ((fileChooseContext->bossRushIndex + 1) > BR_OPTIONS_MAX - 1) { + fileChooseContext->bossRushIndex = 0; + fileChooseContext->bossRushOffset = 0; + } else { + fileChooseContext->bossRushIndex++; + // When last visible option is selected when moving down, offset the list down by one. + if (fileChooseContext->bossRushIndex - fileChooseContext->bossRushOffset > + BOSSRUSH_MAX_OPTIONS_ON_SCREEN - 1) { + fileChooseContext->bossRushOffset++; + } + } + } else if (fileChooseContext->stickRelY > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DUP))) { + // When selecting past the first option, cycle back to the last option and offset the list to view it + // properly. + if ((fileChooseContext->bossRushIndex - 1) < 0) { + fileChooseContext->bossRushIndex = BR_OPTIONS_MAX - 1; + fileChooseContext->bossRushOffset = + fileChooseContext->bossRushIndex - BOSSRUSH_MAX_OPTIONS_ON_SCREEN + 1; + } else { + // When first visible option is selected when moving up, offset the list up by one. + if (fileChooseContext->bossRushIndex - fileChooseContext->bossRushOffset == 0) { + fileChooseContext->bossRushOffset--; + } + fileChooseContext->bossRushIndex--; + } + } + + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } + + // Cycle through choices for currently selected option. + if (ABS(fileChooseContext->stickRelX) > 30 || + (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) { + if (fileChooseContext->stickRelX > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DRIGHT))) { + // If exceeding the amount of choices for the selected option, cycle back to the first. + if ((gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] + 1) == + BossRush_GetSettingOptionsAmount(fileChooseContext->bossRushIndex)) { + gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] = 0; + } else { + gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]++; + } + } else if (fileChooseContext->stickRelX < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT))) { + // If cycling back when already at the first choice for the selected option, cycle back to the last choice. + if ((gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] - 1) < 0) { + gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] = + BossRush_GetSettingOptionsAmount(fileChooseContext->bossRushIndex) - 1; + } else { + gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]--; + } + } + + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } + + if (sLastBossRushOptionIndex != fileChooseContext->bossRushIndex || + sLastBossRushOptionValue != gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]) { + GameInteractor_ExecuteOnUpdateFileBossRushOptionSelection( + fileChooseContext->bossRushIndex, + gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]); + sLastBossRushOptionIndex = fileChooseContext->bossRushIndex; + sLastBossRushOptionValue = gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + fileChooseContext->configMode = CM_BOSS_RUSH_TO_QUEST; + return; + } + + // Load into the game. + if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + fileChooseContext->buttonIndex = 0xFE; + fileChooseContext->menuMode = FS_MENU_MODE_SELECT; + fileChooseContext->selectMode = SM_FADE_OUT; + fileChooseContext->prevConfigMode = fileChooseContext->configMode; + return; + } +} + +void FileChoose_DrawBossRushMenuWindowContents(FileChooseContext* fileChooseContext) { + OPEN_DISPS(fileChooseContext->state.gfxCtx); + + uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language; + uint8_t listOffset = fileChooseContext->bossRushOffset; + uint8_t textAlpha = fileChooseContext->bossRushUIAlpha; + + // Draw arrows to indicate that the list can scroll up or down. + // Arrow up + if (listOffset > 0) { + uint16_t arrowUpX = 140; + uint16_t arrowUpY = 76 - (fileChooseContext->bossRushArrowOffset / 10); + gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowUpTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSPWideTextureRectangle(POLY_OPA_DISP++, arrowUpX << 2, arrowUpY << 2, (arrowUpX + 8) << 2, (arrowUpY + 8) << 2, + G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11)); + } + // Arrow down + if (BR_OPTIONS_MAX - listOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN) { + uint16_t arrowDownX = 140; + uint16_t arrowDownY = 181 + (fileChooseContext->bossRushArrowOffset / 10); + gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowDownTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSPWideTextureRectangle(POLY_OPA_DISP++, arrowDownX << 2, arrowDownY << 2, (arrowDownX + 8) << 2, + (arrowDownY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11)); + } + + // Draw options. There's more options than what fits on the screen, so the visible options + // depend on the current offset of the list. Currently selected option pulses in + // color and has arrows surrounding the option. + for (uint8_t i = listOffset; i - listOffset < BOSSRUSH_MAX_OPTIONS_ON_SCREEN; i++) { + uint16_t textYOffset = (i - listOffset) * 16; + + // Option name. + Interface_DrawTextLine(fileChooseContext->state.gfxCtx, (char*)BossRush_GetSettingName(i, language), 65, + (87 + textYOffset), 255, 255, 80, textAlpha, 0.8f, true); + + // Selected choice for option. + uint16_t finalKerning = Interface_DrawTextLine( + fileChooseContext->state.gfxCtx, + (char*)BossRush_GetSettingChoiceName(i, gSaveContext.ship.quest.data.bossRush.options[i], language), 165, + (87 + textYOffset), 255, 255, 255, textAlpha, 0.8f, true); + + // Draw arrows around selected option. + if (fileChooseContext->bossRushIndex == i) { + Gfx_SetupDL_39Opa(fileChooseContext->state.gfxCtx); + gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + FileChoose_DrawTextRec(fileChooseContext->state.gfxCtx, fileChooseContext->stickLeftPrompt.arrowColorR, + fileChooseContext->stickLeftPrompt.arrowColorG, + fileChooseContext->stickLeftPrompt.arrowColorB, textAlpha, 160, (92 + textYOffset), + 0.42f, 0, 0, -1.0f, 1.0f); + FileChoose_DrawTextRec(fileChooseContext->state.gfxCtx, fileChooseContext->stickRightPrompt.arrowColorR, + fileChooseContext->stickRightPrompt.arrowColorG, + fileChooseContext->stickRightPrompt.arrowColorB, textAlpha, (171 + finalKerning), + (92 + textYOffset), 0.42f, 0, 0, 1.0f, 1.0f); + } + } + + CLOSE_DISPS(fileChooseContext->state.gfxCtx); +} + void BossRush_SpawnBlueWarps(PlayState* play) { // Spawn blue warps in Chamber of Sages based on what bosses have been defeated. @@ -316,7 +565,7 @@ void BossRush_HandleCompleteBoss(PlayState* play) { } } -void BossRush_InitSave() { +extern "C" void BossRush_InitSave() { // Set player name to Lonk for the few textboxes that show up during Boss Rush. Player can't input their own name. std::array brPlayerName = { 21, 50, 49, 46, 62, 62, 62, 62 }; @@ -609,6 +858,17 @@ void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; break; } + // Handle the heal on blue warp + case VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE: { + if (*should) { + BossRush_HandleBlueWarpHeal(gPlayState); + } + break; + } + case VB_SHOW_GAMEPLAY_TIMER: { + *should |= gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_TIMER] == BR_CHOICE_TIMER_YES; + break; + } // Prevent saving case VB_BE_ABLE_TO_SAVE: // Disable doors so the player can't leave the boss rooms backwards. @@ -629,25 +889,6 @@ void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li va_end(args); } -void BossRush_OnActorInitHandler(void* actorRef) { - Actor* actor = static_cast(actorRef); - - if (actor->id == ACTOR_DEMO_SA && gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES) { - BossRush_SpawnBlueWarps(gPlayState); - Actor_Kill(actor); - GET_PLAYER(gPlayState)->actor.world.rot.y = GET_PLAYER(gPlayState)->actor.shape.rot.y = 27306; - return; - } - - // Remove chests, mainly for the chest in King Dodongo's boss room. - // Remove bushes, used in Gohma's arena. - // Remove pots, used in Barinade's and Ganondorf's arenas. - if (actor->id == ACTOR_EN_KUSA || actor->id == ACTOR_OBJ_TSUBO || actor->id == ACTOR_EN_BOX) { - Actor_Kill(actor); - return; - } -} - void BossRush_OnSceneInitHandler(s16 sceneNum) { // Unpause the timer when the scene loaded isn't the Chamber of Sages. if (sceneNum != SCENE_CHAMBER_OF_THE_SAGES) { @@ -667,38 +908,32 @@ void BossRush_OnBlueWarpUpdate(void* actor) { } } -void BossRush_RegisterHooks() { - static u32 onVanillaBehaviorHook = 0; - static u32 onSceneInitHook = 0; - static u32 onActorInitHook = 0; - static u32 onBossDefeatHook = 0; - static u32 onActorUpdate = 0; +void RegisterBossRush() { + COND_HOOK(OnLoadGame, true, [](int32_t fileNum) { + COND_ID_HOOK(OnActorInit, ACTOR_DEMO_SA, IS_BOSS_RUSH, [](void* actorPtr) { + BossRush_SpawnBlueWarps(gPlayState); + Actor_Kill((Actor*)actorPtr); + GET_PLAYER(gPlayState)->actor.world.rot.y = 27306; + GET_PLAYER(gPlayState)->actor.shape.rot.y = 27306; + }); - GameInteractor::Instance->RegisterGameHook([](int32_t fileNum) { - GameInteractor::Instance->UnregisterGameHook(onVanillaBehaviorHook); - GameInteractor::Instance->UnregisterGameHook(onSceneInitHook); - GameInteractor::Instance->UnregisterGameHook(onActorInitHook); - GameInteractor::Instance->UnregisterGameHook(onBossDefeatHook); - GameInteractor::Instance->UnregisterGameHookForID(onActorUpdate); + // Remove bushes, used in Gohma's arena + COND_ID_HOOK(OnActorInit, ACTOR_EN_KUSA, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); }); - onVanillaBehaviorHook = 0; - onSceneInitHook = 0; - onActorInitHook = 0; - onBossDefeatHook = 0; - onActorUpdate = 0; + // Remove pots, used in Barinade's and Ganondorf's arenas + COND_ID_HOOK(OnActorInit, ACTOR_OBJ_TSUBO, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); }); - if (!IS_BOSS_RUSH) - return; + // Remove chests, mainly for the chest in King Dodongo's boss room + COND_ID_HOOK(OnActorInit, ACTOR_EN_BOX, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); }); - onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook( - BossRush_OnVanillaBehaviorHandler); - onSceneInitHook = - GameInteractor::Instance->RegisterGameHook(BossRush_OnSceneInitHandler); - onActorInitHook = - GameInteractor::Instance->RegisterGameHook(BossRush_OnActorInitHandler); - onBossDefeatHook = - GameInteractor::Instance->RegisterGameHook(BossRush_OnBossDefeatHandler); - onActorUpdate = GameInteractor::Instance->RegisterGameHookForID( - ACTOR_DOOR_WARP1, BossRush_OnBlueWarpUpdate); + COND_HOOK(OnVanillaBehavior, IS_BOSS_RUSH, BossRush_OnVanillaBehaviorHandler); + + COND_HOOK(OnSceneInit, IS_BOSS_RUSH, BossRush_OnSceneInitHandler); + + COND_HOOK(OnBossDefeat, IS_BOSS_RUSH, BossRush_OnBossDefeatHandler); + + COND_ID_HOOK(OnActorUpdate, ACTOR_DOOR_WARP1, IS_BOSS_RUSH, BossRush_OnBlueWarpUpdate); }); } + +static RegisterShipInitFunc initFunc(RegisterBossRush); \ No newline at end of file diff --git a/soh/soh/Enhancements/boss-rush/BossRush.h b/soh/soh/Enhancements/boss-rush/BossRush.h index 881d8d53f..4a47f4f5b 100644 --- a/soh/soh/Enhancements/boss-rush/BossRush.h +++ b/soh/soh/Enhancements/boss-rush/BossRush.h @@ -1,16 +1,41 @@ #pragma once -#include "z64.h" +#include #ifdef __cplusplus extern "C" { #endif -void BossRush_HandleBlueWarpHeal(PlayState* play); -void BossRush_InitSave(); +struct GameState; +struct FileChooseContext; + +void FileChoose_UpdateBossRushMenu(struct GameState* gameState); +void FileChoose_DrawBossRushMenuWindowContents(struct FileChooseContext* fileChooseContext); const char* BossRush_GetSettingName(u8 optionIndex, u8 language); const char* BossRush_GetSettingChoiceName(u8 optionIndex, u8 choiceIndex, u8 language); u8 BossRush_GetSettingOptionsAmount(u8 optionIndex); -void BossRush_RegisterHooks(); #ifdef __cplusplus }; #endif + +#define BOSSRUSH_MAX_OPTIONS_ON_SCREEN 6 + +typedef enum { + BR_OPTIONS_BOSSES, + BR_OPTIONS_HEARTS, + BR_OPTIONS_AMMO, + BR_OPTIONS_HEAL, + BR_OPTIONS_HYPERBOSSES, + BR_OPTIONS_MAGIC, + BR_OPTIONS_BGS, + BR_OPTIONS_BOTTLE, + BR_OPTIONS_LONGSHOT, + BR_OPTIONS_HOVERBOOTS, + BR_OPTIONS_BUNNYHOOD, + BR_OPTIONS_TIMER, + BR_OPTIONS_MAX, +} BossRushOptionEnums; + +typedef enum { + BR_CHOICE_HYPERBOSSES_NO, + BR_CHOICE_HYPERBOSSES_YES, +} BossRushHyperBossesChoices; \ No newline at end of file diff --git a/soh/soh/Enhancements/boss-rush/BossRushTypes.h b/soh/soh/Enhancements/boss-rush/BossRushTypes.h deleted file mode 100644 index 973f1599f..000000000 --- a/soh/soh/Enhancements/boss-rush/BossRushTypes.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#define BOSSRUSH_MAX_OPTIONS_ON_SCREEN 6 - -typedef enum { - BR_OPTIONS_BOSSES, - BR_OPTIONS_HEARTS, - BR_OPTIONS_AMMO, - BR_OPTIONS_HEAL, - BR_OPTIONS_HYPERBOSSES, - BR_OPTIONS_MAGIC, - BR_OPTIONS_BGS, - BR_OPTIONS_BOTTLE, - BR_OPTIONS_LONGSHOT, - BR_OPTIONS_HOVERBOOTS, - BR_OPTIONS_BUNNYHOOD, - BR_OPTIONS_TIMER, - BR_OPTIONS_MAX, -} BossRushOptionEnums; - -typedef enum { - BR_CHOICE_BOSSES_ALL, - BR_CHOICE_BOSSES_CHILD, - BR_CHOICE_BOSSES_ADULT, - BR_CHOICE_BOSSES_GANONDORF_GANON -} BossRushBossesChoices; - -typedef enum { - BR_CHOICE_HEARTS_10, - BR_CHOICE_HEARTS_15, - BR_CHOICE_HEARTS_20, - BR_CHOICE_HEARTS_3, - BR_CHOICE_HEARTS_5, - BR_CHOICE_HEARTS_7 -} BossRushHeartsChoices; - -typedef enum { - BR_CHOICE_AMMO_LIMITED, - BR_CHOICE_AMMO_FULL, - BR_CHOICE_AMMO_MAXED, -} BossRushAmmoChoices; - -typedef enum { - BR_CHOICE_HEAL_GANONDORF, - BR_CHOICE_HEAL_EVERYBOSS, - BR_CHOICE_HEAL_NEVER, -} BossRushHealChoices; - -typedef enum { - BR_CHOICE_HYPERBOSSES_NO, - BR_CHOICE_HYPERBOSSES_YES, -} BossRushHyperBossesChoices; - -typedef enum { - BR_CHOICE_MAGIC_SINGLE, - BR_CHOICE_MAGIC_DOUBLE, -} BossRushMagicChoices; - -typedef enum { - BR_CHOICE_BGS_NO, - BR_CHOICE_BGS_YES, -} BossRushBgsChoices; - -typedef enum { - BR_CHOICE_BOTTLE_NO, - BR_CHOICE_BOTTLE_EMPTY, - BR_CHOICE_BOTTLE_FAIRY, - BR_CHOICE_BOTTLE_REDPOTION, - BR_CHOICE_BOTTLE_GREENPOTION, - BR_CHOICE_BOTTLE_BLUEPOTION -} BossRushBottleChoices; - -typedef enum { - BR_CHOICE_LONGSHOT_NO, - BR_CHOICE_LONGSHOT_YES, -} BossRushLongshotChoices; - -typedef enum { - BR_CHOICE_HOVERBOOTS_NO, - BR_CHOICE_HOVERBOOTS_YES, -} BossRushHoverBootsChoices; - -typedef enum { - BR_CHOICE_BUNNYHOOD_NO, - BR_CHOICE_BUNNYHOOD_YES, -} BossRushBunnyHoodChoices; - -typedef enum { - BR_CHOICE_TIMER_YES, - BR_CHOICE_TIMER_NO, -} BossRushTimerChoices; diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 0ba910cf6..09defd088 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2225,6 +2225,20 @@ typedef enum { // #### `result` // ```c + // DoorWarp1_PlayerInRange(this, play) + // ``` + // #### `args` + // - `*DoorWarp1` + VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE, + + // #### `result` + // ```c + // (CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum + // <= 2) + // ``` + // #### `args` + // - `*PlayState` + VB_SHOW_GAMEPLAY_TIMER, // (this->dyna.actor.params >> 5 & 0x7F) == GI_ICE_TRAP && this->actionFunc == EnBox_Open && // this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100 // ``` diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index f276e19d3..2f56cadd6 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -6,7 +6,6 @@ #include "soh/SaveManager.h" #include "soh/ResourceManagerHelpers.h" #include "soh/resource/type/Skeleton.h" -#include "soh/Enhancements/boss-rush/BossRushTypes.h" #include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/randomizer/3drando/random.hpp" @@ -930,7 +929,6 @@ void RegisterCustomSkeletons() { } void InitMods() { - BossRush_RegisterHooks(); RandomizerRegisterHooks(); TimeSaverRegisterHooks(); RegisterTTS(); diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 0fc923070..f2713c8d6 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -8,7 +8,6 @@ #include "libultraship/bridge.h" #include "soh/Enhancements/gameplaystats.h" -#include "soh/Enhancements/boss-rush/BossRushTypes.h" #include "soh/Enhancements/custom-message/CustomMessageInterfaceAddon.h" #include "soh/Enhancements/cosmetics/cosmeticsTypes.h" #include "soh/Enhancements/enhancementTypes.h" @@ -6374,11 +6373,10 @@ void Interface_Draw(PlayState* play) { void Interface_DrawTotalGameplayTimer(PlayState* play) { // Draw timer based on the Gameplay Stats total time. - - if ((IS_BOSS_RUSH && gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_TIMER] == BR_CHOICE_TIMER_YES) || - (CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && - gSaveContext.fileNum <= 2)) { - + if (GameInteractor_Should(VB_SHOW_GAMEPLAY_TIMER, + CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && + gSaveContext.fileNum <= 2, + play)) { s32 X_Margins_Timer = 0; if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.UseMargins"), 0) != 0) { if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.PosType"), 0) == ORIGINAL_LOCATION) { diff --git a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c index 54e9768b4..2e0cd458f 100644 --- a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c +++ b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c @@ -1,7 +1,6 @@ #include "z_door_warp1.h" #include "objects/object_warp1/object_warp1.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h" -#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/OTRGlobals.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" @@ -693,12 +692,7 @@ void DoorWarp1_AdultWarpIdle(DoorWarp1* this, PlayState* play) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); - if (DoorWarp1_PlayerInRange(this, play)) { - // Heal player in Boss Rush - if (IS_BOSS_RUSH) { - BossRush_HandleBlueWarpHeal(play); - } - + if (GameInteractor_Should(VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE, DoorWarp1_PlayerInRange(this, play), this)) { player = GET_PLAYER(play); OnePointCutscene_Init(play, 0x25E8, 999, &this->actor, MAIN_CAM); diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 8fa504748..9e58f5e31 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -1398,110 +1398,6 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) { } } -static s8 sLastBossRushOptionIndex = -1; -static s8 sLastBossRushOptionValue = -1; - -void FileChoose_UpdateBossRushMenu(GameState* thisx) { - FileChoose_UpdateStickDirectionPromptAnim(thisx); - FileChooseContext* this = (FileChooseContext*)thisx; - Input* input = &this->state.input[0]; - bool dpad = CVarGetInteger(CVAR_SETTING("DpadInText"), 0); - - // Fade in elements after opening Boss Rush options menu - this->bossRushUIAlpha += 25; - if (this->bossRushUIAlpha > 255) { - this->bossRushUIAlpha = 255; - } - - // Animate up/down arrows. - this->bossRushArrowOffset += 1; - if (this->bossRushArrowOffset >= 30) { - this->bossRushArrowOffset = 0; - } - - // Move menu selection up or down. - if (ABS(this->stickRelY) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN | BTN_DUP))) { - // Move down - if (this->stickRelY < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN))) { - // When selecting past the last option, cycle back to the first option. - if ((this->bossRushIndex + 1) > BR_OPTIONS_MAX - 1) { - this->bossRushIndex = 0; - this->bossRushOffset = 0; - } else { - this->bossRushIndex++; - // When last visible option is selected when moving down, offset the list down by one. - if (this->bossRushIndex - this->bossRushOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN - 1) { - this->bossRushOffset++; - } - } - } else if (this->stickRelY > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DUP))) { - // When selecting past the first option, cycle back to the last option and offset the list to view it - // properly. - if ((this->bossRushIndex - 1) < 0) { - this->bossRushIndex = BR_OPTIONS_MAX - 1; - this->bossRushOffset = this->bossRushIndex - BOSSRUSH_MAX_OPTIONS_ON_SCREEN + 1; - } else { - // When first visible option is selected when moving up, offset the list up by one. - if (this->bossRushIndex - this->bossRushOffset == 0) { - this->bossRushOffset--; - } - this->bossRushIndex--; - } - } - - Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, - &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); - } - - // Cycle through choices for currently selected option. - if (ABS(this->stickRelX) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) { - if (this->stickRelX > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DRIGHT))) { - // If exceeding the amount of choices for the selected option, cycle back to the first. - if ((gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] + 1) == - BossRush_GetSettingOptionsAmount(this->bossRushIndex)) { - gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] = 0; - } else { - gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]++; - } - } else if (this->stickRelX < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT))) { - // If cycling back when already at the first choice for the selected option, cycle back to the last choice. - if ((gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] - 1) < 0) { - gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] = - BossRush_GetSettingOptionsAmount(this->bossRushIndex) - 1; - } else { - gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]--; - } - } - - Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, - &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); - } - - if (sLastBossRushOptionIndex != this->bossRushIndex || - sLastBossRushOptionValue != gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]) { - GameInteractor_ExecuteOnUpdateFileBossRushOptionSelection( - this->bossRushIndex, gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]); - sLastBossRushOptionIndex = this->bossRushIndex; - sLastBossRushOptionValue = gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]; - } - - if (CHECK_BTN_ALL(input->press.button, BTN_B)) { - this->configMode = CM_BOSS_RUSH_TO_QUEST; - return; - } - - // Load into the game. - if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) { - Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, - &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); - this->buttonIndex = 0xFE; - this->menuMode = FS_MENU_MODE_SELECT; - this->selectMode = SM_FADE_OUT; - this->prevConfigMode = this->configMode; - return; - } -} - void FileChoose_UpdateRandomizerMenu(GameState* thisx) { FileChoose_UpdateStickDirectionPromptAnim(thisx); FileChooseContext* this = (FileChooseContext*)thisx; @@ -2590,63 +2486,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) { break; } } else if (this->configMode == CM_BOSS_RUSH_MENU) { - uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language; - uint8_t listOffset = this->bossRushOffset; - uint8_t textAlpha = this->bossRushUIAlpha; - - // Draw arrows to indicate that the list can scroll up or down. - // Arrow up - if (listOffset > 0) { - uint16_t arrowUpX = 140; - uint16_t arrowUpY = 76 - (this->bossRushArrowOffset / 10); - gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowUpTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0, - G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, - G_TX_NOLOD, G_TX_NOLOD); - gSPWideTextureRectangle(POLY_OPA_DISP++, arrowUpX << 2, arrowUpY << 2, (arrowUpX + 8) << 2, - (arrowUpY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11)); - } - // Arrow down - if (BR_OPTIONS_MAX - listOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN) { - uint16_t arrowDownX = 140; - uint16_t arrowDownY = 181 + (this->bossRushArrowOffset / 10); - gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowDownTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0, - G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, - G_TX_NOLOD, G_TX_NOLOD); - gSPWideTextureRectangle(POLY_OPA_DISP++, arrowDownX << 2, arrowDownY << 2, (arrowDownX + 8) << 2, - (arrowDownY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11)); - } - - // Draw options. There's more options than what fits on the screen, so the visible options - // depend on the current offset of the list. Currently selected option pulses in - // color and has arrows surrounding the option. - for (uint8_t i = listOffset; i - listOffset < BOSSRUSH_MAX_OPTIONS_ON_SCREEN; i++) { - uint16_t textYOffset = (i - listOffset) * 16; - - // Option name. - Interface_DrawTextLine(this->state.gfxCtx, BossRush_GetSettingName(i, language), 65, (87 + textYOffset), - 255, 255, 80, textAlpha, 0.8f, true); - - // Selected choice for option. - uint16_t finalKerning = Interface_DrawTextLine( - this->state.gfxCtx, - BossRush_GetSettingChoiceName(i, gSaveContext.ship.quest.data.bossRush.options[i], language), 165, - (87 + textYOffset), 255, 255, 255, textAlpha, 0.8f, true); - - // Draw arrows around selected option. - if (this->bossRushIndex == i) { - Gfx_SetupDL_39Opa(this->state.gfxCtx); - gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); - gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0, - G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD, - G_TX_NOLOD); - FileChoose_DrawTextRec(this->state.gfxCtx, this->stickLeftPrompt.arrowColorR, - this->stickLeftPrompt.arrowColorG, this->stickLeftPrompt.arrowColorB, textAlpha, - 160, (92 + textYOffset), 0.42f, 0, 0, -1.0f, 1.0f); - FileChoose_DrawTextRec(this->state.gfxCtx, this->stickRightPrompt.arrowColorR, - this->stickRightPrompt.arrowColorG, this->stickRightPrompt.arrowColorB, - textAlpha, (171 + finalKerning), (92 + textYOffset), 0.42f, 0, 0, 1.0f, 1.0f); - } - } + FileChoose_DrawBossRushMenuWindowContents(this); } else if (this->configMode == CM_RANDOMIZER_SETTINGS_MENU) { uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language; uint8_t textAlpha = this->randomizerUIAlpha;