fix(render): Phase A8.F — camera sweep uses retail moverFlags 0x5c (PathClipped hard-stop)

Code review found the probe passed ObjectInfoState.None; retail's
SmartBox::update_viewer calls init_object(player, 0x5c) =
IsViewer|PathClipped|FreeRotate|PerfectClip (pseudo-C :92864). PathClipped makes
the sweep hard-stop at first contact (TransitionTypes.cs:811) instead of
edge-sliding around corners (which would re-trigger the A8.F camera-cell
instability); IsViewer lets the eye pass through creatures, colliding only with
world geometry. Resolves the spec's slide-vs-stop open question. Also reset
CollideCamera in the Defaults_AreRetailValues baseline test (review: maintenance
trap). Spec §5.1/§11.1 synced.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-29 19:11:53 +02:00
parent 376e2c3578
commit fcea05f808
3 changed files with 27 additions and 11 deletions

View file

@ -43,9 +43,17 @@ public sealed class PhysicsCameraCollisionProbe : ICameraCollisionProbe
stepDownHeight: 0f, // no step-down / ground snap
isOnGround: false, // no contact-plane / walkable semantics
body: null, // no cross-frame persistence
moverFlags: ObjectInfoState.None, // all targets collide; also keeps
// camera sweeps out of the #98
// IsPlayer capture filter
// Retail SmartBox::update_viewer calls init_object(player, 0x5c) =
// IsViewer | PathClipped | FreeRotate | PerfectClip (acclient
// pseudo-C :92864; enum TransitionTypes.cs:24-33). PathClipped makes
// the sweep HARD-STOP at first contact (TransitionTypes.cs:811) — the
// spring-arm pull-in, not the player's edge-slide. IsViewer lets the
// eye pass through creatures, colliding only with world geometry
// (CollisionExemption.cs:83-85). FreeRotate/PerfectClip are no-ops in
// acdream today but set to match retail's exact value. NOT IsPlayer
// (0x100), so camera sweeps stay out of the #98 capture filter.
moverFlags: ObjectInfoState.IsViewer | ObjectInfoState.PathClipped
| ObjectInfoState.FreeRotate | ObjectInfoState.PerfectClip,
movingEntityId: selfEntityId); // skip the player's own ShadowEntry
return FromSpherePath(r.Position, ViewerSphereRadius);