feat: add separate button mappings for the ocarina notes #1930

Merged
bluetoe-99 merged 7 commits from ocarina-notes into main 2026-11-12 03:03:43 +00:00
bluetoe-99 commented 2026-11-10 20:53:43 +00:00 (Migrated from github.com)

This adds 5 rows to the controls menu allowing for remapping of the 5 ocarina notes.

Why? To allow for easier button configurations for different control schemes (keyboard especially) when playing the ocarina. Currently, the notes cannot be moved to separate controls from the main game controls. The default keyboard bindings currently are Space, Q, R, E and Left mouse button, which can make it difficult to remember which note is which.

The default controls are unset, so nothing changes for a player who never touches them.

Ocarina control scheme should be set to normal in the native game settings menu.

Unable to test with Mic inputs. Would appreciate if someone with a working mic could test against this PR.

Checked in game: mappings overlapping with normal controls, interrupting dialogue (Saria, scarecrow, Zelda song of time), environment play spots (zoras domain waterfall). Screenshot of the Controls page with the new rows, all unset, below.

Tested on Linux, Vulkan, RTX 5070 Ti. Full test suite 374 passed, 0 failed.

Controls page with the five ocarina note rows unset

This adds 5 rows to the controls menu allowing for remapping of the 5 ocarina notes. Why? To allow for easier button configurations for different control schemes (keyboard especially) when playing the ocarina. Currently, the notes cannot be moved to separate controls from the main game controls. The default keyboard bindings currently are Space, Q, R, E and Left mouse button, which can make it difficult to remember which note is which. The default controls are unset, so nothing changes for a player who never touches them. Ocarina control scheme should be set to **normal** in the native game settings menu. Unable to test with Mic inputs. Would appreciate if someone with a working mic could test against this PR. Checked in game: mappings overlapping with normal controls, interrupting dialogue (Saria, scarecrow, Zelda song of time), environment play spots (zoras domain waterfall). Screenshot of the Controls page with the new rows, all unset, below. Tested on Linux, Vulkan, RTX 5070 Ti. Full test suite 374 passed, 0 failed. ![Controls page with the five ocarina note rows unset](https://raw.githubusercontent.com/bluetoe-99/Hookshot/pr1930-shot/controls-ocarina-notes.png)
Joey-Magnus (Migrated from github.com) reviewed 2026-11-10 20:53:43 +00:00
Joey-Magnus (Migrated from github.com) requested changes 2026-11-11 20:06:17 +00:00
Joey-Magnus (Migrated from github.com) left a comment

Thanks for this. Being able to bind the five notes on their own is a nice addition, the wait detection is carefully done (the beat timeout, and the dialogue gates that end the wait), and the new checks pass at the PR's head (test_ocarina_*, test_bound_keys, test_input_background, test_menu_keys). But the defaults change how the ocarina plays for most players before they touch the new rows, and I think that needs fixing before it merges.

blocking Keyboard players lose their note keys by default (host/nn_hid.c:876 and :520). During a button wait, oc_pad_word clears L, R, A, X and Y from the merged button word, keyboard included, and puts back only what the note rows hold. The note rows default to Num 0/2/4/6/8. So out of the box, the keys a player already plays the ocarina with (E, Q, R, Space, the left mouse button, or their own bindings) stop playing notes, and the ocarina only answers the numpad. Laptops and tenkeyless keyboards have no numpad, so those players can't play a song until they find the new rows. With NumLock off, the numpad keys also arrive as Insert and the arrows, so Num 0 opens the menu.

blocking Pads on the Xbox / PC or Swap Face layout (host/nn_hid.c:503, :858). The note pads default to the Nintendo positions (A = East, Y = West, X = North) and don't follow the Layout preset. On Xbox / PC, apply_pad_layout puts the Switch's B on East and A on South. During the wait, oc_notes marks East as consumed, so pads_read no longer reports B: the player can't put the ocarina away with their B button, and East plays the A note instead. South, their A, is stripped by oc_pad_word and plays nothing. That's a common layout on PC.

One way to fix both: leave the note rows unbound by default, meaning "the Switch button's own binding", and only take the wait over (strip and consume) for a note the player actually bound. Then nothing changes for anyone who doesn't use the rows, and HOS_OC_NOTES=0 is no longer needed as an escape hatch. If you'd rather keep explicit defaults, derive them from the current L/R/A/Y/X bindings (pad and keys) when the rows are first created, and re-derive the pad ones when the Layout preset changes.

should fix gpu/vk/vk_api.h:58: HOS_KEY_N grows, and the meaning of MkwVkHost::binding/bind changes (the pad set now accepts rows above HOS_SW_N). AGENTS.md asks for an MKWVK_API_VERSION bump when the host/renderer contract changes, and an exe and DLL from either side of this would disagree about the rows (an old host answers -1 and the menu would save pad_note_*=-1). It's a one-line bump. The branch is also several versions behind: its base has version 14, main is at 17.

should fix The branch conflicts with main (mergeable: CONFLICTING), so it needs a rebase. CI hasn't run the test workflows on it yet (only the title check), so they should run after the rebase.

minor The description should open with why the change is needed (CONTRIBUTING, Commits and pull requests), for example "the notes can't be moved off the game's buttons", and include a screenshot of the Controls page with the new rows, since they show on screen. It would also help to say how the wait was checked in game: the field ocarina, a song lesson, the scarecrow, and a dialogue that takes the buttons mid-song.

Thanks for this. Being able to bind the five notes on their own is a nice addition, the wait detection is carefully done (the beat timeout, and the dialogue gates that end the wait), and the new checks pass at the PR's head (`test_ocarina_*`, `test_bound_keys`, `test_input_background`, `test_menu_keys`). But the defaults change how the ocarina plays for most players before they touch the new rows, and I think that needs fixing before it merges. **blocking** Keyboard players lose their note keys by default (`host/nn_hid.c:876` and `:520`). During a button wait, `oc_pad_word` clears L, R, A, X and Y from the merged button word, keyboard included, and puts back only what the note rows hold. The note rows default to Num 0/2/4/6/8. So out of the box, the keys a player already plays the ocarina with (`E`, `Q`, `R`, Space, the left mouse button, or their own bindings) stop playing notes, and the ocarina only answers the numpad. Laptops and tenkeyless keyboards have no numpad, so those players can't play a song until they find the new rows. With NumLock off, the numpad keys also arrive as Insert and the arrows, so Num 0 opens the menu. **blocking** Pads on the Xbox / PC or Swap Face layout (`host/nn_hid.c:503`, `:858`). The note pads default to the Nintendo positions (A = East, Y = West, X = North) and don't follow the Layout preset. On Xbox / PC, `apply_pad_layout` puts the Switch's B on East and A on South. During the wait, `oc_notes` marks East as consumed, so `pads_read` no longer reports B: the player can't put the ocarina away with their B button, and East plays the A note instead. South, their A, is stripped by `oc_pad_word` and plays nothing. That's a common layout on PC. One way to fix both: leave the note rows unbound by default, meaning "the Switch button's own binding", and only take the wait over (strip and consume) for a note the player actually bound. Then nothing changes for anyone who doesn't use the rows, and `HOS_OC_NOTES=0` is no longer needed as an escape hatch. If you'd rather keep explicit defaults, derive them from the current L/R/A/Y/X bindings (pad and keys) when the rows are first created, and re-derive the pad ones when the Layout preset changes. **should fix** `gpu/vk/vk_api.h:58`: `HOS_KEY_N` grows, and the meaning of `MkwVkHost::binding`/`bind` changes (the pad set now accepts rows above `HOS_SW_N`). AGENTS.md asks for an `MKWVK_API_VERSION` bump when the host/renderer contract changes, and an exe and DLL from either side of this would disagree about the rows (an old host answers -1 and the menu would save `pad_note_*=-1`). It's a one-line bump. The branch is also several versions behind: its base has version 14, main is at 17. **should fix** The branch conflicts with main (`mergeable: CONFLICTING`), so it needs a rebase. CI hasn't run the test workflows on it yet (only the title check), so they should run after the rebase. **minor** The description should open with why the change is needed (CONTRIBUTING, *Commits and pull requests*), for example "the notes can't be moved off the game's buttons", and include a screenshot of the Controls page with the new rows, since they show on screen. It would also help to say how the wait was checked in game: the field ocarina, a song lesson, the scarecrow, and a dialogue that takes the buttons mid-song.
bluetoe-99 commented 2026-11-11 20:44:27 +00:00 (Migrated from github.com)

Thanks both blockers are right. With NumLock off, the default Num 0 is Insert, so the default opened the Insert menu.

Fixed in 0d55e38e, taking your first suggestion: the five rows start unset, meaning "the Switch button's own binding".

  • oc_pad_word walks the five rows and takes a Switch bit over only where the player bound that row; an unset row is left exactly as the player's own binding has it. Keyboard players keep E/Q/R/Space/the left mouse button (or their own), and the numpad is no longer mentioned anywhere.
  • Nothing is consumed for an unset row either (that part was already conditional on the row being bound), so on Xbox / PC the note layer no longer eats East: B still puts the ocarina away and A still plays. The rows follow the Layout preset by inheriting the button's own binding, so there is nothing to re-derive when the preset changes.
  • Kind 2 (the song lesson) keeps its behaviour: a bound remap is added to the game's own note buttons, never replacing them. My first attempt made kind 2 return early and I let the test catch that.

I left HOS_OC_NOTES in as an in-game A/B rather than an escape hatch. With the rows unset the layer is inert on its own, and deleting the variable would leave a stale guarded variant in the translation cache referencing a host symbol that no longer exists, which breaks an incremental rebuild. Say the word and I'll take it out.

Should fix: MKWVK_API_VERSION 17 → 18, folded into e9e28512, the commit that introduces the rows. The binding/bind comment now says the pad set takes the note rows. Rebased onto current main (463eab1f): the conflicts were comment shape (main's prompts_live block sitting above the key enum; main's hos_control_input early return where oc_notes() now runs; the second rebase's docstring bullets beside our note-wait one). Two fixtures needed for the rebase: test_menu_env_overrides.py stubs HOS_KEY_N and had to learn the note row names, and test_ocarina_note_bindings.py's extraction anchor now hit main's inner hos_bind declaration and stopped short of the definitions. 374 passed, 0 failed, 52 skipped.

Screenshot added.

Thanks both blockers are right. With NumLock off, the default `Num 0` is Insert, so the default opened the Insert menu. Fixed in 0d55e38e, taking your first suggestion: the five rows start unset, meaning "the Switch button's own binding". - `oc_pad_word` walks the five rows and takes a Switch bit over only where the player bound that row; an unset row is left exactly as the player's own binding has it. Keyboard players keep `E`/`Q`/`R`/Space/the left mouse button (or their own), and the numpad is no longer mentioned anywhere. - Nothing is consumed for an unset row either (that part was already conditional on the row being bound), so on Xbox / PC the note layer no longer eats East: B still puts the ocarina away and A still plays. The rows follow the Layout preset by inheriting the button's own binding, so there is nothing to re-derive when the preset changes. - Kind 2 (the song lesson) keeps its behaviour: a bound remap is *added* to the game's own note buttons, never replacing them. My first attempt made kind 2 return early and I let the test catch that. I left `HOS_OC_NOTES` in as an in-game A/B rather than an escape hatch. With the rows unset the layer is inert on its own, and deleting the variable would leave a stale guarded variant in the translation cache referencing a host symbol that no longer exists, which breaks an incremental rebuild. Say the word and I'll take it out. Should fix: `MKWVK_API_VERSION` 17 → 18, folded into e9e28512, the commit that introduces the rows. The `binding`/`bind` comment now says the pad set takes the note rows. Rebased onto current main (`463eab1f`): the conflicts were comment shape (main's `prompts_live` block sitting above the key enum; main's `hos_control_input` early return where `oc_notes()` now runs; the second rebase's docstring bullets beside our note-wait one). Two fixtures needed for the rebase: `test_menu_env_overrides.py` stubs `HOS_KEY_N` and had to learn the note row names, and `test_ocarina_note_bindings.py`'s extraction anchor now hit main's inner `hos_bind` declaration and stopped short of the definitions. `374 passed, 0 failed, 52 skipped`. Screenshot added.
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 21:46:16 +00:00
Joey-Magnus (Migrated from github.com) left a comment

Thanks, 0d55e38 fixes both blockers from my last review. The rows start unset, and oc_pad_word (host/nn_hid.c:885) only takes over a Switch bit for a row the player bound, so keyboard defaults and the Xbox / PC layout behave as before. I merged the head into current main: it merges cleanly. tests/run_all.py gives 379 passed, 0 failed. test_ocarina_notes, test_ocarina_note_bindings, test_ocarina_wait, test_menu_keys and test_env_on all pass.

One thing left: oc_notes (host/nn_hid.c:837) reads HOS_OC_NOTES with atoi, so HOS_OC_NOTES=on or =true turns the layer off. Main's rule is env_on / env_value_off (hookshot_env.h, already used in host/display.c). Since the switch defaults to on, !(off && env_value_off(off)) does it. It's also read on every input poll, so a static const would fit better.

Heads-up: #1990, #2261 and #2317 also set MKWVK_API_VERSION to 18. Whichever lands second needs 19. The microphone wait is still untested.

Thanks, 0d55e38 fixes both blockers from my last review. The rows start unset, and `oc_pad_word` (host/nn_hid.c:885) only takes over a Switch bit for a row the player bound, so keyboard defaults and the Xbox / PC layout behave as before. I merged the head into current main: it merges cleanly. `tests/run_all.py` gives 379 passed, 0 failed. `test_ocarina_notes`, `test_ocarina_note_bindings`, `test_ocarina_wait`, `test_menu_keys` and `test_env_on` all pass. One thing left: `oc_notes` (host/nn_hid.c:837) reads `HOS_OC_NOTES` with `atoi`, so `HOS_OC_NOTES=on` or `=true` turns the layer off. Main's rule is `env_on` / `env_value_off` (hookshot_env.h, already used in host/display.c). Since the switch defaults to on, `!(off && env_value_off(off))` does it. It's also read on every input poll, so a `static const` would fit better. Heads-up: #1990, #2261 and #2317 also set `MKWVK_API_VERSION` to 18. Whichever lands second needs 19. The microphone wait is still untested.
bluetoe-99 commented 2026-11-11 22:03:14 +00:00 (Migrated from github.com)

Removed entirely, since HOS_OC_NOTES is no longer needed.

Removed entirely, since HOS_OC_NOTES is no longer needed.
hckrman101 commented 2026-11-11 22:17:21 +00:00 (Migrated from github.com)

This now conflicts with main in gpu/vk/vk_menu.cpp, host/nn_hid.c. Could you merge main into the branch (or rebase) when you get a chance? I'll take another look once it's up to date.

This now conflicts with main in `gpu/vk/vk_menu.cpp`, `host/nn_hid.c`. Could you merge main into the branch (or rebase) when you get a chance? I'll take another look once it's up to date.
bluetoe-99 commented 2026-11-11 23:25:19 +00:00 (Migrated from github.com)

This now conflicts with main in gpu/vk/vk_menu.cpp, host/nn_hid.c. Could you merge main into the branch (or rebase) when you get a chance? I'll take another look once it's up to date.

Rebased on fce53453
The note rows were adapted to it: each now has Input 1 and Input 2, pad_note_x=[,] in the ini, and #1990's one-input-per-button rules never touch a note row (the shared-binding exemption is unchanged).

  • MKWVK_API_VERSION is now 18 → 19 since #1990 took 18
  • Suite on the head: 404 passed, 2 failed, both failing identically on main as well (test_linux_saves_move.py and the new upstream test_env_on.py hit on HOS_FPS_LOG).
> This now conflicts with main in `gpu/vk/vk_menu.cpp`, `host/nn_hid.c`. Could you merge main into the branch (or rebase) when you get a chance? I'll take another look once it's up to date. Rebased on fce53453 The note rows were adapted to it: each now has Input 1 and Input 2, pad_note_x=<pos>[,<pos>] in the ini, and #1990's one-input-per-button rules never touch a note row (the shared-binding exemption is unchanged). - MKWVK_API_VERSION is now 18 → 19 since #1990 took 18 - Suite on the head: 404 passed, 2 failed, both failing identically on main as well (test_linux_saves_move.py and the new upstream test_env_on.py hit on HOS_FPS_LOG).
bluetoe-99 commented 2026-11-11 23:25:55 +00:00 (Migrated from github.com)
image
<img width="2560" height="1440" alt="image" src="https://github.com/user-attachments/assets/5ef2faf3-2f8c-4e50-bed2-a0c6d84d7063" />
Commenting is not possible because the repository is archived.
No description provided.