feat(input): Camera speed row for the right stick and the camera keys #1945

Merged
j64esatxzdrhfg merged 1 commit from feat/camera-speed into main 2026-11-11 16:58:21 +00:00
j64esatxzdrhfg commented 2026-11-10 21:23:38 +00:00 (Migrated from github.com)

The right stick turns the camera slowly, even at the fastest of the game's own camera speeds.

What changed

  • Controls tab, under Invert camera X/Y: Camera speed, 50–400% in steps of 25, default 100%, saved as camera_speed, a slider that stops at its ends.
  • nx2/oot_patches.py: each return of the right stick's turn-speed functions multiplies s0 by hos_camera_scale. The functions are f_80643f10 (yaw), f_80643b60 (pitch) and their level-2 pair f_82ea21f0/f_82ea2220. None of them calls another, so a turn is scaled once. The functions are found by name, so only the supported build's lift is patched, with a warning if fewer than four are found.
  • host/nn_vi.c: sets the scale from the row at each present via the option table (HOS_OPT_CAMERA_SPEED, 48; free beside main's newer ids: Text size 10, Skip title 44, camera centering 45). It sits next to #1921's Disable camera centering in the Controls tab. At 100% the lifted code runs as shipped.
  • The camera keys and Mouse camera "As the stick" follow it; native mouse look has its own sensitivity.
  • Tests: test_camera_speed.py covers the patch on a made-up lift, the option's path to the host and the scale; test_frame_rate_cap_step.py's fixture has the new scale.

Testing: Pro Controller at 100/200/300% on the v2.0.13 build. On main with this series, the row is in the Controls tab at 200% (screenshot).

Not tested: other game versions (by design unpatched), Linux.

Screenshot

Camera speed row in the Controls tab

🤖 Generated with Claude Code

The right stick turns the camera slowly, even at the fastest of the game's own camera speeds. **What changed** - Controls tab, under Invert camera X/Y: Camera speed, 50–400% in steps of 25, default 100%, saved as `camera_speed`, a slider that stops at its ends. - `nx2/oot_patches.py`: each return of the right stick's turn-speed functions multiplies `s0` by `hos_camera_scale`. The functions are `f_80643f10` (yaw), `f_80643b60` (pitch) and their level-2 pair `f_82ea21f0`/`f_82ea2220`. None of them calls another, so a turn is scaled once. The functions are found by name, so only the supported build's lift is patched, with a warning if fewer than four are found. - `host/nn_vi.c`: sets the scale from the row at each present via the option table (`HOS_OPT_CAMERA_SPEED`, 48; free beside main's newer ids: Text size 10, Skip title 44, camera centering 45). It sits next to #1921's Disable camera centering in the Controls tab. At 100% the lifted code runs as shipped. - The camera keys and Mouse camera "As the stick" follow it; native mouse look has its own sensitivity. - Tests: `test_camera_speed.py` covers the patch on a made-up lift, the option's path to the host and the scale; `test_frame_rate_cap_step.py`'s fixture has the new scale. **Testing:** Pro Controller at 100/200/300% on the v2.0.13 build. On main with this series, the row is in the Controls tab at 200% (screenshot). **Not tested:** other game versions (by design unpatched), Linux. **Screenshot** ![Camera speed row in the Controls tab](https://raw.githubusercontent.com/j64esatxzdrhfg/Hookshot/pr-screenshots/camera-speed/menu-camera-speed.jpg) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hckrman101 (Migrated from github.com) approved these changes 2026-11-11 02:18:49 +00:00
hckrman101 (Migrated from github.com) left a comment

Looks good to me. I read the patch, the host side and the tests; I did not build or run it.

  • Option id 48 is free: main uses 0-9, 10 (Text size), 16-22, 24-28, 32-35, 40-43 and 45 (camera centering), and none of the open PRs, including the rest of #1940-#1946, claims 46-59. It is wired the same way as the other rows: HOS_OPT_CAMERA_SPEED in vk_api.h, the fill_replay_table answer, then rx_option(...) in realtime_step. Out-of-range values fall back to 100%, so a missing menu or an edited ini gives the game's own speed.
  • The patch is idempotent (a second run keeps one scale), it only touches the four functions by name, and it warns if fewer than four are found. It leaves any lift that lacks the names unpatched. The row is a slider that stops at its ends, with the readme row and the description in the Controls tab matching what the code does.
  • Tests are declared (RUN_ALL_ARGS), and test_frame_rate_cap_step.py got the new scale and the option enums it now needs.
  • Overlap with the rest of the series: #1941, #1942, #1944 and #1946 also edit vk_menu.cpp/vk_menu.h, and #1940, #1941, #1942 and #1944 edit vk_replay.cpp. None of them adds an option id or touches vk_api.h, so there is no id clash. Only textual conflicts are possible near the row table and the fill_replay_table chain, and whichever merges later rebases.

Nits and things I could not check:

  • CI has not run on this head (the workflows are waiting for approval, action_required), and the branch is 56 commits behind main, so please run it and rebase before merging.
  • That s0 carries the return value of those four functions, and that scaling them is the whole of the right stick's turn, rests on the author's reading of the lift and the in-game test at 100/200/300% on v2.0.13. I can't check that without the dump. The made-up lift in the test covers the patch mechanics only.
  • The patch turns every return; into a block, which is fine for the if (...) return; shape the test covers; an if (...) return; else ... in a real lift would not compile, though the author's run on the supported build suggests there is none.

