feat(phys): A6.P4 door — cdb-driven NegPolyHit dispatch (incomplete; needs BSP near-miss recording)
cdb attached to retail at a Holtburg cottage door while user walked the inside-out off-center scenario. Three trace iterations identified that retail's collision-recording happens via SPHEREPATH::set_neg_poly_hit (fires hundreds of times during inside-out walk), NOT via the more obvious-named COLLISIONINFO setters (which fire 0 times). Apparatus scripts at tools/cdb/door-inside-out-v[1-3].cdb + symbol-probe.cdb. Our codebase has NegPolyHitDispatch defined but never called. The downstream TransitionalInsert NegPolyHit handler was a stub. Two-part fix landed: 1. BSPQuery.FindCollisions Path 5 (Contact branch) restructured — distinguishes full hit (hit0 == true → StepSphereUp) from near-miss (hit0 == false but hitPoly0 != null → NegPolyHitDispatch). Mirrors retail BSPTREE::find_collisions at acclient_2013_pseudo_c.txt:0053a630-0053a6fb. 2. Transition.TransitionalInsert NegPolyHit handler — dispatches to step_up + step_up_slide (NegStepUp=true) or records collision normal + returns Collided (NegStepUp=false). Mirrors retail CTransition::transitional_insert at acclient_2013_pseudo_c.txt:0050b7af-0050b7e6. Tests: all 11 fix-relevant + regression tests pass including issue #98. VISUAL VERIFICATION (user-driven inside-out off-center): still squeezes through. Diagnostic [neg-poly-dispatch] probe shows ZERO hits in production. The Path 5 restructuring doesn't surface NegPolyHit because our SphereIntersectsPolyInternal only sets hitPoly on FULL hits — retail's sphere_intersects_poly sets var_5c (closest polygon) even on near-misses via BSP-traversal side effect. Remaining fix (next session): add near-miss polygon recording to SphereIntersectsPolyInternal. Once it sets hitPoly on near-miss BSP traversal, the Path 5 NegPolyHit dispatch (this commit) will fire and the TransitionalInsert handler (this commit) will block. Full handoff with cdb trace table + next-step plan: docs/research/2026-05-25-door-bug-cdb-retail-trace-findings.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a657ca946c
commit
fd1548af61
7 changed files with 382 additions and 29 deletions
133
docs/research/2026-05-25-door-bug-cdb-retail-trace-findings.md
Normal file
133
docs/research/2026-05-25-door-bug-cdb-retail-trace-findings.md
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# Door bug — retail cdb trace + NegPolyHit dispatch findings
|
||||
2026-05-25, continuation of door-collision investigation
|
||||
|
||||
## TL;DR
|
||||
|
||||
cdb attached to retail at a Holtburg cottage door while user walked the
|
||||
inside-out off-center scenario. The smoking-gun trace identified the
|
||||
real collision-recording function: **`SPHEREPATH::set_neg_poly_hit`**
|
||||
fired hundreds of times during the walk; `SPHEREPATH::set_collide`,
|
||||
`COLLISIONINFO::set_collision_normal`, `set_sliding_normal`,
|
||||
`add_object` ALL fired zero times.
|
||||
|
||||
In our codebase, `NegPolyHitDispatch` exists but **is never called
|
||||
from any production code path** — it's dead code. The `path.NegPolyHit`
|
||||
flag is therefore never set. The downstream handler in
|
||||
`Transition.TransitionalInsert` was a stub that just cleared the flag.
|
||||
|
||||
Two-part fix attempted this session:
|
||||
|
||||
1. **`BSPQuery.FindCollisions` Path 5** (Contact branch) restructured
|
||||
to call `NegPolyHitDispatch` when sphere 0 had a near-miss polygon
|
||||
set but didn't fully penetrate (mirrors retail's `var_5c != 0` case
|
||||
at `acclient_2013_pseudo_c.txt:0053a6ce-0053a6fb`).
|
||||
|
||||
2. **`Transition.TransitionalInsert` NegPolyHit handler** rewritten
|
||||
to dispatch to `step_up + step_up_slide` (NegStepUp=true) or
|
||||
record collision normal + return `Collided` (NegStepUp=false).
|
||||
|
||||
**Result: fix doesn't fully close the bug.** User still squeezes
|
||||
through. Diagnostic `[neg-poly-dispatch]` probe shows ZERO hits in
|
||||
production — the BSP Path 5 changes don't surface NegPolyHit for this
|
||||
case.
|
||||
|
||||
## Why the fix doesn't fire
|
||||
|
||||
Retail's `BSPTREE::find_collisions` calls
|
||||
`vtable->sphere_intersects_poly(localspace_sphere, var_78_6, var_74_6, var_70_8)`
|
||||
which:
|
||||
- **Returns `eax_10`**: non-zero on full sphere-vs-poly hit
|
||||
- **Writes `var_5c`**: closest polygon pointer, set EVEN ON
|
||||
NEAR-MISS (BSP traversal sets it when entering a leaf containing
|
||||
candidate polys, regardless of intersection)
|
||||
|
||||
So retail records "near miss" polygons during BSP traversal. The
|
||||
caller dispatches `set_neg_poly_hit(1, var_5c + 0x20)` when sphere 0
|
||||
returned `eax_10 == 0` but `var_5c != 0`.
|
||||
|
||||
Our `SphereIntersectsPolyInternal` only sets `hitPoly` on actual
|
||||
hits. Near-miss polygons are NOT recorded. So the Path 5 branch
|
||||
`if (hitPoly0 is not null)` is false → no `NegPolyHitDispatch` call
|
||||
→ no NegPolyHit set → no dispatch in TransitionalInsert.
|
||||
|
||||
## The deeper fix needed
|
||||
|
||||
Implement retail's "BSP traversal records closest near-miss polygon"
|
||||
behavior in `SphereIntersectsPolyInternal` (or a sibling). The
|
||||
function should return TWO outputs:
|
||||
|
||||
- `bool hit` — true if sphere fully penetrates a polygon
|
||||
- `ResolvedPolygon? closestPoly` — set during traversal to the
|
||||
polygon that the sphere came closest to (in the BSP node walk),
|
||||
regardless of whether the full intersection test passed
|
||||
|
||||
This requires modifying the BSP recursion to track the "closest
|
||||
considered" polygon. Retail's sphere_intersects_poly likely tracks
|
||||
this as a side effect of testing each candidate polygon during the
|
||||
traversal.
|
||||
|
||||
Once that's in place, the existing Path 5 changes + TransitionalInsert
|
||||
NegPolyHit dispatch should fire correctly and produce the block.
|
||||
|
||||
## What the cdb trace proved
|
||||
|
||||
| Symbol | v1 hits | v2 hits | v3 hits |
|
||||
|---|---|---|---|
|
||||
| `CPhysicsObj::FindObjCollisions` | 161,081 | 196,608 | 196,608 |
|
||||
| `CCylSphere::collides_with_sphere` | 35,527 | — | — |
|
||||
| `SPHEREPATH::set_collide` | **0** | — | — |
|
||||
| `COLLISIONINFO::set_collision_normal` | — | **0** | — |
|
||||
| `COLLISIONINFO::set_sliding_normal` | — | **0** | — |
|
||||
| `COLLISIONINFO::add_object` | — | **0** | — |
|
||||
| `BSPTREE::slide_sphere` | — | — | **0** |
|
||||
| `CTransition::cliff_slide` | — | — | **0** |
|
||||
| **`SPHEREPATH::set_neg_poly_hit`** | — | — | **303+ (fires)** |
|
||||
| `CTransition::insert_into_cell` | — | — | 3,652 |
|
||||
|
||||
Retail records collisions almost exclusively via
|
||||
`SPHEREPATH::set_neg_poly_hit` during normal-grounded-motion. The
|
||||
COLLISIONINFO normal/sliding setters fire essentially never for
|
||||
walking-into-walls scenarios. Our investigation premise was wrong;
|
||||
the cdb data forced the correction.
|
||||
|
||||
## Apparatus + scripts committed
|
||||
|
||||
- `tools/cdb/door-inside-out.cdb` — v1 (set_collide check)
|
||||
- `tools/cdb/door-inside-out-v2.cdb` — v2 (COLLISIONINFO family)
|
||||
- `tools/cdb/door-inside-out-v3.cdb` — v3 (wide net, found
|
||||
set_neg_poly_hit)
|
||||
- `tools/cdb/symbol-probe.cdb` — verifies symbol resolution
|
||||
|
||||
## Pickup prompt for next session
|
||||
|
||||
```
|
||||
A6.P4 door inside-out: cdb trace + NegPolyHit dispatch landed
|
||||
(BSPQuery.FindCollisions Path 5 + TransitionalInsert NegPolyHit
|
||||
branch) but the fix doesn't fire because our SphereIntersectsPolyInternal
|
||||
doesn't record near-miss polygons. Retail's sphere_intersects_poly
|
||||
sets a "closest polygon" output even on non-hits via BSP traversal
|
||||
side-effect; our equivalent only sets it on full hits.
|
||||
|
||||
Read docs/research/2026-05-25-door-bug-cdb-retail-trace-findings.md
|
||||
|
||||
State both altitudes:
|
||||
Currently working toward: M1.5 — Indoor world feels right
|
||||
Current phase: A6.P4 door bug — implement near-miss polygon
|
||||
recording in SphereIntersectsPolyInternal.
|
||||
|
||||
First move: read SphereIntersectsPolyInternal in
|
||||
src/AcDream.Core/Physics/BSPQuery.cs (the function used at the
|
||||
Path 5 entry). Identify where polygons are tested during BSP
|
||||
traversal. Add a "closestPoly" output param that's set to ANY
|
||||
polygon considered during traversal (not just hit polygons).
|
||||
Then the Path 5 branch `if (hitPoly0 is not null)` will fire on
|
||||
near-miss cases, NegPolyHitDispatch will set NegPolyHit, and the
|
||||
TransitionalInsert dispatch (already landed) will block the sphere.
|
||||
|
||||
Retail oracle: BSPTREE::find_collisions + sphere_intersects_poly
|
||||
vtable call at acclient_2013_pseudo_c.txt:0053a630-0053a6fb.
|
||||
|
||||
Visual verification: same scenario (Holtburg cottage door,
|
||||
inside-out, ~50cm off-center). Should block fully, no squeeze-through.
|
||||
Outside-in should still work. Issue #98 cellar cap must still pass.
|
||||
```
|
||||
|
|
@ -1800,63 +1800,87 @@ public static class BSPQuery
|
|||
bool hit0 = SphereIntersectsPolyInternal(root, resolved, sphere0, movement,
|
||||
ref hitPoly0, ref contact0);
|
||||
|
||||
if (hit0 || hitPoly0 is not null)
|
||||
// A6.P4 door inside-out fix (2026-05-25). Retail distinguishes
|
||||
// FULL HIT (eax_10 != 0 → step_sphere_up early-return) from
|
||||
// NEAR-MISS (eax_10 == 0 but var_5c != 0 → set_neg_poly_hit).
|
||||
// Previously this branch conflated both — going to StepSphereUp
|
||||
// on near-misses too. That meant when sphere 0 had a near-miss
|
||||
// poly recorded but didn't actually penetrate, we still recursed
|
||||
// into step_sphere_up, which (because the sphere wasn't actually
|
||||
// colliding) returned OK and let the sphere walk on. Inside-out
|
||||
// cottage doors at off-center: sphere 0 has a near-miss with the
|
||||
// cottage exterior wall east of the doorway, but the full
|
||||
// sphere_intersects_poly returns false. Pre-fix → walked through.
|
||||
// Post-fix → set_neg_poly_hit, outer transitional_insert loop
|
||||
// dispatches to slide_sphere, blocks the sphere properly.
|
||||
//
|
||||
// Retail oracle: BSPTREE::find_collisions Contact branch at
|
||||
// acclient_2013_pseudo_c.txt:0053a630-0053a6fb.
|
||||
if (hit0)
|
||||
{
|
||||
// L.2d slice 1.5 (2026-05-13): record the hit poly EARLY,
|
||||
// before the StepSphereUp branch can recurse into
|
||||
// ResolveWithTransition → FindObjCollisions and clobber the
|
||||
// side-channel via the inner call's per-resolve clear. Path 5
|
||||
// is the dominant grounded-player path; without this the
|
||||
// probe's [resolve-bldg] line for every grounded BSP hit was
|
||||
// mis-labeled as "n/a (cylinder)".
|
||||
// Full hit — step_sphere_up.
|
||||
if (PhysicsDiagnostics.ProbeBuildingEnabled || PhysicsDiagnostics.ProbeIndoorBspEnabled)
|
||||
PhysicsDiagnostics.LastBspHitPoly = hitPoly0;
|
||||
|
||||
var worldNormal = L2W(hitPoly0!.Plane.Normal);
|
||||
// L.2.3b (2026-04-29): recursion guard. Retail
|
||||
// (acclient_2013_pseudo_c.txt:272954) gates step_sphere_up on
|
||||
// `if (sp.step_up == 0 && sp.step_down == 0)`. Without this,
|
||||
// the inner TransitionalInsert spawned by DoStepDown re-enters
|
||||
// FindObjCollisions, hits the same wall, and recursively
|
||||
// re-invokes step-up — churning the contact plane until
|
||||
// numAttempts decays. Mid-recursion we fall back to wall-slide.
|
||||
// `if (sp.step_up == 0 && sp.step_down == 0)`.
|
||||
if (engine is not null && !path.StepUp && !path.StepDown)
|
||||
return StepSphereUp(transition, worldNormal, engine);
|
||||
|
||||
// No engine OR step-up/step-down already in progress — fall
|
||||
// back to wall-slide so the inner sphere doesn't recurse.
|
||||
// back to wall-slide.
|
||||
collisions.SetCollisionNormal(worldNormal);
|
||||
collisions.SetSlidingNormal(worldNormal);
|
||||
return TransitionState.Slid;
|
||||
}
|
||||
|
||||
// Sphere 0 didn't fully hit. Test sphere 1 (head sphere).
|
||||
ResolvedPolygon? hitPoly1 = null;
|
||||
bool hit1 = false;
|
||||
|
||||
if (sphere1 is not null)
|
||||
{
|
||||
ResolvedPolygon? hitPoly1 = null;
|
||||
Vector3 contact1 = Vector3.Zero;
|
||||
Vector3 contact1 = Vector3.Zero;
|
||||
hit1 = SphereIntersectsPolyInternal(root, resolved, sphere1, movement,
|
||||
ref hitPoly1, ref contact1);
|
||||
|
||||
bool hit1 = SphereIntersectsPolyInternal(root, resolved, sphere1, movement,
|
||||
ref hitPoly1, ref contact1);
|
||||
|
||||
if (hit1 || hitPoly1 is not null)
|
||||
if (hit1)
|
||||
{
|
||||
// L.2d slice 1.5 (2026-05-13): same early-record as foot
|
||||
// sphere — head-sphere wall hits also recurse via
|
||||
// StepSphereUp on the grounded path.
|
||||
// Sphere 1 full hit while sphere 0 had only near-miss
|
||||
// (hitPoly0) — retail calls slide_sphere here. Record
|
||||
// collision + slide.
|
||||
if (PhysicsDiagnostics.ProbeBuildingEnabled || PhysicsDiagnostics.ProbeIndoorBspEnabled)
|
||||
PhysicsDiagnostics.LastBspHitPoly = hitPoly1;
|
||||
|
||||
var worldNormal = L2W(hitPoly1!.Plane.Normal);
|
||||
// L.2.3b: same recursion guard as the foot-sphere branch.
|
||||
if (engine is not null && !path.StepUp && !path.StepDown)
|
||||
return StepSphereUp(transition, worldNormal, engine);
|
||||
|
||||
collisions.SetCollisionNormal(worldNormal);
|
||||
collisions.SetSlidingNormal(worldNormal);
|
||||
return TransitionState.Slid;
|
||||
}
|
||||
}
|
||||
|
||||
// Neither sphere fully hit. Record neg-poly hit if either had a
|
||||
// near-miss polygon. Retail's set_neg_poly_hit with stepUp=false
|
||||
// for sphere 0's near-miss, stepUp=true for sphere 1's near-miss.
|
||||
// Outer transitional_insert loop then dispatches via slide_sphere
|
||||
// (stepUp=false) or step_up + step_up_slide (stepUp=true).
|
||||
if (hitPoly0 is not null)
|
||||
{
|
||||
if (PhysicsDiagnostics.ProbeBuildingEnabled || PhysicsDiagnostics.ProbeIndoorBspEnabled)
|
||||
PhysicsDiagnostics.LastBspHitPoly = hitPoly0;
|
||||
NegPolyHitDispatch(path, hitPoly0, stepUp: false, localToWorld);
|
||||
return TransitionState.OK;
|
||||
}
|
||||
if (hitPoly1 is not null)
|
||||
{
|
||||
if (PhysicsDiagnostics.ProbeBuildingEnabled || PhysicsDiagnostics.ProbeIndoorBspEnabled)
|
||||
PhysicsDiagnostics.LastBspHitPoly = hitPoly1;
|
||||
NegPolyHitDispatch(path, hitPoly1, stepUp: true, localToWorld);
|
||||
return TransitionState.OK;
|
||||
}
|
||||
|
||||
return TransitionState.OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -959,11 +959,72 @@ public sealed class Transition
|
|||
}
|
||||
|
||||
// Handle neg-poly hit (backward-facing polygon contact).
|
||||
//
|
||||
// A6.P4 door inside-out fix (2026-05-25). Pre-fix this was a
|
||||
// stub that just cleared the flag — letting the sphere walk
|
||||
// through closed Holtburg cottage doors at off-center
|
||||
// positions. cdb trace against retail (door-inside-out-v3.cdb)
|
||||
// confirmed retail fires SPHEREPATH::set_neg_poly_hit hundreds
|
||||
// of times during the walk, and the caller dispatches to
|
||||
// slide_sphere (no neg_step_up) or step_up + step_up_slide
|
||||
// fallback (neg_step_up). Both paths RECORD the collision
|
||||
// and stop / slide the sphere.
|
||||
//
|
||||
// Retail oracle: CTransition::transitional_insert at
|
||||
// docs/research/named-retail/acclient_2013_pseudo_c.txt:0050b7af-0050b7e6:
|
||||
// if (sphere_path.neg_step_up == 0)
|
||||
// edi = CSphere::slide_sphere(global_sphere, sphere_path,
|
||||
// collision_info,
|
||||
// neg_collision_normal, ...);
|
||||
// else if (CTransition::step_up(this, neg_collision_normal) == 0)
|
||||
// edi = SPHEREPATH::step_up_slide(sphere_path, this,
|
||||
// collision_info);
|
||||
if (sp.NegPolyHit && !sp.StepDown && !sp.StepUp)
|
||||
{
|
||||
sp.NegPolyHit = false;
|
||||
// ACE: dispatch to StepUp or SlideSphere based on NegStepUp flag.
|
||||
// Simplified: accept current position.
|
||||
|
||||
if (PhysicsDiagnostics.ProbeBuildingEnabled || PhysicsDiagnostics.ProbeIndoorBspEnabled)
|
||||
{
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[neg-poly-dispatch] stepUp={sp.NegStepUp} n=({sp.NegCollisionNormal.X:F3},{sp.NegCollisionNormal.Y:F3},{sp.NegCollisionNormal.Z:F3})"));
|
||||
}
|
||||
|
||||
if (sp.NegStepUp)
|
||||
{
|
||||
// Retail CTransition::step_up; on failure SPHEREPATH::step_up_slide.
|
||||
if (DoStepUp(sp.NegCollisionNormal, engine))
|
||||
{
|
||||
// Step-up succeeded — sphere repositioned by climb.
|
||||
// Fall through to subsequent step-down logic.
|
||||
}
|
||||
else
|
||||
{
|
||||
var stepUpSlideRes = sp.StepUpSlide(this);
|
||||
if (stepUpSlideRes == TransitionState.Slid)
|
||||
{
|
||||
ci.ContactPlaneValid = false;
|
||||
ci.ContactPlaneIsWater = false;
|
||||
continue;
|
||||
}
|
||||
if (stepUpSlideRes != TransitionState.OK)
|
||||
return stepUpSlideRes;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Retail CSphere::slide_sphere — the full retail version
|
||||
// adjusts sphere position via add_offset_to_check_pos +
|
||||
// returns Adjusted (on success) or Collided (degenerate).
|
||||
// Our simpler response: record the collision normal +
|
||||
// return Collided. The outer engine sees Collided and
|
||||
// does NOT advance the sphere position — block achieved.
|
||||
//
|
||||
// A6.P4 door inside-out fix (2026-05-25): user-visible
|
||||
// blocking is the goal (retail behavior); the full
|
||||
// slide-position adjustment can be a later iteration.
|
||||
ci.SetCollisionNormal(sp.NegCollisionNormal);
|
||||
return TransitionState.Collided;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle step-down when in contact but no ground plane found.
|
||||
|
|
|
|||
34
tools/cdb/door-inside-out-v2.cdb
Normal file
34
tools/cdb/door-inside-out-v2.cdb
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
$$
|
||||
$$ Retail cdb trace v2 — find what records the collision when retail blocks the door.
|
||||
$$ v1 had SPHEREPATH::set_collide which never fired (0 hits despite retail BLOCKING).
|
||||
$$ Switching to the COLLISIONINFO family.
|
||||
$$
|
||||
$$ BP1: CPhysicsObj::FindObjCollisions (entry count)
|
||||
$$ BP2: COLLISIONINFO::set_collision_normal (the "I got blocked" record)
|
||||
$$ BP3: COLLISIONINFO::set_sliding_normal (slide-along response)
|
||||
$$ BP4: COLLISIONINFO::add_object (which entity caused the collision)
|
||||
$$
|
||||
|
||||
.logopen C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c\retail-door-v2.log
|
||||
|
||||
.sympath C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c\refs
|
||||
.symopt+ 0x40
|
||||
.reload /f acclient.exe
|
||||
|
||||
r $t0 = 0
|
||||
r $t1 = 0
|
||||
r $t2 = 0
|
||||
r $t3 = 0
|
||||
r $t4 = 0
|
||||
|
||||
bp acclient!CPhysicsObj::FindObjCollisions "r $t1 = @$t1 + 1; r $t0 = @$t0 + 1; .if (@$t1 % 5000 == 0) { .printf /D \"[BP1] FindObj=%d setColN=%d setSldN=%d addObj=%d\\n\", @$t1, @$t2, @$t3, @$t4 }; .if (@$t0 >= 30000) { .printf /D \"=== DETACH total=%d findObj=%d setColN=%d setSldN=%d addObj=%d ===\\n\", @$t0, @$t1, @$t2, @$t3, @$t4; qd } .else { gc }"
|
||||
|
||||
bp acclient!COLLISIONINFO::set_collision_normal "r $t2 = @$t2 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP2] set_collision_normal hit#%d nx_h=0x%08X ny_h=0x%08X nz_h=0x%08X\\n\", @$t2, dwo(poi(@esp+4)+0), dwo(poi(@esp+4)+4), dwo(poi(@esp+4)+8); .if (@$t0 >= 30000) { qd } .else { gc }"
|
||||
|
||||
bp acclient!COLLISIONINFO::set_sliding_normal "r $t3 = @$t3 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP3] set_sliding_normal hit#%d nx_h=0x%08X ny_h=0x%08X nz_h=0x%08X\\n\", @$t3, dwo(poi(@esp+4)+0), dwo(poi(@esp+4)+4), dwo(poi(@esp+4)+8); .if (@$t0 >= 30000) { qd } .else { gc }"
|
||||
|
||||
bp acclient!COLLISIONINFO::add_object "r $t4 = @$t4 + 1; r $t0 = @$t0 + 1; .if (@$t0 >= 30000) { qd } .else { gc }"
|
||||
|
||||
.printf "v2 trace armed: 4 BPs (COLLISIONINFO family). Walk now.\\n"
|
||||
|
||||
g
|
||||
43
tools/cdb/door-inside-out-v3.cdb
Normal file
43
tools/cdb/door-inside-out-v3.cdb
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
$$
|
||||
$$ Retail cdb trace v3 — wider breakpoint net to catch what retail does
|
||||
$$ when it blocks the sphere. v1 (SPHEREPATH::set_collide) and v2
|
||||
$$ (COLLISIONINFO::set_collision/sliding/add_object) all came back at 0.
|
||||
$$
|
||||
$$ Trying: anything in the BSP slide / step / find_collisions chain.
|
||||
$$ BP1: CPhysicsObj::FindObjCollisions (counter only)
|
||||
$$ BP2: BSPTREE::slide_sphere
|
||||
$$ BP3: CSphere::slide_sphere
|
||||
$$ BP4: CTransition::cliff_slide
|
||||
$$ BP5: SPHEREPATH::set_neg_poly_hit (alternate hit-recording)
|
||||
$$ BP6: CTransition::insert_into_cell (cell transit attempts)
|
||||
$$
|
||||
|
||||
.logopen C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c\retail-door-v3.log
|
||||
|
||||
.sympath C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c\refs
|
||||
.symopt+ 0x40
|
||||
.reload /f acclient.exe
|
||||
|
||||
r $t0 = 0
|
||||
r $t1 = 0
|
||||
r $t2 = 0
|
||||
r $t3 = 0
|
||||
r $t4 = 0
|
||||
r $t5 = 0
|
||||
r $t6 = 0
|
||||
|
||||
bp acclient!CPhysicsObj::FindObjCollisions "r $t1 = @$t1 + 1; r $t0 = @$t0 + 1; .if (@$t1 % 5000 == 0) { .printf /D \"[STATS] FindObj=%d bspSld=%d sphSld=%d cliffSld=%d negPoly=%d insertCell=%d\\n\", @$t1, @$t2, @$t3, @$t4, @$t5, @$t6 }; .if (@$t0 >= 40000) { .printf /D \"=== DETACH FindObj=%d bspSld=%d sphSld=%d cliffSld=%d negPoly=%d insertCell=%d ===\\n\", @$t1, @$t2, @$t3, @$t4, @$t5, @$t6; qd } .else { gc }"
|
||||
|
||||
bp acclient!BSPTREE::slide_sphere "r $t2 = @$t2 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP2] BSPTREE::slide_sphere hit#%d\\n\", @$t2; .if (@$t0 >= 40000) { qd } .else { gc }"
|
||||
|
||||
bp acclient!CSphere::slide_sphere "r $t3 = @$t3 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP3] CSphere::slide_sphere hit#%d\\n\", @$t3; .if (@$t0 >= 40000) { qd } .else { gc }"
|
||||
|
||||
bp acclient!CTransition::cliff_slide "r $t4 = @$t4 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP4] cliff_slide hit#%d\\n\", @$t4; .if (@$t0 >= 40000) { qd } .else { gc }"
|
||||
|
||||
bp acclient!SPHEREPATH::set_neg_poly_hit "r $t5 = @$t5 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP5] set_neg_poly_hit hit#%d\\n\", @$t5; .if (@$t0 >= 40000) { qd } .else { gc }"
|
||||
|
||||
bp acclient!CTransition::insert_into_cell "r $t6 = @$t6 + 1; r $t0 = @$t0 + 1; .if (@$t0 >= 40000) { qd } .else { gc }"
|
||||
|
||||
.printf "v3 trace armed: 6 BPs wide net. Walk into door now.\\n"
|
||||
|
||||
g
|
||||
39
tools/cdb/door-inside-out.cdb
Normal file
39
tools/cdb/door-inside-out.cdb
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
$$
|
||||
$$ Retail cdb trace — does the cottage door block sphere at off-center inside-out approach?
|
||||
$$ 2026-05-25
|
||||
$$
|
||||
$$ Three breakpoints with counters; auto-detach at 30K total events to avoid
|
||||
$$ retail-session-lag → ACE timeout. Pattern from a6-probe.cdb (proven).
|
||||
$$
|
||||
$$ Question: when player walks from inside cottage to outside, off-center ~50cm,
|
||||
$$ does retail's collision system FIRE (set_collide) at the corner where
|
||||
$$ alcove east wall meets cottage north exterior wall? Or does it walk through
|
||||
$$ silently like acdream does?
|
||||
$$
|
||||
$$ BP1: CPhysicsObj::FindObjCollisions — per-object collision test entry.
|
||||
$$ BP2: CCylSphere::collides_with_sphere — cylinder shape hit test.
|
||||
$$ BP3: SPHEREPATH::set_collide — collision RECORDED (the smoking gun).
|
||||
$$ If BP3 fires often during inside-out walk: retail blocks.
|
||||
$$ If BP3 silent: retail walks through too (= retail-faithful behavior).
|
||||
$$
|
||||
|
||||
.logopen C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c\retail-door-inside-out.log
|
||||
|
||||
.sympath C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c\refs
|
||||
.symopt+ 0x40
|
||||
.reload /f acclient.exe
|
||||
|
||||
r $t0 = 0
|
||||
r $t1 = 0
|
||||
r $t2 = 0
|
||||
r $t3 = 0
|
||||
|
||||
bp acclient!CPhysicsObj::FindObjCollisions "r $t1 = @$t1 + 1; r $t0 = @$t0 + 1; .if (@$t1 % 5000 == 0) { .printf /D \"[BP1] FindObjCollisions=%d cyl_collides=%d set_collide=%d\\n\", @$t1, @$t2, @$t3 }; .if (@$t0 >= 30000) { .printf /D \"=== DETACH total=%d FindObj=%d cyl=%d setCollide=%d ===\\n\", @$t0, @$t1, @$t2, @$t3; qd } .else { gc }"
|
||||
|
||||
bp acclient!CCylSphere::collides_with_sphere "r $t2 = @$t2 + 1; r $t0 = @$t0 + 1; .if (@$t0 >= 30000) { qd } .else { gc }"
|
||||
|
||||
bp acclient!SPHEREPATH::set_collide "r $t3 = @$t3 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP3] set_collide hit#%d nx_h=0x%08X ny_h=0x%08X nz_h=0x%08X\\n\", @$t3, dwo(poi(@esp+4)+0), dwo(poi(@esp+4)+4), dwo(poi(@esp+4)+8); .if (@$t0 >= 30000) { qd } .else { gc }"
|
||||
|
||||
.printf "Door inside-out trace armed: 3 BPs, threshold 30K. Walk now.\\n"
|
||||
|
||||
g
|
||||
19
tools/cdb/symbol-probe.cdb
Normal file
19
tools/cdb/symbol-probe.cdb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
$$ Quick symbol probe: list what COLLISIONINFO / SPHEREPATH functions exist.
|
||||
.logopen C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c\retail-sym-probe.log
|
||||
|
||||
.sympath C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c\refs
|
||||
.symopt+ 0x40
|
||||
.reload /f acclient.exe
|
||||
|
||||
.printf "=== COLLISIONINFO symbols ===\n"
|
||||
x acclient!COLLISIONINFO::*
|
||||
|
||||
.printf "\n=== SPHEREPATH symbols ===\n"
|
||||
x acclient!SPHEREPATH::*
|
||||
|
||||
.printf "\n=== CTransition symbols (subset) ===\n"
|
||||
x acclient!CTransition::*collide*
|
||||
x acclient!CTransition::insert*
|
||||
x acclient!CTransition::makeTr*
|
||||
|
||||
qd
|
||||
Loading…
Add table
Add a link
Reference in a new issue