diff --git a/code/oot.ld b/code/oot.ld
index 22438c7b..f04597d8 100644
--- a/code/oot.ld
+++ b/code/oot.ld
@@ -844,6 +844,10 @@ SECTIONS
*(.patch_ActorDraw)
}
+ .patch_OcarinaNoteSound_Player 0x2D6A04 : {
+ *(.patch_OcarinaNoteSound_Player)
+ }
+
.patch_CamUpdate 0x2D84C8 : {
*(.patch_CamUpdate)
}
@@ -1734,6 +1738,10 @@ SECTIONS
*(.patch_EnableFW)
}
+ .patch_OcarinaNoteSound_Npc 0x477C08 + region_offset : {
+ *(.patch_OcarinaNoteSound_Npc)
+ }
+
.patch_StoreTargetActorType 0x479984 + region_offset : {
*(.patch_StoreTargetActorType)
}
diff --git a/code/src/hooks.s b/code/src/hooks.s
index e953f54a..3b960805 100644
--- a/code/src/hooks.s
+++ b/code/src/hooks.s
@@ -2467,3 +2467,23 @@ hook_DrawHeartIcon:
cmp r0,#0x0
pop {r0-r12, lr}
bx lr
+
+.global hook_OcarinaNoteSound_Player
+hook_OcarinaNoteSound_Player:
+ push {r0,r2,r3,lr}
+ bl 0x2CFE00 @ original instruction, sets vibrato
+ mov r0,#0x0 @ default ocarina instrument
+ bl OcarinaNotes_OverrideInstrument
+ cpy r1,r0
+ pop {r0,r2,r3,lr}
+ b 0x2CFD24 @ set instrument
+
+.global hook_OcarinaNoteSound_Npc
+hook_OcarinaNoteSound_Npc:
+ and r1,r0,#0xFF @ instrument
+ push {r0, r2-r12, lr}
+ cpy r0,r1
+ bl OcarinaNotes_OverrideInstrument
+ cpy r1,r0
+ pop {r0, r2-r12, lr}
+ bx lr
diff --git a/code/src/ocarina_notes.c b/code/src/ocarina_notes.c
index eb762c2b..9cf92fae 100644
--- a/code/src/ocarina_notes.c
+++ b/code/src/ocarina_notes.c
@@ -1,6 +1,7 @@
#include "ocarina_notes.h"
#include "savefile.h"
#include "settings.h"
+#include "common.h"
s32 OcarinaNotes_IsButtonOwned(OcarinaNoteButton button) {
return (gSettingsContext.shuffleOcarinaButtons == OFF) ||
@@ -81,3 +82,13 @@ u32 OcarinaNotes_HandleInputs(u32 ocarinaInputs) {
ocarinaInputs &= ownedBtnsMask;
return ocarinaInputs;
}
+
+s32 OcarinaNotes_OverrideInstrument(u32 instrument) {
+ if (instrument != OCARINA_INSTRUMENT_DEFAULT) {
+ return instrument;
+ }
+ if (gSettingsContext.ocarinaNoteInstrument == OCARINA_INSTR_SETTING_SCENE_SPECIFIC) {
+ return Hash(gGlobalContext->sceneNum) % OCARINA_INSTRUMENT_MAX;
+ }
+ return gSettingsContext.ocarinaNoteInstrument - OCARINA_INSTR_SETTING_DEFAULT;
+}
diff --git a/code/src/ocarina_notes.h b/code/src/ocarina_notes.h
index ebb20873..fbf8f2ed 100644
--- a/code/src/ocarina_notes.h
+++ b/code/src/ocarina_notes.h
@@ -4,6 +4,17 @@
#include "z3D/z3D.h"
#include "input.h"
+// These values are one less than in OoT decomp
+typedef enum OcarinaInstrumentId {
+ OCARINA_INSTRUMENT_DEFAULT,
+ OCARINA_INSTRUMENT_MALON,
+ OCARINA_INSTRUMENT_WHISTLE, // Impa
+ OCARINA_INSTRUMENT_HARP, // Sheik
+ OCARINA_INSTRUMENT_GRIND_ORGAN, // Windmill Man
+ OCARINA_INSTRUMENT_FLUTE, // Skull Kid
+ OCARINA_INSTRUMENT_MAX,
+} OcarinaInstrumentId;
+
typedef enum OcarinaNoteButton {
OCARINA_BUTTON_L,
OCARINA_BUTTON_R,
diff --git a/code/src/patches.s b/code/src/patches.s
index 2d100e3c..e0fb6119 100644
--- a/code/src/patches.s
+++ b/code/src/patches.s
@@ -2536,3 +2536,13 @@ AfterInvalidatingRoomObjects_patch:
.global DrawHeartIcon_patch
DrawHeartIcon_patch:
bl hook_DrawHeartIcon
+
+.section .patch_OcarinaNoteSound_Player
+.global OcarinaNoteSound_Player_patch
+OcarinaNoteSound_Player_patch:
+ b hook_OcarinaNoteSound_Player
+
+.section .patch_OcarinaNoteSound_Npc
+.global OcarinaNoteSound_Npc_patch
+OcarinaNoteSound_Npc_patch:
+ bl hook_OcarinaNoteSound_Npc
diff --git a/code/src/settings.h b/code/src/settings.h
index 67dfee1b..6ff8881f 100644
--- a/code/src/settings.h
+++ b/code/src/settings.h
@@ -447,6 +447,12 @@ typedef enum {
SHUFFLESFX_CHAOS,
} ShuffleSFXSetting;
+typedef enum {
+ OCARINA_INSTR_SETTING_RANDOM_CHOICE,
+ OCARINA_INSTR_SETTING_SCENE_SPECIFIC,
+ OCARINA_INSTR_SETTING_DEFAULT,
+} OcarinaNoteInstrumentSetting;
+
typedef enum {
DUNGEON_NEITHER,
DUNGEON_BARREN,
@@ -699,6 +705,8 @@ typedef struct {
u8 shuffleSFXLinkVoice;
u8 shuffleSFXCategorically;
+ u8 ocarinaNoteInstrument;
+
union {
u8 dungeonModes[12];
struct {
diff --git a/source/descriptions.cpp b/source/descriptions.cpp
index de19370e..e0dae846 100644
--- a/source/descriptions.cpp
+++ b/source/descriptions.cpp
@@ -1321,6 +1321,13 @@ string_view shuffleSFXCategorically = "Sound effects will be shuffled in categ
"\n" //
"The sound may get annoying fast when disabled."; //
/*------------------------------ //
+| OCARINA INSTRUMENT | //
+------------------------------*/ //
+string_view ocarinaInstrDesc = "Change the instrument used when playing the\n" //
+ "ocarina."; //
+string_view ocarinaInstrRandomDesc = "A random instrument from the list will be chosen.";
+string_view ocarinaInstrSceneDesc = "The instrument will be different in each scene."; //
+/*------------------------------ //
| RANDOM TRAP DAMAGE TYPE | //
------------------------------*/ //
string_view randomTrapDmgDesc = "All traps will be the base game ice trap"; //
diff --git a/source/descriptions.hpp b/source/descriptions.hpp
index 42f98567..989c1563 100644
--- a/source/descriptions.hpp
+++ b/source/descriptions.hpp
@@ -417,6 +417,10 @@ extern string_view shuffleSFXSceneSpecific;
extern string_view shuffleSFXChaos;
extern string_view shuffleSFXCategorically;
+extern string_view ocarinaInstrRandomDesc;
+extern string_view ocarinaInstrSceneDesc;
+extern string_view ocarinaInstrDesc;
+
extern string_view randomTrapDmgDesc;
extern string_view basicTrapDmgDesc;
extern string_view advancedTrapDmgDesc;
diff --git a/source/settings.cpp b/source/settings.cpp
index 39ded1ff..c809e308 100644
--- a/source/settings.cpp
+++ b/source/settings.cpp
@@ -1331,6 +1331,10 @@ Option ShuffleSFXFootsteps = Option::Bool(2, "Include Footsteps", {"No",
Option ShuffleSFXLinkVoice = Option::Bool(2, "Include Link's Voice", {"No", "Yes"}, {""}, OptionCategory::Cosmetic, ON);
Option ShuffleSFXCategorically = Option::Bool(2, "Categorical Shuffle", {"Off", "On"}, {shuffleSFXCategorically}, OptionCategory::Cosmetic, ON);
+Option OcarinaNoteInstrument = Option::U8 ("Ocarina Instrument", {"Random Choice", "Scene Specific",
+ "Default", "Malon", "Whistle", "Harp",
+ "Grind Organ", "Flute"}, {ocarinaInstrRandomDesc, ocarinaInstrSceneDesc, ocarinaInstrDesc}, OptionCategory::Cosmetic, OCARINA_INSTR_SETTING_DEFAULT);
+
std::vector