neon-sprawl/docs/reviews/2026-04-07-NEO-15.md

44 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Code review: NEO-15 (isometric follow camera)
**Date:** 2026-04-07
**Scope:** Branch `NEO-15-isometric-follow-camera` vs `main`; issue [NEO-15](https://linear.app/neon-sprawl/issue/NEO-15). Commits on branch include implementation plan, locked defaults, **NEO-19** backlog row in `E1_M2_IsometricCameraController.md`, and camera implementation + tests + docs. Manual testing reported OK by author.
**Base:** `main` (compare: `main...NEO-15-isometric-follow-camera`).
## Verdict
**Approve with nits**
## Summary
The change replaces a static `Camera3D` with an `IsometricFollowCamera` rig that follows the player using a documented spherical offset (`desired_eye_world`), exponential smoothing, teleport snap beyond `snap_distance`, and per-frame `look_at` toward a configurable focus. A `RefCounted` `CameraState` is owned by the rig and refreshed each `_process`, with orbit yaw held at zero while `allow_yaw` is false—matching prototype policy. `main.gd` stays a thin composer; `ground_pick` still receives the active `Camera3D`. Automated tests cover `CameraState` fields and the static eye-position helper; manual verification (movement, bumps, picking) is appropriate per plan. No server or authority surface is added. Risk is low: presentation-only, well-scoped, aligns with E1.M2 and the implementation plan.
## Documentation checked
| Document | Result |
|----------|--------|
| `docs/plans/NEO-15-implementation-plan.md` | **Matches** — locked defaults (`_process`, damped follow + snap, `NodePath` follow, per-frame `CameraState`), file list, tests, and README/module snapshot are reflected in the diff. |
| `docs/decomposition/modules/E1_M2_IsometricCameraController.md` | **Matches** — implementation snapshot documents rig scripts, yaw seam, single-band distance; Linear backlog table includes NEO-19 (separate ticket; see nits). |
| `docs/decomposition/modules/client_server_authority.md` (E1.M2) | **Matches** — camera and `CameraState` remain client-local; no server use of camera pose. |
| `docs/decomposition/modules/module_dependency_register.md` | **Partially matches** — E1.M2 row still **Planned** with full contract set; NEO-15 delivers a slice (follow + `CameraState` seam), not zoom/occlusion. Updating register or module **Status** after merge is optional hygiene, not required for this PRs correctness. |
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | **N/A** — no E1.M2-specific row found; no conflict. |
## Blocking issues
None.
## Suggestions
1. **Null follow target:** If `follow_target_path` breaks or resolves empty, `_process` returns without updating `_smoothed_eye` or syncing state—acceptable for the current scene, but a one-line comment or dev warning could save a future scene refactor.
2. **Register / status (optional):** After merge, consider whether `module_dependency_register.md` or the E1.M2 summary **Status** should reflect partial delivery (e.g. follow baseline landed; zoom/occlusion still open)—only if the team tracks that table at story granularity.
## Nits
- **Nit:** `camera_state.gd` header references `[member presentation_yaw_deg]`; that property lives on `isometric_follow_camera.gd`, not on `CameraState`—minor doc confusion for Godot help tooltips.
- **Nit:** Same branch carries **NEO-19** (prototype district) backlog registration; harmless, but reviewers merging a “NEO-15 only” PR should expect that small doc delta.
- **Nit:** Plan wording “set state yaw to `0` when `allow_yaw` is false” is satisfied via `_orbit_yaw_rad = 0` and `_state.yaw = _orbit_yaw_rad`; presentation yaw remains in exports—consistent with module intent but worth remembering for readers who expect a single “total yaw” on state.
## Verification
- **Automated:** From `client/`, run GdUnit headless per `client/README.md` (Godot 4.6 import + `GdUnitCmdTool` on `res://test`).
- **Manual:** (Author confirmed OK.) Re-run: server + F5, click-move floor and NEO-10 bumps, ground pick from moving camera, idle observation; optional inspector tweak of `follow_smoothness` / `snap_distance` if feel regresses on different hardware.