feat(phys L.2a slice 3): populate CollisionInfo entity attribution

The CollisionInfo.CollideObjectGuids list + LastCollidedObjectGuid
fields existed but were never written anywhere in the codebase — slice
2's [resolve] probe found this when 85 hit=yes lines came back with
no obj= attribution.

This commit fills the gap at the only place we have the attribution
data: the per-object iteration in Transition.FindObjCollisions, where
obj.EntityId is in scope right after each per-object BSPQuery /
CylinderCollision call. Two cases trigger an Add():
  - result != TransitionState.OK (object hard-blocked transition)
  - normal flipped invalid→valid during the call (BSPQuery captured
    a slide normal without halting — covers wall-slide cases).

Beyond the diagnostic, this also fixes a quiet structural gap — any
future physics behavior that wants "who did I just collide with"
(PvP exemption sanity check, NPC bump rules, etc.) was previously
flying blind on stub fields. Now the data flows.

Build green. Will re-test the doorway with the same trace to get
the wall's entity id.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-12 18:06:05 +02:00
parent e0c08bc57e
commit a068292f2a

View file

@ -1389,6 +1389,7 @@ public sealed class Transition
var sp = SpherePath;
var oi = ObjectInfo;
var ci = CollisionInfo;
// #42 diagnostic (2026-05-05): identify which static object causes
// the airborne first-frame ~1m push. Capture sphere check pos at
@ -1460,6 +1461,14 @@ public sealed class Transition
if (CollisionExemption.ShouldSkip(obj.State, obj.Flags, ObjectInfo.State))
continue;
// L.2a slice 3 (2026-05-12): snapshot collision-normal state so
// we can tell whether THIS object's BSP/CylSphere test produced a
// new collision (BSPQuery sets the normal but may still return OK
// for slide cases). Together with the `result != OK` check below
// this populates ci.CollideObjectGuids + LastCollidedObjectGuid so
// the [resolve] probe surfaces the responsible entity id.
bool collisionWasValidPre = ci.CollisionNormalValid;
TransitionState result;
if (obj.CollisionType == ShadowCollisionType.BSP)
@ -1523,6 +1532,22 @@ public sealed class Transition
result = CylinderCollision(obj, sp);
}
// L.2a slice 3: attribute the collision (if any) to this entity.
// Two cases:
// - result != OK: the object stopped the transition (hard-block).
// - result == OK but the normal flipped from invalid→valid during
// this call: BSPQuery captured a slide normal without halting.
// Either way this object is responsible for the hit, so add its
// entity id. CollideObjectGuids carries the full chain; the last
// assignment to LastCollidedObjectGuid wins which matches retail's
// "most recent" semantics for the probe.
if (result != TransitionState.OK
|| (!collisionWasValidPre && ci.CollisionNormalValid))
{
ci.CollideObjectGuids.Add(obj.EntityId);
ci.LastCollidedObjectGuid = obj.EntityId;
}
if (result != TransitionState.OK)
{
if (airborneDiag)