feat: Add an option to disable automatic camera centering #1921

Merged
fatalis merged 3 commits from feat/disable-camera-centering into main 2026-11-10 21:11:19 +00:00
fatalis commented 2026-11-10 20:29:45 +00:00 (Migrated from github.com)

Add a setting to disable automatic camera centering

Add “Disable camera centering” under Controls so the camera stops turning toward the direction you move. This keeps it from fighting native mouse look.

The setting defaults to Off, saves your choice, and applies immediately. Manual camera controls, explicit re-centering, and scripted camera movements still work.

Add a setting to disable automatic camera centering Add “Disable camera centering” under Controls so the camera stops turning toward the direction you move. This keeps it from fighting native mouse look. The setting defaults to Off, saves your choice, and applies immediately. Manual camera controls, explicit re-centering, and scripted camera movements still work.
hckrman101 (Migrated from github.com) reviewed 2026-11-10 20:37:51 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks, this is a nice option and the shape is right: default Off, a live row (no restart flag, preset-exempt), the ini key comes out as disable_camera_centering from the row name, and the hook is a runtime check inside the lifted camera update, so toggling it in the menu takes effect at once (after one re-lift to get the hook in). I checked the anchor and the join label against a local lift of the supported build: each is found exactly once in that function, and the x25 = x26 before the jump matches what an existing path into the same join already does, so the skip looks consistent.

A few things before merge:

  • Option id 44 is also taken by #1560 (Skip title, still open). Whichever lands second has to renumber, here in vk_api.h and in the rx_option(44, 0) literal in host/mouse_native.c. Worth agreeing on a number now (45?) so it doesn't collide silently.
  • No test for the patch. patch_camera_centering is pure string work and easy to cover without the game: a small synthetic body with the anchor and the join, checking the hook goes in once right after the anchor, a second run changes nothing, the extern is added, and it returns None when the anchor or the join is missing or appears twice. If you add a test file, give it RUN_ALL_ARGS (#1822). The two fixture edits (test_framegen_preset, test_rumble_menu) are needed because they compile set_row, and they look right.
  • In-game confirmation. CI can't show this, so please say what you tried with it On: Z-targeting (does the camera still snap behind Link on Z and stay put while targeting?), Epona, swimming, crawlspaces, first person, and a cutscene or two, with both Mouse camera modes. A screenshot of the new Controls row would be good too (CONTRIBUTING asks for one when something shows on screen).
  • CI hasn't run on this PR yet (no checks reported; fork workflows probably need a maintainer to approve them).

Nits inline.

Thanks, this is a nice option and the shape is right: default Off, a live row (no restart flag, preset-exempt), the ini key comes out as `disable_camera_centering` from the row name, and the hook is a runtime check inside the lifted camera update, so toggling it in the menu takes effect at once (after one re-lift to get the hook in). I checked the anchor and the join label against a local lift of the supported build: each is found exactly once in that function, and the `x25 = x26` before the jump matches what an existing path into the same join already does, so the skip looks consistent. A few things before merge: - **Option id 44 is also taken by #1560** (Skip title, still open). Whichever lands second has to renumber, here in `vk_api.h` and in the `rx_option(44, 0)` literal in `host/mouse_native.c`. Worth agreeing on a number now (45?) so it doesn't collide silently. - **No test for the patch.** `patch_camera_centering` is pure string work and easy to cover without the game: a small synthetic body with the anchor and the join, checking the hook goes in once right after the anchor, a second run changes nothing, the extern is added, and it returns None when the anchor or the join is missing or appears twice. If you add a test file, give it `RUN_ALL_ARGS` (#1822). The two fixture edits (`test_framegen_preset`, `test_rumble_menu`) are needed because they compile `set_row`, and they look right. - **In-game confirmation.** CI can't show this, so please say what you tried with it On: Z-targeting (does the camera still snap behind Link on Z and stay put while targeting?), Epona, swimming, crawlspaces, first person, and a cutscene or two, with both Mouse camera modes. A screenshot of the new Controls row would be good too (CONTRIBUTING asks for one when something shows on screen). - **CI hasn't run** on this PR yet (no checks reported; fork workflows probably need a maintainer to approve them). Nits inline.
hckrman101 (Migrated from github.com) commented 2026-11-10 20:37:51 +00:00

Nit: this table is for rows whose numbers are "not the obvious one"; this one is plain 0 Off / 1 On, which the line after the table already covers. Fine to drop it here (the readme row and paragraph are the right place for it).

Nit: this table is for rows whose numbers are "not the obvious one"; this one is plain 0 Off / 1 On, which the line after the table already covers. Fine to drop it here (the readme row and paragraph are the right place for it).
hckrman101 (Migrated from github.com) commented 2026-11-10 20:37:51 +00:00

#1560 also takes 44 (HOS_OPT_SKIP_TITLE). One of the two needs a different number; if it's this one, the literal in host/mouse_native.c changes with it.

#1560 also takes 44 (`HOS_OPT_SKIP_TITLE`). One of the two needs a different number; if it's this one, the literal in `host/mouse_native.c` changes with it.
hckrman101 (Migrated from github.com) commented 2026-11-10 20:37:51 +00:00

Nit, optional: the other options the game thread reads through rx_option (Mouse camera, Invert mouse vertical, Text speed) are read straight from opts in vk_replay.cpp's lambda. Doing the same here (if (id == HOS_OPT_DISABLE_CAMERA_CENTERING) return opts.disable_camera_centering;) would drop the atomic, its two stores, the accessor, and the fixture lines in the two tests. The atomic isn't wrong, just more than its neighbours do.

Nit, optional: the other options the game thread reads through `rx_option` (Mouse camera, Invert mouse vertical, Text speed) are read straight from `opts` in `vk_replay.cpp`'s lambda. Doing the same here (`if (id == HOS_OPT_DISABLE_CAMERA_CENTERING) return opts.disable_camera_centering;`) would drop the atomic, its two stores, the accessor, and the fixture lines in the two tests. The atomic isn't wrong, just more than its neighbours do.
@ -145,0 +152,4 @@
" /* (nx2/oot_patches.py: disable camera centering) */\n")
def patch_camera_centering(source):
hckrman101 (Migrated from github.com) commented 2026-11-10 20:37:51 +00:00

