feat(camera): first-person view at Link's eyes #2567

Open
kafasam wants to merge 8 commits from kafasam/first-person into main
kafasam commented 2026-11-11 23:33:40 +00:00 (Migrated from github.com)

A walk-around first-person view as a row in the Insert menu's Controls tab (option 57, first_person in hookshot_options.ini; HOS_FIRST_PERSON=1 for one run), with two live sliders beside it — First person height (option 58, 0.5 to 2.5 m over Link's origin, default 1.5) and First person offset (option 59, 0 to 1 m along the view, default 0.15), both read every frame and persisted.

It is a constrained free camera rather than a camera patch: the shipped free camera's hook in the shared view-matrix routine (f_804e48c0, patch_view) already replaces the gameplay view's matrix after the game stores it, and the game's own frustum culling reads the replacement back the same frame. hos_first_person(c, view) beside it takes the frame's eye→target direction (yaw, pitch, mouse and lock-on easing all keep working — no angle math, no feedback loop into the camera FSM) and moves the eye to the player actor's origin plus the slider height, a small forward offset, and a rest-tilt correction toward level (HOS_FP_PITCH, 0.12 rad, clamped at the camera's own up limit). The camera pointer comes from the f_80644c80 hook mouse_native.c publishes; liveness is the camera hook's flag consumed by the view hook, plus the event-playing test, so cutscenes, shops and the game's own aim camera take the view back untouched — the aim's own view passes through this same routine and ours stands down before anything is written.

Known limits (readme documents them too): the view inherits the game's camera angles; the eye is not checked against walls; targeting/climb/swim states step out to third person until they pass; a recentering (the Z tap) holds the view briefly rather than pop. The aim (slingshot, bow, hookshot) and guard holds, the head hide and further view work continue on a local branch and will follow as their own commits once they feel right in play.

Option ids 57-59: past main's FOV 51 and the 52-56 that the open sprint, view-distance and keyboard PRs claim (#2604, #2669, #2530) — renumbered from the earlier 47/52/53 drafts as those were taken while this was in review.

Tests: test_first_person_anchor / _options / _view and test_mouse_native (the fixture compiles the view host against a fake guest and drives apply, sliders, hold, aim stand-down and pitch clamp). Single-commit-per-concern history on current main; no other PR's changes carried. Off by default.

A walk-around first-person view as a row in the Insert menu's Controls tab (option 57, `first_person` in hookshot_options.ini; `HOS_FIRST_PERSON=1` for one run), with two live sliders beside it — **First person height** (option 58, 0.5 to 2.5 m over Link's origin, default 1.5) and **First person offset** (option 59, 0 to 1 m along the view, default 0.15), both read every frame and persisted. It is a constrained free camera rather than a camera patch: the shipped free camera's hook in the shared view-matrix routine (`f_804e48c0`, `patch_view`) already replaces the gameplay view's matrix after the game stores it, and the game's own frustum culling reads the replacement back the same frame. `hos_first_person(c, view)` beside it takes the frame's eye→target direction (yaw, pitch, mouse and lock-on easing all keep working — no angle math, no feedback loop into the camera FSM) and moves the eye to the player actor's origin plus the slider height, a small forward offset, and a rest-tilt correction toward level (`HOS_FP_PITCH`, 0.12 rad, clamped at the camera's own up limit). The camera pointer comes from the `f_80644c80` hook mouse_native.c publishes; liveness is the camera hook's flag consumed by the view hook, plus the event-playing test, so cutscenes, shops and the game's own aim camera take the view back untouched — the aim's own view passes through this same routine and ours stands down before anything is written. Known limits (readme documents them too): the view inherits the game's camera angles; the eye is not checked against walls; targeting/climb/swim states step out to third person until they pass; a recentering (the Z tap) holds the view briefly rather than pop. The aim (slingshot, bow, hookshot) and guard holds, the head hide and further view work continue on a local branch and will follow as their own commits once they feel right in play. Option ids 57-59: past main's FOV 51 and the 52-56 that the open sprint, view-distance and keyboard PRs claim (#2604, #2669, #2530) — renumbered from the earlier 47/52/53 drafts as those were taken while this was in review. Tests: test_first_person_anchor / _options / _view and test_mouse_native (the fixture compiles the view host against a fake guest and drives apply, sliders, hold, aim stand-down and pitch clamp). Single-commit-per-concern history on current main; no other PR's changes carried. Off by default.
Owner

