Commit graph

2 commits

Author SHA1 Message Date
Erik
f51c1dffa5 feat(#184): Slice 3 — Setup-derived mover sphere for the remote de-overlap sweep
The per-tick remote de-overlap sweep used a hardcoded HUMAN collision sphere
(0.48 m radius / 1.835 m capsule top) for EVERY creature, so large and small
monsters de-overlapped at human spacing (register TS-46). Retail seeds the
transition from the object's OWN Setup sphere list scaled by its wire ObjScale
(CPhysicsObj::transition 0x00512dc0 -> init_sphere(GetNumSphere, GetSphere,
m_scale); ObjScale from set_description 0x00514f40).

Slice 3 (one call site, no signature change): before the Path B ResolveWithTransition
call, read the creature's Setup-derived dims via the existing GetSetupCylinder
helper -- (setup.Radius, setup.Height) x ObjScale, the same source the local player
and the moveto/sticky radii already use, consistent with the spawn-time shadow
registration's entScale -- and pass them as sphereRadius/sphereHeight. Fall back to
the human capsule when GetSetupCylinder returns (0,0) for a shapeless / unresolvable
Setup (a zero radius would degenerate the sweep). The player call site is unchanged
(the player IS the human Setup). stepUp/stepDown stay 0.4 m (retail derives those
from the Setup too -- an adjacent divergence left as-is).

Big monsters now spread wider, small ones tighter -- the de-overlap distance tracks
each creature's true radius.

Test: RemoteDeOverlapMechanismTests.ConvergingLargeCreatures_DeOverlapWiderThanHuman
(an R=0.9 pair settles ~1.8 m -- materially wider than the human 0.96 m contact --
proving the sweep de-overlaps at the radius it is given). Register: narrows TS-46
(remotes no longer human-dimmed; residual = the two-scalar reconstruction vs retail's
sphere list, plus the 0.4 m step heights). Core 2621 / App 741 green.

Research: workflow wf_e8306250-21b (3-agent read-only sweep: acdream data source /
retail init_sphere reference / minimal-edit path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:53:21 +02:00
Erik
37a94e1fa4 feat(#184): remote-creature de-overlap — placement-snap + shadow-follows-resolved (visual gate passed)
Packed monster remotes interpenetrate in acdream but barely in retail on the same
ACE. Retail de-overlaps them CLIENT-side: it sweeps every remote creature every
tick against neighbours' LIVE resolved positions (the collision shadow == the
resolved m_position, re-registered every moved transition step), with the server
position a gentle catch-up target (CPhysicsObj::MoveOrTeleport 0x00516330), not a
hard-snap. The collision math was already faithful; the bug was the reconciliation
(hard-snap) + the movement model (synth-velocity) + a stale shadow.

A first attempt (reverted) enqueued EVERYTHING and left the shadow at the raw
server position — it gate-failed with invisible monsters (an unplaced body blipped
over a huge distance into the sweep -> garbage pos) and the player stuck on offset
shadows. This redo fixes both root causes, mechanism-proven in a Core test first:

- NPC UpdatePosition routes through MoveOrTeleport with a PLACEMENT-SNAP: the body
  is snapped to the server pos when it is not already near it (first UP, no
  Sequencer to consume the queue, >96 m, or |Body - worldPos| > 4 m); only near DR
  corrections enqueue. This restores the body's placement authority (no invisible
  monsters). Airborne keeps the authoritative hard-snap.
- Grounded movement drives the body from the interp CATCH-UP (ComputeOffset ->
  InterpolationManager::adjust_offset, REPLACE dichotomy) instead of synth-velocity
  (get_state_velocity / SERVERVEL); MovementManager::UseTime runs unconditionally.
- SHADOW-FOLLOWS-RESOLVED: after each tick's sweep the collision shadow is
  re-registered at the resolved body (SyncRemoteShadowToBody), movement-gated
  (|Body - LastShadowSyncPos| > 1 cm). The per-UP :5669 raw-pos sync is now
  PLAYERS-ONLY, so an NPC's shadow is only ever written to its resolved body ->
  neighbours de-overlap against resolved bodies, the spread PERSISTS, and collision
  == render (no stuck-on-nothing). Landing clears the interp queue.

Preserved: airborne path, sticky #171 (gate + StickyManager overwrite of the seeded
frame), omega, the #173 bounce, landing, the node_fail_counter watchdog, and Path A
(player remotes, untouched -- Slice 2 unifies it).

Tests (RemoteDeOverlapMechanismTests): converging pair settles STABLE at 0.86 m
(barely overlapping = the retail look) WITH the shadow-sync vs <0.40 m (full
overlap) WITHOUT it; a third test drives the REAL InterpolationManager loop and
confirms the sweep absorbs the stall-blip (no pop-into-neighbour). 2-lens Opus
review (CONCERNS) addressed: movement-gated re-flood for the town-FPS risk;
players-only :5669; the blip-absorption test.

Register: retires TS-41 (SERVERVEL synth-velocity -> catch-up), narrows TS-44 (NPC
UP unified onto the interp queue; gate kept for orientation), adds AP-86
(shadow-follows-resolved impl) + AP-87 (MoveOrTeleport 4 m/no-Sequencer placement
snap). Known residual: the de-overlap sweep uses the human sphere for the mover, so
large creatures de-overlap at human radii (TS-46; Slice 3 plumbs Setup dims).

Visual gate PASSED (user: monsters visible + spacing much better). Core 2620 /
App 741 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:36:54 +02:00