Use VB hook to fix Deku Nut upgrade bug (#5047)

* Use VB hook to fix Deku Nut upgrade bug

* Use constexpr, remove unused extern

* Separate IS_RANDO out of value macro

* Restore mask check in hook

* Call VB hook in SkipMiscInteractions hook

* Mask of Truth hook not registered in rando
This commit is contained in:
Jordan Longstaff 2025-02-19 20:22:56 -05:00 committed by GitHub
parent 75f33a00d8
commit b6e2a995f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 71 additions and 4 deletions

View File

@ -0,0 +1,43 @@
#include <libultraship/bridge.h>
#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" });

View File

@ -287,6 +287,14 @@ typedef enum {
// - `*EnTk` // - `*EnTk`
VB_DAMPE_IN_GRAVEYARD_DESPAWN, 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` // #### `result`
// ```c // ```c
// CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) // CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)
@ -1500,6 +1508,14 @@ typedef enum {
// - `*DemoIm` // - `*DemoIm`
VB_PLAY_ZELDAS_LULLABY_CS, VB_PLAY_ZELDAS_LULLABY_CS,
// #### `result`
// ```c
// item == ITEM_SAW
// ```
// #### `args`
// - None
VB_POACHERS_SAW_SET_DEKU_NUT_UPGRADE_FLAG,
// #### `result` // #### `result`
// ```c // ```c
// (dropParams >= ITEM00_RUPEE_GREEN) && (dropParams <= ITEM00_BOMBS_SPECIAL) // (dropParams >= ITEM00_RUPEE_GREEN) && (dropParams <= ITEM00_BOMBS_SPECIAL)

View File

@ -92,7 +92,9 @@ void EnDntDemo_JudgeSkipToReward(EnDntDemo* enDntDemo, PlayState* play) {
return; return;
} }
case PLAYER_MASK_TRUTH: { case PLAYER_MASK_TRUTH: {
if (GameInteractor_Should(VB_DEKU_SCRUBS_REACT_TO_MASK_OF_TRUTH, true)) {
Flags_SetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE); Flags_SetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE);
}
return; return;
} }
default: { default: {

View File

@ -1335,7 +1335,10 @@ void DrawEnhancementsMenu() {
UIWidgets::PaddedEnhancementCheckbox("Fix the Gravedigging Tour Glitch", CVAR_ENHANCEMENT("GravediggingTourFix"), true, false, SaveManager::Instance->IsRandoFile(), 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); "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::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::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::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"); UIWidgets::Tooltip("Correctly centers the Navi text prompt on the HUD's C-Up button");

View File

@ -2442,7 +2442,7 @@ u8 Item_Give(PlayState* play, u8 item) {
} }
return Return_Item(item, MOD_NONE, ITEM_NONE); return Return_Item(item, MOD_NONE, ITEM_NONE);
} else if ((item >= ITEM_WEIRD_EGG) && (item <= ITEM_CLAIM_CHECK)) { } 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); Flags_SetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE);
} }

View File

@ -10,6 +10,7 @@
#include "overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.h" #include "overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.h"
#include "vt.h" #include "vt.h"
#include "soh/OTRGlobals.h" #include "soh/OTRGlobals.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS 0 #define FLAGS 0
@ -165,7 +166,9 @@ void EnDntDemo_Judge(EnDntDemo* this, PlayState* play) {
break; break;
} }
case PLAYER_MASK_TRUTH: 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, Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultReverb); &gSfxDefaultReverb);
this->prize = DNT_PRIZE_NUTS; this->prize = DNT_PRIZE_NUTS;