diff --git a/soh/soh/Enhancements/Fixes/DekuNutUpgradeFix.cpp b/soh/soh/Enhancements/Fixes/DekuNutUpgradeFix.cpp new file mode 100644 index 000000000..2e095c59f --- /dev/null +++ b/soh/soh/Enhancements/Fixes/DekuNutUpgradeFix.cpp @@ -0,0 +1,43 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" +#include "functions.h" +#include "macros.h" +#include "variables.h" +#include "z64save.h" + +extern "C" PlayState* gPlayState; + +static constexpr int32_t CVAR_NUT_UPGRADE_FIX_DEFAULT = 0; +#define CVAR_NUT_UPGRADE_FIX_NAME CVAR_ENHANCEMENT("DekuNutUpgradeFix") +#define CVAR_NUT_UPGRADE_FIX_VALUE CVarGetInteger(CVAR_NUT_UPGRADE_FIX_NAME, CVAR_NUT_UPGRADE_FIX_DEFAULT) + +void DekuNutUpgradeFixAtForestStage(bool* should) { + // This check is needed because of an intentional fallthrough at the source + if (Player_GetMask(gPlayState) == PLAYER_MASK_SKULL) { + return; + } + + s32 expectedNutUpgrades = (INV_CONTENT(ITEM_NUT) == ITEM_NUT ? 1 : 0) + + (Flags_GetInfTable(INFTABLE_BOUGHT_NUT_UPGRADE) ? 1 : 0) + + (Flags_GetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE) ? 1 : 0); + s32 actualNutUpgrades = CUR_UPG_VALUE(UPG_NUTS); + + if (expectedNutUpgrades != actualNutUpgrades) { + Flags_UnsetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE); + *should = true; + } +} + +void DekuNutUpgradeSetByPoachersSaw(bool* should) { + *should = false; +} + +void RegisterDekuNutUpgradeFix() { + COND_VB_SHOULD(VB_POACHERS_SAW_SET_DEKU_NUT_UPGRADE_FLAG, CVAR_NUT_UPGRADE_FIX_VALUE || IS_RANDO, + { DekuNutUpgradeSetByPoachersSaw(should); }); + COND_VB_SHOULD(VB_DEKU_SCRUBS_REACT_TO_MASK_OF_TRUTH, CVAR_NUT_UPGRADE_FIX_VALUE && !IS_RANDO, + { DekuNutUpgradeFixAtForestStage(should); }); +} + +static RegisterShipInitFunc initFunc(RegisterDekuNutUpgradeFix, { CVAR_NUT_UPGRADE_FIX_NAME, "IS_RANDO" }); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index a9118c776..1157f3c75 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -287,6 +287,14 @@ typedef enum { // - `*EnTk` VB_DAMPE_IN_GRAVEYARD_DESPAWN, + // #### `result` + // ```c + // !Flags_GetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE) && (Player_GetMask(play) != PLAYER_MASK_SKULL) + // ``` + // #### `args` + // - None + VB_DEKU_SCRUBS_REACT_TO_MASK_OF_TRUTH, + // #### `result` // ```c // CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) @@ -1500,6 +1508,14 @@ typedef enum { // - `*DemoIm` VB_PLAY_ZELDAS_LULLABY_CS, + // #### `result` + // ```c + // item == ITEM_SAW + // ``` + // #### `args` + // - None + VB_POACHERS_SAW_SET_DEKU_NUT_UPGRADE_FLAG, + // #### `result` // ```c // (dropParams >= ITEM00_RUPEE_GREEN) && (dropParams <= ITEM00_BOMBS_SPECIAL) diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index d4675621f..eee2a0e9b 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -92,7 +92,9 @@ void EnDntDemo_JudgeSkipToReward(EnDntDemo* enDntDemo, PlayState* play) { return; } case PLAYER_MASK_TRUTH: { - Flags_SetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE); + if (GameInteractor_Should(VB_DEKU_SCRUBS_REACT_TO_MASK_OF_TRUTH, true)) { + Flags_SetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE); + } return; } default: { diff --git a/soh/soh/SohGui/SohMenuBar.cpp b/soh/soh/SohGui/SohMenuBar.cpp index 053ed9e13..5fd7324fb 100644 --- a/soh/soh/SohGui/SohMenuBar.cpp +++ b/soh/soh/SohGui/SohMenuBar.cpp @@ -1335,7 +1335,10 @@ void DrawEnhancementsMenu() { UIWidgets::PaddedEnhancementCheckbox("Fix the Gravedigging Tour Glitch", CVAR_ENHANCEMENT("GravediggingTourFix"), true, false, SaveManager::Instance->IsRandoFile(), "This setting is always enabled in randomizer files", UIWidgets::CheckboxGraphics::Checkmark); UIWidgets::Tooltip("Fixes a bug where the Gravedigging Tour Heart Piece disappears if the area reloads"); - UIWidgets::PaddedEnhancementCheckbox("Fix Deku Nut upgrade", CVAR_ENHANCEMENT("DekuNutUpgradeFix"), true, false); + UIWidgets::PaddedEnhancementCheckbox( + "Fix Deku Nut upgrade", CVAR_ENHANCEMENT("DekuNutUpgradeFix"), true, false, IS_RANDO, + "This setting is forcefully enabled when you are playing a randomizer.", + UIWidgets::CheckboxGraphics::Checkmark); UIWidgets::Tooltip("Prevents the Forest Stage Deku Nut upgrade from becoming unobtainable after receiving the Poacher's Saw"); UIWidgets::PaddedEnhancementCheckbox("Fix Navi text HUD position", CVAR_ENHANCEMENT("NaviTextFix"), true, false); UIWidgets::Tooltip("Correctly centers the Navi text prompt on the HUD's C-Up button"); diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index a696292ef..a331e4ace 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2442,7 +2442,7 @@ u8 Item_Give(PlayState* play, u8 item) { } return Return_Item(item, MOD_NONE, ITEM_NONE); } else if ((item >= ITEM_WEIRD_EGG) && (item <= ITEM_CLAIM_CHECK)) { - if ((item == ITEM_SAW) && CVarGetInteger(CVAR_ENHANCEMENT("DekuNutUpgradeFix"), 0) == 0) { + if (GameInteractor_Should(VB_POACHERS_SAW_SET_DEKU_NUT_UPGRADE_FLAG, item == ITEM_SAW)) { Flags_SetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE); } diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c b/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c index f9328d729..05cda9db4 100644 --- a/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c +++ b/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c @@ -10,6 +10,7 @@ #include "overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.h" #include "vt.h" #include "soh/OTRGlobals.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -165,7 +166,9 @@ void EnDntDemo_Judge(EnDntDemo* this, PlayState* play) { break; } case PLAYER_MASK_TRUTH: - if (!Flags_GetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE) && (Player_GetMask(play) != PLAYER_MASK_SKULL)) { + if (GameInteractor_Should(VB_DEKU_SCRUBS_REACT_TO_MASK_OF_TRUTH, + !Flags_GetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE) && + (Player_GetMask(play) != PLAYER_MASK_SKULL))) { Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); this->prize = DNT_PRIZE_NUTS;