@Joey-Magnus requested changes on GitHub · 2026-11-12 14:33 UTC · original

Since the workflows haven't run, I merged this into current main and ran tests/run_all.py: 429 passed and 1 failed. The failure is tests/test_lift_cache.py, which fails on main here too. test_first_person_anchor/options/view and test_mouse_native pass. The row is off by default and the hook stands down for other cameras, events and the free camera, so players who don't turn it on aren't at risk.

What blocks it:

  • The title "first person: …" fails the Conventional Commit title check. Something like feat(camera): first-person view at Link's eyes would pass, and CI then needs maintainer approval to run.
  • Option ids: you moved to 54 with 52/53 for the sliders, but open PRs already claim them. #2604 takes HOS_OPT_SPRINT = 52, #2669 takes 52 onward, and #2530 takes HOS_OPT_KEYBOARD = 54. Whoever lands second has to renumber, so it's worth agreeing on numbers now.
  • This changes what's on screen, so it needs a screenshot or short clip in the description (CONTRIBUTING).
  • The description still says option 47 and calls the sliders a follow-up, and host/hos_first_person.c:91 still says "option 47". Please bring both up to date.
> [@Joey-Magnus](https://github.com/Joey-Magnus) requested changes on GitHub · 2026-11-12 14:33 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2567#pullrequestreview-5876217151) Since the workflows haven't run, I merged this into current main and ran tests/run_all.py: 429 passed and 1 failed. The failure is tests/test_lift_cache.py, which fails on main here too. test_first_person_anchor/options/view and test_mouse_native pass. The row is off by default and the hook stands down for other cameras, events and the free camera, so players who don't turn it on aren't at risk. What blocks it: - The title "first person: …" fails the Conventional Commit title check. Something like `feat(camera): first-person view at Link's eyes` would pass, and CI then needs maintainer approval to run. - Option ids: you moved to 54 with 52/53 for the sliders, but open PRs already claim them. #2604 takes HOS_OPT_SPRINT = 52, #2669 takes 52 onward, and #2530 takes HOS_OPT_KEYBOARD = 54. Whoever lands second has to renumber, so it's worth agreeing on numbers now. - This changes what's on screen, so it needs a screenshot or short clip in the description (CONTRIBUTING). - The description still says option 47 and calls the sliders a follow-up, and host/hos_first_person.c:91 still says "option 47". Please bring both up to date. <!-- github-sync:r:5876217151 -->
github-sync changed title from first person: the view at Link's eyes, as a menu option to feat(camera): first-person view at Link's eyes 2026-11-12 19:01:39 +00:00
Owner

@kafasam commented on GitHub · 2026-11-12 18:44 UTC · original

Thanks for the thorough review — running the suite yourself on a merge is above and beyond. All four addressed:

  • Title: feat(camera): first-person view at Link's eyes.
  • Option ids: moved to 57/58/59 (view, height slider, offset slider) — past main's FOV 51 and clear of #2604's sprint 52, #2669's view-distance 52-onward, and #2530's keyboard 54. If any of those land first and reach into 57+, we renumber again; the comments in vk_api.h now name the whole contested range.
  • Description: rewritten — the sliders are in this PR (options 58/59), not a follow-up, and every id reference is current; the stale option 47 in host/hos_first_person.c:91 is fixed in f3adcba0 too.
  • Screenshot: adding one to the description shortly (a clean first-person frame in the field); happy to add a short clip of the live sliders if that reads better.
> [@kafasam](https://github.com/kafasam) commented on GitHub · 2026-11-12 18:44 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2567#issuecomment-6832710082) Thanks for the thorough review — running the suite yourself on a merge is above and beyond. All four addressed: - **Title**: `feat(camera): first-person view at Link's eyes`. - **Option ids**: moved to **57/58/59** (view, height slider, offset slider) — past main's FOV 51 and clear of #2604's sprint 52, #2669's view-distance 52-onward, and #2530's keyboard 54. If any of those land first and reach into 57+, we renumber again; the comments in vk_api.h now name the whole contested range. - **Description**: rewritten — the sliders are in this PR (options 58/59), not a follow-up, and every id reference is current; the stale `option 47` in host/hos_first_person.c:91 is fixed in f3adcba0 too. - **Screenshot**: adding one to the description shortly (a clean first-person frame in the field); happy to add a short clip of the live sliders if that reads better. <!-- github-sync:c:6832710082 -->
Commenting is not possible because the repository is archived.
No description provided.