diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 4655bdfc..9e33e213 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -24,6 +24,77 @@ What does NOT go here: - Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending. - Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed. +## #333 — The shadow broadphase reach filter measures from the PART ORIGIN, so an off-centre BSP part can be in the right cell and still never be tested + +**Status:** OPEN +**Severity:** high for the tall-prop population. It is the gate immediately +downstream of the AP-156 membership fix, so that fix alone may not be enough to +make the worst objects block. +**Filed:** 2026-08-06 at the AP-156 fix (commit `b52967de`), which surfaced it. +**Do NOT bundle with AP-156.** Different code path (collision query, not cell +membership), and it needs its own retail question answered first. + +### The mechanism + +`src/AcDream.Core/Physics/TransitionTypes.cs:3756-3764`, the shadow broadphase: + +```csharp +Vector3 deltaToCurr = currPos - obj.Position; +... +float maxReach = sphereRadius + obj.Radius + movement.Length() + 2f; +if (distToCurr > maxReach) + continue; // candidate discarded, never tested +``` + +`obj.Position` is the shadow row's PART placement — +`entityWorldPos + rotate(shape.LocalPosition, entityWorldRot)` — while +`obj.Radius` is the physics-BSP ROOT BOUNDING SPHERE radius, which is measured +about that sphere's own centre, not about the part origin. AP-156 established +that the two are frequently far apart: 376 of 973 installed physics-BSP parts +have a root-sphere origin further from the part origin than half their own +radius, worst 20.762 m on a 27.708 m sphere. + +Write `d` for the distance from part origin to the true sphere centre, `R` for +`obj.Radius`, `r` for the mover's sphere radius. A mover just touching the +geometry is `R + r` from the sphere's TRUE centre, hence up to `d + R + r` from +`obj.Position`. The filter admits it only when +`d + R + r <= r + R + movement + 2`, i.e. only when `d <= movement + 2`. Per +physics tick the movement term is well under a metre, so any part whose root +sphere sits more than about 2 m from its part origin can have a genuine contact +discarded before `BSPQuery` ever runs. + +Worked case — Setup `0x02000255`, one part, root sphere +origin `(0.000, -0.007, 9.911)`, radius `10.522`, `Setup.Height = 18.692`. +`d = 9.911` against a budget of roughly 2.5 m. A player standing against the +upper half of that prop is about 20.4 m from the part origin while `maxReach` +is about 13.5 m. Discarded. + +### Why it matters now + +Before AP-156 those objects were usually not registered in the cell at all, so +this filter never got the chance to reject them. AP-156 puts them into the +correct cells; this filter is the next gate they hit. If the connected session +finds a tall prop that still does not block after AP-156, look here first. + +### What to establish before fixing + +1. Does retail have this pre-filter at all? `CPhysicsObj::FindObjCollisions` + @0x0050f050 walks the cell's object list and dispatches per object; the + `+ 2f` slack and the `movement.Length()` term look like acdream's own + broadphase rather than a port. If it is acdream's, it also needs a + divergence-register row, which it does not currently have. +2. If it is kept, it must measure from where the geometry is: `ShadowEntry` + needs the `BoundsCenter` that `ShadowShape` now carries, and `deltaToCurr` + must be taken against `obj.Position + rotate(obj.BoundsCenter, obj.Rotation)`. +3. Cylinder rows are unaffected (`BoundsCenter == Zero` by construction) — the + change must not move them. + +### Test to write first + +A mover adjacent to an off-centre BSP part's geometry, with the part origin far +outside `maxReach`. It must reach `BSPQuery`. Sabotage by restoring the +part-origin measurement; it must go back to reporting no collision. + ## #332 — Headless bots appear to have no remote dead-reckoning at all **Status:** OPEN (observation, not yet established as a defect)