From a068292f2ad5162736bf56677a92646beb340d41 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 12 May 2026 18:06:05 +0200 Subject: [PATCH] feat(phys L.2a slice 3): populate CollisionInfo entity attribution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- src/AcDream.Core/Physics/TransitionTypes.cs | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index f2c4f6c..1a3a12f 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -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)