4.9 KiB
Code review — NEO-17 (OcclusionPolicy)
Date: 2026-04-08
Scope: Branch NEO-17-occlusion-policy vs origin/main (NEO-17 occlusion policy resource, camera integration, scene wiring, tests, and docs). Issue NEO-17; no PR URL supplied.
Base: origin/main @ 1d891eb8bb42ccc5e8a74b4fcdb349b9165f3454 (merge-base with HEAD at review time).
Follow-up: Previously requested changes below are done and were re-reviewed against the updated branch state.
Verdict
Approve
Summary
The branch adds a data-driven OcclusionPolicy resource, wires it into IsometricFollowCamera, tags the prototype obstacle as an "occluder", and documents the prototype readability gate in the module doc. Re-review confirms the prior cleanup bug is fixed: occluders are now restored on the _process() early-return paths as well as on node exit, so transient target-resolution failures no longer strand transparent geometry. The plan and decomposition tracking docs have also been brought back into alignment with the implemented null-material behavior and shipped E1.M2 scope. Residual risk is low and limited to the manual runtime behavior that still needs Godot-side verification.
Documentation checked
| Document | Result |
|---|---|
docs/plans/NEO-17-implementation-plan.md |
Matches — technical approach now reflects the implemented three-way material handling, including the explicit null-material fallback. |
docs/decomposition/modules/E1_M2_IsometricCameraController.md |
Matches — implementation snapshot and readability gate note align with the code and scene changes. |
docs/decomposition/modules/module_dependency_register.md |
Matches — E1.M2 still correctly reads In progress, and the note now reflects follow + zoom + occlusion shipped with NEO-18 remaining. |
docs/decomposition/modules/documentation_and_implementation_alignment.md |
Matches — implementation tracking now includes E1.M2 with current shipped scope and pointers. |
docs/decomposition/modules/client_server_authority.md |
Matches — the change stays client-local and does not introduce any server use of camera pose. |
Blocking issues
Done.client/scripts/isometric_follow_camera.gd— occluders are never restored when the follow target disappears mid-occlusion._process()returns immediately whenfollow_target_pathno longer resolves, but that path does not call_restore_all_occluders(). If the player target is freed, renamed, or otherwise temporarily unavailable while an obstacle is faded, the saved surface overrides stay applied until the camera node exits the tree. Because this leaves world geometry stuck semi-transparent in a recoverable runtime failure mode, it should be fixed before merge by restoring all active occluders on the early-return path (and similarly on any other “cannot update camera this frame” exit that can strand state)._process()now restores all occluders before both early returns (camera/_stateunavailable and unresolvedfollow_target_path), so the fade state is cleared when camera updates cannot proceed.
Suggestions
-
Done. The “Material override strategy” section now documents the three explicit cases:docs/plans/NEO-17-implementation-plan.md— reconcile the null-material story. The plan currently says null materials are skipped in the “Material override strategy” section, but the implementation and Decisions table intentionally create a transparentStandardMaterial3Dfor null-material surfaces. Update the technical-approach prose so the plan is a single source of truth.StandardMaterial3D, null material, and non-StandardMaterial3D. -
Done. The register note now says NEO-15 through NEO-17 are in repo, and the implementation alignment table now includes andocs/decomposition/modules/module_dependency_register.mdanddocs/decomposition/modules/documentation_and_implementation_alignment.md— refresh implementation tracking after merge.E1.M2has meaningful shipped work now (follow, zoom, occlusion), so the register note and tracking table should reflect that status explicitly instead of implying occlusion is still pending or leaving the implementation table empty for this module.E1.M2row with current scope and pointers.
Nits
None.
Verification
- From
client/: Godot 4.6 headless import, then GdUnit:
godot --headless --import --path . --quit-after 8
godot --headless --path . -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --ignoreHeadlessMode -a res://test - Manual: run the prototype scene, walk the player behind the
Obstacle, confirm it fades and restores normally, then test the failure path by makingfollow_target_pathunresolved or removing the target while the obstacle is faded; geometry should restore immediately instead of staying transparent. - Review environment note: I could not run the automated Godot command here because no
godotbinary was available onPATH.