The maintainer's call on merging; nothing here blocks it.

Looks good to me. I read the patch, the host side and the tests; I did not build or run it. - Option id 48 is free: main uses 0-9, 10 (Text size), 16-22, 24-28, 32-35, 40-43 and 45 (camera centering), and none of the open PRs, including the rest of #1940-#1946, claims 46-59. It is wired the same way as the other rows: `HOS_OPT_CAMERA_SPEED` in `vk_api.h`, the `fill_replay_table` answer, then `rx_option(...)` in `realtime_step`. Out-of-range values fall back to 100%, so a missing menu or an edited ini gives the game's own speed. - The patch is idempotent (a second run keeps one scale), it only touches the four functions by name, and it warns if fewer than four are found. It leaves any lift that lacks the names unpatched. The row is a slider that stops at its ends, with the readme row and the description in the Controls tab matching what the code does. - Tests are declared (`RUN_ALL_ARGS`), and `test_frame_rate_cap_step.py` got the new scale and the option enums it now needs. - Overlap with the rest of the series: #1941, #1942, #1944 and #1946 also edit `vk_menu.cpp`/`vk_menu.h`, and #1940, #1941, #1942 and #1944 edit `vk_replay.cpp`. None of them adds an option id or touches `vk_api.h`, so there is no id clash. Only textual conflicts are possible near the row table and the `fill_replay_table` chain, and whichever merges later rebases. Nits and things I could not check: - CI has not run on this head (the workflows are waiting for approval, `action_required`), and the branch is 56 commits behind main, so please run it and rebase before merging. - That `s0` carries the return value of those four functions, and that scaling them is the whole of the right stick's turn, rests on the author's reading of the lift and the in-game test at 100/200/300% on v2.0.13. I can't check that without the dump. The made-up lift in the test covers the patch mechanics only. - The patch turns every `return;` into a block, which is fine for the `if (...) return;` shape the test covers; an `if (...) return; else ...` in a real lift would not compile, though the author's run on the supported build suggests there is none. The maintainer's call on merging; nothing here blocks it.
Ekibunnel commented 2026-11-11 05:08:24 +00:00 (Migrated from github.com)

I was working on the same thing myself, but this pull request beat me to it.
And it’s neater than what I would have done.
Please merge as fast as possible. (for some of us the game is not playable without this)

I was working on the same thing myself, but this pull request beat me to it. And it’s neater than what I would have done. Please merge as fast as possible. (for some of us the game is not playable without this)
j64esatxzdrhfg commented 2026-11-11 10:39:42 +00:00 (Migrated from github.com)

Rebased onto current main again: resolved the conflicts with the new movement-speed patch (SPEED_PLACES in oot_patches.py, realtime_rate in the frame-cap fixture) and #1921's camera centering (id 45). test_camera_speed.py and the menu and frame-cap checks pass. Thanks for the review!

Rebased onto current main again: resolved the conflicts with the new movement-speed patch (`SPEED_PLACES` in `oot_patches.py`, `realtime_rate` in the frame-cap fixture) and #1921's camera centering (id 45). `test_camera_speed.py` and the menu and frame-cap checks pass. Thanks for the review!
hckrman101 (Migrated from github.com) approved these changes 2026-11-11 16:49:44 +00:00
hckrman101 (Migrated from github.com) left a comment

Re-read after the rebase; still looks good. I read the diff, did not build or run it.

The push is a rebase onto current main (resolving the movement-speed SPEED_PLACES patch and #1921's camera centering), and the patch is the same as the one I approved: the four turn-speed functions in oot_patches.py, hos_camera_scale in nn_vi.c, the slider row, the fill_replay_table answer and the tests.

Option id 48 is still free: main uses 0-9, 10, 16-22, 24-28, 32-35, 40-43, 45 and 4096+, and the open PRs I checked take 46/47 (#2345's Walk key) and 60 (#2261's item wheel). #1600 and #2286 are fixes in the input path that do not touch these files or ids, so no clash with them.

The earlier nits stand: the real-lift shape of return; and the s0 reading rest on the author's in-game run on v2.0.13, which I can't check. CI has not run on this head (workflows are action_required, waiting for approval), so someone needs to approve the run before merging. The branch is mergeable now. Ekibunnel's note that this is needed for them to play is a good reason to land it soon; the call stays with the maintainer.

Re-read after the rebase; still looks good. I read the diff, did not build or run it. The push is a rebase onto current main (resolving the movement-speed `SPEED_PLACES` patch and #1921's camera centering), and the patch is the same as the one I approved: the four turn-speed functions in `oot_patches.py`, `hos_camera_scale` in `nn_vi.c`, the slider row, the `fill_replay_table` answer and the tests. Option id 48 is still free: main uses 0-9, 10, 16-22, 24-28, 32-35, 40-43, 45 and 4096+, and the open PRs I checked take 46/47 (#2345's Walk key) and 60 (#2261's item wheel). #1600 and #2286 are fixes in the input path that do not touch these files or ids, so no clash with them. The earlier nits stand: the real-lift shape of `return;` and the `s0` reading rest on the author's in-game run on v2.0.13, which I can't check. CI has not run on this head (workflows are `action_required`, waiting for approval), so someone needs to approve the run before merging. The branch is mergeable now. Ekibunnel's note that this is needed for them to play is a good reason to land it soon; the call stays with the maintainer.
Commenting is not possible because the repository is archived.
No description provided.