summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Longstaff <JordanLongstaff@users.noreply.github.com>2026-07-24 12:28:36 -0400
committerGitHub <noreply@github.com>2026-07-24 16:28:36 +0000
commit9d1e5d281100c2d3ab40541759041e2c912ae154 (patch)
treeca617a5dcd4767e6d9548bb00930fbbdf5f4324a
parent44909ab230d80a35067e1acf86ae60f2482edd44 (diff)
Hookify Lost Woods Ocarina game (#6976)
-rw-r--r--soh/soh/Enhancements/Minigames/LostWoodsOcarinaGame.cpp105
-rw-r--r--soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h41
-rw-r--r--soh/src/code/code_800EC960.c56
-rw-r--r--soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c24
4 files changed, 164 insertions, 62 deletions
diff --git a/soh/soh/Enhancements/Minigames/LostWoodsOcarinaGame.cpp b/soh/soh/Enhancements/Minigames/LostWoodsOcarinaGame.cpp
new file mode 100644
index 000000000..18f926c3d
--- /dev/null
+++ b/soh/soh/Enhancements/Minigames/LostWoodsOcarinaGame.cpp
@@ -0,0 +1,105 @@
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
+#include "soh/ShipInit.hpp"
+
+extern "C" {
+#include "functions.h"
+#include "variables.h"
+#include "src/overlays/actors/ovl_En_Skj/z_en_skj.h"
+extern PlayState* gPlayState;
+
+void EnSkj_WaitForPlayback(EnSkj* enSkj, PlayState* play);
+}
+
+typedef enum {
+ OCARINA_GAME_STARTING_NOTES = 3,
+ OCARINA_GAME_ROUND_ONE_NOTES = 5,
+ OCARINA_GAME_ROUND_TWO_NOTES = 6,
+ OCARINA_GAME_ROUND_THREE_NOTES = 8,
+} OcarinaGameDefaults;
+
+#define CVAR_CUSTOMIZE_NAME CVAR_ENHANCEMENT("CustomizeOcarinaGame")
+#define CVAR_CUSTOMIZE_VALUE CVarGetInteger(CVAR_CUSTOMIZE_NAME, 0)
+
+#define CVAR_STARTING_NOTES_NAME CVAR_ENHANCEMENT("OcarinaGame.StartingNotes")
+#define CVAR_STARTING_NOTES_VALUE CVarGetInteger(CVAR_STARTING_NOTES_NAME, OCARINA_GAME_STARTING_NOTES)
+
+#define CVAR_ROUND_ONE_NOTES_NAME CVAR_ENHANCEMENT("OcarinaGame.RoundOneNotes")
+#define CVAR_ROUND_ONE_NOTES_VALUE CVarGetInteger(CVAR_ROUND_ONE_NOTES_NAME, OCARINA_GAME_ROUND_ONE_NOTES)
+
+#define CVAR_ROUND_TWO_NOTES_NAME CVAR_ENHANCEMENT("OcarinaGame.RoundTwoNotes")
+#define CVAR_ROUND_TWO_NOTES_VALUE CVarGetInteger(CVAR_ROUND_TWO_NOTES_NAME, OCARINA_GAME_ROUND_TWO_NOTES)
+
+#define CVAR_ROUND_THREE_NOTES_NAME CVAR_ENHANCEMENT("OcarinaGame.RoundThreeNotes")
+#define CVAR_ROUND_THREE_NOTES_VALUE CVarGetInteger(CVAR_ROUND_THREE_NOTES_NAME, OCARINA_GAME_ROUND_THREE_NOTES)
+
+#define CVAR_NOTE_SPEED_NAME CVAR_ENHANCEMENT("OcarinaGame.NoteSpeed")
+#define CVAR_NOTE_SPEED_VALUE CVarGetInteger(CVAR_NOTE_SPEED_NAME, 1)
+
+#define CVAR_INFINITE_TIME_NAME CVAR_ENHANCEMENT("OcarinaUnlimitedFailTime")
+#define CVAR_INFINITE_TIME_VALUE CVarGetInteger(CVAR_INFINITE_TIME_NAME, 0)
+
+#define CVAR_INSTANT_WIN_NAME CVAR_ENHANCEMENT("InstantOcarinaGameWin")
+#define CVAR_INSTANT_WIN_VALUE CVarGetInteger(CVAR_INSTANT_WIN_NAME, 0)
+
+static void RegisterLostWoodsOcarinaGameRoundNotesSetup() {
+ COND_VB_SHOULD(VB_SET_LOST_WOODS_OCARINA_GAME_NOTES, CVAR_CUSTOMIZE_VALUE, {
+ s32 minigameRound = va_arg(args, s32);
+ u8* roundNotes = va_arg(args, u8*);
+
+ switch (minigameRound) {
+ case 0:
+ *roundNotes = CVAR_ROUND_ONE_NOTES_VALUE;
+ break;
+ case 1:
+ *roundNotes = CVAR_ROUND_TWO_NOTES_VALUE;
+ break;
+ default:
+ *roundNotes = CVAR_ROUND_THREE_NOTES_VALUE;
+ break;
+ }
+
+ *should = false;
+ });
+}
+
+static void RegisterLostWoodsOcarinaGameStartingNotesSetup() {
+ COND_VB_SHOULD(VB_SET_LOST_WOODS_OCARINA_GAME_STARTING_NOTES,
+ CVAR_CUSTOMIZE_VALUE && (CVAR_STARTING_NOTES_VALUE != OCARINA_GAME_STARTING_NOTES), {
+ for (u8 i = 0; i < CVAR_STARTING_NOTES_VALUE; i++) {
+ AudioOcarina_MemoryGameNextNote();
+ }
+ *should = false;
+ });
+}
+
+static void RegisterLostWoodsOcarinaGameModifyNoteSpeed() {
+ COND_VB_SHOULD(VB_MODIFY_LOST_WOODS_OCARINA_GAME_NOTE_SPEED, CVAR_CUSTOMIZE_VALUE && (CVAR_NOTE_SPEED_VALUE != 1), {
+ s32 appendPos = va_arg(args, s32);
+ sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][appendPos].length /= CVAR_NOTE_SPEED_VALUE;
+ });
+}
+
+static void RegisterLostWoodsOcarinaGameInfiniteTime() {
+ COND_VB_SHOULD(VB_LOST_WOODS_OCARINA_GAME_TIMER_TICK, CVAR_CUSTOMIZE_VALUE && CVAR_INFINITE_TIME_VALUE,
+ { *should = false; });
+}
+
+static void RegisterLostWoodsOcarinaGameInstantWin() {
+ COND_VB_SHOULD(VB_PLAY_LOST_WOODS_OCARINA_GAME, CVAR_CUSTOMIZE_VALUE && CVAR_INSTANT_WIN_VALUE, {
+ EnSkj* enSkj = va_arg(args, EnSkj*);
+ gPlayState->msgCtx.ocarinaMode = OCARINA_MODE_0F;
+ enSkj->multiuseTimer = 160;
+ enSkj->actionFunc = EnSkj_WaitForPlayback;
+ *should = false;
+ });
+}
+
+static RegisterShipInitFunc initFunc_Notes(RegisterLostWoodsOcarinaGameRoundNotesSetup, { CVAR_CUSTOMIZE_NAME });
+static RegisterShipInitFunc initFunc_StartingNotes(RegisterLostWoodsOcarinaGameStartingNotesSetup,
+ { CVAR_CUSTOMIZE_NAME, CVAR_STARTING_NOTES_NAME });
+static RegisterShipInitFunc initFunc_ModifyTime(RegisterLostWoodsOcarinaGameModifyNoteSpeed,
+ { CVAR_CUSTOMIZE_NAME, CVAR_NOTE_SPEED_NAME });
+static RegisterShipInitFunc initFunc_InfiniteTime(RegisterLostWoodsOcarinaGameInfiniteTime,
+ { CVAR_CUSTOMIZE_NAME, CVAR_INFINITE_TIME_NAME });
+static RegisterShipInitFunc initFunc_InstantWin(RegisterLostWoodsOcarinaGameInstantWin,
+ { CVAR_CUSTOMIZE_NAME, CVAR_INSTANT_WIN_NAME }); \ No newline at end of file
diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
index ceeb4068c..58c60ba67 100644
--- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
+++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
@@ -1557,6 +1557,14 @@ typedef enum {
// true
// ```
// #### `args`
+ // - None
+ VB_LOST_WOODS_OCARINA_GAME_TIMER_TICK,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
// - `*EnMs`
VB_MAGIC_BEAN_SALESMAN_TAKE_MONEY,
@@ -1602,6 +1610,14 @@ typedef enum {
// #### `result`
// ```c
+ // false
+ // ```
+ // #### `args`
+ // - `s32` (note append position)
+ VB_MODIFY_LOST_WOODS_OCARINA_GAME_NOTE_SPEED,
+
+ // #### `result`
+ // ```c
// this->interactInfo.talkState == NPC_TALK_STATE_ACTION
// ```
// #### `args`
@@ -1938,6 +1954,14 @@ typedef enum {
// true
// ```
// #### `args`
+ // - `*EnSkj`
+ VB_PLAY_LOST_WOODS_OCARINA_GAME,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
// - None
VB_PLAY_MINUET_OF_FOREST_CS,
@@ -2418,6 +2442,23 @@ typedef enum {
// true
// ```
// #### `args`
+ // - `s32` (round number, zero-based)
+ // - `*u8` (note end position)
+ VB_SET_LOST_WOODS_OCARINA_GAME_NOTES,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
+ // - None
+ VB_SET_LOST_WOODS_OCARINA_GAME_STARTING_NOTES,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
// - `*s32` (ammo count)
VB_SET_SHOOTING_GALLERY_AMMO,
diff --git a/soh/src/code/code_800EC960.c b/soh/src/code/code_800EC960.c
index 402e5c822..d1ec266d6 100644
--- a/soh/src/code/code_800EC960.c
+++ b/soh/src/code/code_800EC960.c
@@ -2403,35 +2403,16 @@ void AudioOcarina_RecordSong(void) {
void AudioOcarina_MemoryGameInit(u8 minigameRound) {
u8 i;
- // #region SOH [Enhancement]
- if (CVarGetInteger(CVAR_ENHANCEMENT("CustomizeOcarinaGame"), 0)) {
- u8 startingNotes = 3;
- u8 roundOneCount = CVarGetInteger(CVAR_ENHANCEMENT("OcarinaGame.RoundOneNotes"), 5);
- u8 roundTwoCount = CVarGetInteger(CVAR_ENHANCEMENT("OcarinaGame.RoundTwoNotes"), 6);
- u8 roundThreeCount = CVarGetInteger(CVAR_ENHANCEMENT("OcarinaGame.RoundThreeNotes"), 8);
- u8 modMinigameNoteCnts[] = { roundOneCount, roundTwoCount, roundThreeCount };
-
- startingNotes = CVarGetInteger(CVAR_ENHANCEMENT("OcarinaGame.StartingNotes"), 3);
-
- if (minigameRound > 2) {
- minigameRound = 2;
- }
-
- sOcaMemoryGameAppendPos = 0;
- sOcaMemoryGameEndPos = modMinigameNoteCnts[minigameRound];
-
- for (i = 0; i < startingNotes; i++) {
- AudioOcarina_MemoryGameNextNote();
- }
- // #endregion
- } else {
- if (minigameRound > 2) {
- minigameRound = 2;
- }
+ if (minigameRound > 2) {
+ minigameRound = 2;
+ }
- sOcaMemoryGameAppendPos = 0;
+ sOcaMemoryGameAppendPos = 0;
+ if (GameInteractor_Should(VB_SET_LOST_WOODS_OCARINA_GAME_NOTES, true, minigameRound, &sOcaMemoryGameEndPos)) {
sOcaMemoryGameEndPos = sOcaMemoryGameNumNotes[minigameRound];
+ }
+ if (GameInteractor_Should(VB_SET_LOST_WOODS_OCARINA_GAME_STARTING_NOTES, true)) {
for (i = 0; i < 3; i++) {
AudioOcarina_MemoryGameNextNote();
}
@@ -2453,24 +2434,13 @@ s32 AudioOcarina_MemoryGameNextNote(void) {
randomPitch = sButtonToPitchMap[(randomButtonIndex + 1) % 5];
}
- // #region SOH [Enhancement]
- if (CVarGetInteger(CVAR_ENHANCEMENT("CustomizeOcarinaGame"), 0)) {
- int noteSpeed = 0x2D;
- noteSpeed = noteSpeed / CVarGetInteger(CVAR_ENHANCEMENT("OcarinaGame.NoteSpeed"), 1);
+ sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].pitch = randomPitch;
+ sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].length = 0x2D;
+ sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].volume = 0x50;
+ sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].vibrato = 0;
+ sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].bend = 0;
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].pitch = randomPitch;
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].length = noteSpeed;
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].volume = 0x50;
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].vibrato = 0;
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].bend = 0;
- // #endregion
- } else {
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].pitch = randomPitch;
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].length = 0x2D;
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].volume = 0x50;
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].vibrato = 0;
- sOcarinaSongNotes[OCARINA_SONG_MEMORY_GAME][sOcaMemoryGameAppendPos].bend = 0;
- }
+ GameInteractor_Should(VB_MODIFY_LOST_WOODS_OCARINA_GAME_NOTE_SPEED, false, sOcaMemoryGameAppendPos);
sOcaMemoryGameAppendPos++;
diff --git a/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c
index 8a0e41ec4..507bdfdac 100644
--- a/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c
+++ b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c
@@ -1414,21 +1414,12 @@ void EnSkj_StartOcarinaMinigame(EnSkj* this, PlayState* play) {
EnSkj_TurnPlayer(this, player);
- if (dialogState == TEXT_STATE_CLOSING) {
- // #region SOH [Enhancement]
- if (CVarGetInteger(CVAR_ENHANCEMENT("InstantOcarinaGameWin"), 0) &&
- CVarGetInteger(CVAR_ENHANCEMENT("CustomizeOcarinaGame"), 0)) {
- play->msgCtx.ocarinaMode = OCARINA_MODE_0F;
+ if (dialogState == TEXT_STATE_CLOSING && GameInteractor_Should(VB_PLAY_LOST_WOODS_OCARINA_GAME, true, this)) {
+ func_8010BD58(play, OCARINA_ACTION_MEMORY_GAME);
+ if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) {
+ sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState = SKULL_KID_OCARINA_PLAY_NOTES;
this->songFailTimer = 160;
this->actionFunc = EnSkj_WaitForPlayback;
- // #endregion
- } else {
- func_8010BD58(play, OCARINA_ACTION_MEMORY_GAME);
- if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) {
- sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState = SKULL_KID_OCARINA_PLAY_NOTES;
- this->songFailTimer = 160;
- this->actionFunc = EnSkj_WaitForPlayback;
- }
}
}
}
@@ -1478,12 +1469,7 @@ void EnSkj_WaitForPlayback(EnSkj* this, PlayState* play) {
break;
case MSGMODE_MEMORY_GAME_PLAYER_PLAYING:
if (this->songFailTimer != 0) {
- // #region SOH [Enhancement]
- if (CVarGetInteger(CVAR_ENHANCEMENT("OcarinaUnlimitedFailTime"), 0) == 1 &&
- CVarGetInteger(CVAR_ENHANCEMENT("CustomizeOcarinaGame"), 0) == 1) {
- // don't decrement timer
- // #endregion
- } else {
+ if (GameInteractor_Should(VB_LOST_WOODS_OCARINA_GAME_TIMER_TICK, true)) {
this->songFailTimer--;
}
} else { // took too long, game failed