This could use a small game-free test (synthetic body with the anchor and the join): hook inserted once after the anchor, idempotent on a second run, extern added, and None when the anchor or the join is missing or appears twice.

This could use a small game-free test (synthetic body with the anchor and the join): hook inserted once after the anchor, idempotent on a second run, extern added, and None when the anchor or the join is missing or appears twice.
hckrman101 (Migrated from github.com) reviewed 2026-11-10 21:07:37 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks for the update. Checked 2dcf210 -> 109bffe against our earlier points:

  • Fixed: option id is now 45, in vk_api.h and the rx_option(45, 0) literal in host/mouse_native.c. That clears #1560 (44) and main (0-9, 16-22, 24-28, 32-35, 40-43). Whichever of the two lands second should still rebase against the other's enum.
  • Fixed: tests/test_camera_centering.py covers the hook going in once after the anchor, the second run changing nothing, the extern, and None for a missing or doubled anchor/join, a missing camera function and an unterminated body. It declares RUN_ALL_ARGS.
  • Fixed (nits): the option is read straight from opts in the vk_replay.cpp lambda, so the atomic is gone, and the docs/development.md row is dropped.
  • Still open: in-game confirmation and a screenshot of the new Controls row. Nothing has been posted yet, and CI can't show this. Please say what you tried with it On: Z-targeting (snaps behind Link, stays put while targeting), Epona, swimming, crawlspaces, first person, a cutscene or two, with both Mouse camera modes.
  • CI: still no checks reported on the branch (fork workflows likely need a maintainer to approve them), so the merge of main and the new test haven't run in CI. The PR is mergeable with main.

Nothing else new in the merge. Once the in-game notes and screenshot are in, I have no further concerns from the code side.

Thanks for the update. Checked 2dcf210 -> 109bffe against our earlier points: - Fixed: option id is now 45, in `vk_api.h` and the `rx_option(45, 0)` literal in `host/mouse_native.c`. That clears #1560 (44) and main (0-9, 16-22, 24-28, 32-35, 40-43). Whichever of the two lands second should still rebase against the other's enum. - Fixed: `tests/test_camera_centering.py` covers the hook going in once after the anchor, the second run changing nothing, the extern, and None for a missing or doubled anchor/join, a missing camera function and an unterminated body. It declares `RUN_ALL_ARGS`. - Fixed (nits): the option is read straight from `opts` in the `vk_replay.cpp` lambda, so the atomic is gone, and the `docs/development.md` row is dropped. - Still open: in-game confirmation and a screenshot of the new Controls row. Nothing has been posted yet, and CI can't show this. Please say what you tried with it On: Z-targeting (snaps behind Link, stays put while targeting), Epona, swimming, crawlspaces, first person, a cutscene or two, with both Mouse camera modes. - CI: still no checks reported on the branch (fork workflows likely need a maintainer to approve them), so the merge of main and the new test haven't run in CI. The PR is mergeable with main. Nothing else new in the merge. Once the in-game notes and screenshot are in, I have no further concerns from the code side.
hckrman101 commented 2026-11-10 21:11:26 +00:00 (Migrated from github.com)

Make follow-up

Make follow-up
fatalis commented 2026-11-10 21:15:29 +00:00 (Migrated from github.com)
image

Z-targetting: works as intended
Swimming: works as intended
Crawlspaces: works as intended

Epona: untested

<img width="2560" height="1359" alt="image" src="https://github.com/user-attachments/assets/269ca3d5-899b-4ba7-bbef-54a090f2642d" /> Z-targetting: works as intended Swimming: works as intended Crawlspaces: works as intended Epona: untested
Commenting is not possible because the repository is archived.
No description provided.