From 6d6c1c8c328eaaac957dcac1ef094259097a0085 Mon Sep 17 00:00:00 2001 From: Ralphie Morell Date: Wed, 25 Jan 2023 14:32:21 -0500 Subject: [PATCH] Enhancement: Better Farore's Wind (#2374) * Updated SaveManager, SaveContext; lifted restriction from gtg and ganon's castle; disabled clearing fw during master sword lift; added checkbox * increment load version (whoops); clear age fw after use * beef up fw console handler (for testing) * Simplify better FW (2 copies > 1 copy) * Apply suggestions * whoops --- soh/include/z64save.h | 1 + soh/soh/Enhancements/debugconsole.cpp | 54 ++++++++++++++++--- soh/soh/GameMenuBar.cpp | 2 + soh/soh/SaveManager.cpp | 29 ++++++++++ soh/src/code/z_demo.c | 10 +++- soh/src/code/z_parameter.c | 5 ++ .../actors/ovl_player_actor/z_player.c | 4 +- 7 files changed, 94 insertions(+), 11 deletions(-) diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 3327bc4bd..7d409485c 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -253,6 +253,7 @@ typedef struct { /* */ u8 pendingIceTrapCount; /* */ SohStats sohStats; /* */ u8 temporaryWeapon; + /* */ FaroresWindData backupFW; // #endregion // #region SOH [Randomizer] // Upstream TODO: Move these to their own struct or name to more obviously specific to Randomizer diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index 6b27fba1d..40cd841d7 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -389,14 +389,52 @@ static bool ReloadHandler(std::shared_ptr Console, const std::vec return CMD_SUCCESS; } +const static std::map fw_options { + { "clear", 0}, {"warp", 1}, {"nackup", 2} +}; + static bool FWHandler(std::shared_ptr Console, const std::vector& args) { + if (args.size() != 2) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + + const auto& it = fw_options.find(args[1]); + if (it == fw_options.end()) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Invalid option. Options are 'clear', 'warp', 'backup'"); + return CMD_FAILED; + } + if (gPlayState != nullptr) { - if (gSaveContext.respawn[RESPAWN_MODE_TOP].data > 0) { - gPlayState->sceneLoadFlag = 0x14; - gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex; - gPlayState->fadeTransition = 5; - } else { - SohImGui::GetConsole()->SendErrorMessage("Farore's wind not set!"); + FaroresWindData clear = {}; + switch(it->second) { + case 0: //clear + gSaveContext.fw = clear; + SohImGui::GetConsole()->SendInfoMessage("[SOH] Farore's wind point cleared! Reload scene to take effect."); + return CMD_SUCCESS; + break; + case 1: //warp + if (gSaveContext.respawn[RESPAWN_MODE_TOP].data > 0) { + gPlayState->sceneLoadFlag = 0x14; + gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex; + gPlayState->fadeTransition = 5; + } else { + SohImGui::GetConsole()->SendErrorMessage("Farore's wind not set!"); + return CMD_FAILED; + } + return CMD_SUCCESS; + break; + case 2: //backup + if (CVarGetInteger("gBetterFW", 0)) { + gSaveContext.fw = gSaveContext.backupFW; + gSaveContext.fw.set = 1; + SohImGui::GetConsole()->SendInfoMessage("[SOH] Backup FW data copied! Reload scene to take effect."); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendErrorMessage("Better Farore's Wind isn't turned on!"); + return CMD_FAILED; + } + break; } } else { @@ -1046,7 +1084,9 @@ void DebugConsole_Init(void) { // Map & Location CMD_REGISTER("void", { VoidHandler, "Voids out of the current map." }); CMD_REGISTER("reload", { ReloadHandler, "Reloads the current map." }); - CMD_REGISTER("fw", { FWHandler,"Spawns the player where Farore's Wind is set." }); + CMD_REGISTER("fw", { FWHandler,"Spawns the player where Farore's Wind is set." , { + { "clear|warp|backup", Ship::ArgumentType::TEXT } + }}); CMD_REGISTER("entrance", { EntranceHandler, "Sends player to the entered entrance (hex)", { { "entrance", Ship::ArgumentType::NUMBER } }}); diff --git a/soh/soh/GameMenuBar.cpp b/soh/soh/GameMenuBar.cpp index d8800b27d..9862a0d2b 100644 --- a/soh/soh/GameMenuBar.cpp +++ b/soh/soh/GameMenuBar.cpp @@ -400,6 +400,8 @@ namespace GameMenuBar { UIWidgets::Tooltip("Makes nuts explode bombs, similar to how they interact with bombchus. This does not affect bombflowers."); UIWidgets::PaddedEnhancementCheckbox("Equip Multiple Arrows at Once", "gSeparateArrows", true, false); UIWidgets::Tooltip("Allow the bow and magic arrows to be equipped at the same time on different slots"); + UIWidgets::PaddedEnhancementCheckbox("Better Farore's Wind", "gBetterFW", true, false); + UIWidgets::Tooltip("Helps FW persist between ages, gives child and adult separate FW points, and can be used in more places."); UIWidgets::PaddedEnhancementCheckbox("Static Explosion Radius", "gStaticExplosionRadius", true, false); UIWidgets::Tooltip("Explosions are now a static size, like in Majora's Mask and OoT3D. Makes bombchu hovering much easier."); ImGui::EndMenu(); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index ee5eb8b95..b2e2905ed 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -502,6 +502,7 @@ void SaveManager::InitFileNormal() { gSaveContext.fw.set = 0; gSaveContext.fw.tempSwchFlags = 0; gSaveContext.fw.tempCollectFlags = 0; + gSaveContext.backupFW = gSaveContext.fw; for (int flag = 0; flag < ARRAY_COUNT(gSaveContext.gsFlags); flag++) { gSaveContext.gsFlags[flag] = 0; } @@ -1334,6 +1335,20 @@ void SaveManager::LoadBaseVersion3() { SaveManager::Instance->LoadData("", gSaveContext.randomizerInf[i]); }); SaveManager::Instance->LoadData("isMasterQuest", gSaveContext.isMasterQuest); + SaveManager::Instance->LoadStruct("backupFW", []() { + SaveManager::Instance->LoadStruct("pos", []() { + SaveManager::Instance->LoadData("x", gSaveContext.backupFW.pos.x); + SaveManager::Instance->LoadData("y", gSaveContext.backupFW.pos.y); + SaveManager::Instance->LoadData("z", gSaveContext.backupFW.pos.z); + }); + SaveManager::Instance->LoadData("yaw", gSaveContext.backupFW.yaw); + SaveManager::Instance->LoadData("playerParams", gSaveContext.backupFW.playerParams); + SaveManager::Instance->LoadData("entranceIndex", gSaveContext.backupFW.entranceIndex); + SaveManager::Instance->LoadData("roomIndex", gSaveContext.backupFW.roomIndex); + SaveManager::Instance->LoadData("set", gSaveContext.backupFW.set); + SaveManager::Instance->LoadData("tempSwchFlags", gSaveContext.backupFW.tempSwchFlags); + SaveManager::Instance->LoadData("tempCollectFlags", gSaveContext.backupFW.tempCollectFlags); + }); SaveManager::Instance->LoadData("dogParams", gSaveContext.dogParams); } @@ -1510,6 +1525,20 @@ void SaveManager::SaveBase() { SaveManager::Instance->SaveData("", gSaveContext.randomizerInf[i]); }); SaveManager::Instance->SaveData("isMasterQuest", gSaveContext.isMasterQuest); + SaveManager::Instance->SaveStruct("backupFW", []() { + SaveManager::Instance->SaveStruct("pos", []() { + SaveManager::Instance->SaveData("x", gSaveContext.backupFW.pos.x); + SaveManager::Instance->SaveData("y", gSaveContext.backupFW.pos.y); + SaveManager::Instance->SaveData("z", gSaveContext.backupFW.pos.z); + }); + SaveManager::Instance->SaveData("yaw", gSaveContext.backupFW.yaw); + SaveManager::Instance->SaveData("playerParams", gSaveContext.backupFW.playerParams); + SaveManager::Instance->SaveData("entranceIndex", gSaveContext.backupFW.entranceIndex); + SaveManager::Instance->SaveData("roomIndex", gSaveContext.backupFW.roomIndex); + SaveManager::Instance->SaveData("set", gSaveContext.backupFW.set); + SaveManager::Instance->SaveData("tempSwchFlags", gSaveContext.backupFW.tempSwchFlags); + SaveManager::Instance->SaveData("tempCollectFlags", gSaveContext.backupFW.tempCollectFlags); + }); SaveManager::Instance->SaveData("dogParams", gSaveContext.dogParams); } diff --git a/soh/src/code/z_demo.c b/soh/src/code/z_demo.c index b8eab98b2..a00d018d6 100644 --- a/soh/src/code/z_demo.c +++ b/soh/src/code/z_demo.c @@ -616,8 +616,14 @@ void Cutscene_Command_Terminator(PlayState* play, CutsceneContext* csCtx, CsCmdB play->fadeTransition = 11; break; case 8: - gSaveContext.fw.set = 0; - gSaveContext.respawn[RESPAWN_MODE_TOP].data = 0; + if (CVarGetInteger("gBetterFW", 0)) { + FaroresWindData tempFW = gSaveContext.backupFW; + gSaveContext.backupFW = gSaveContext.fw; + gSaveContext.fw = tempFW; + } else { + gSaveContext.fw.set = 0; + gSaveContext.respawn[RESPAWN_MODE_TOP].data = 0; + } if (!(gSaveContext.eventChkInf[4] & 0x20)) { gSaveContext.eventChkInf[4] |= 0x20; play->nextEntranceIndex = 0x00A0; diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index d82b4922f..c7ec3b46f 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -1371,6 +1371,11 @@ void Interface_SetSceneRestrictions(PlayState* play) { interfaceCtx->restrictions.farores, interfaceCtx->restrictions.dinsNayrus, interfaceCtx->restrictions.all); osSyncPrintf(VT_RST); + if (CVarGetInteger("gBetterFW", 0)) { + if (currentScene == SCENE_MEN || currentScene == SCENE_GANONTIKA) { + interfaceCtx->restrictions.farores = 0; + } + } return; } i++; 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 3712d359f..71501754b 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -13716,7 +13716,7 @@ void func_8085063C(Player* this, PlayState* play) { if (Message_GetState(&play->msgCtx) == TEXT_STATE_CLOSING) { s32 respawnData = gSaveContext.respawn[RESPAWN_MODE_TOP].data; - if (play->msgCtx.choiceIndex == 0) { + if (play->msgCtx.choiceIndex == 0) { //Returns to FW gSaveContext.respawnFlag = 3; play->sceneLoadFlag = 0x14; play->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex; @@ -13725,7 +13725,7 @@ void func_8085063C(Player* this, PlayState* play) { return; } - if (play->msgCtx.choiceIndex == 1) { + if (play->msgCtx.choiceIndex == 1) { //Unsets FW gSaveContext.respawn[RESPAWN_MODE_TOP].data = -respawnData; gSaveContext.fw.set = 0; func_80078914(&gSaveContext.respawn[RESPAWN_MODE_TOP].pos, NA_SE_PL_MAGIC_WIND_VANISH);