fix #175: door BSP collision poses at the motion-table closed pose
The Facility Hub double door (Setup 0x02000C9D) embeds the player into the visual panel from one side and blocks with a phantom slab on the other: its Setup PLACEMENT frames pose the two panels AJAR (yaw -150/-30 deg, 0.44 m behind the doorway plane — dat-confirmed by the Issue175 inspection) while the rendered door poses them CLOSED from the wire-supplied motion table via the sequencer. ShadowShapeBuilder read placement frames, so the 1.66x0.29x2.95 m physics slabs registered at the ajar pose. Retail tests each part's LIVE CPhysicsPart pose — for an idle door, the motion table's default (closed) state. Fix: ShadowShapeBuilder.FromSetup gains partPoseOverride (BSP part shapes only); RegisterServerEntityCollision derives it from the spawn MotionTableId via GameWindow.MotionTableDefaultPose (default style -> first cycle -> LowFrame part frames). Null/short poses fall back per-part to placement frames — table-less entities and landblock statics unchanged. One-shot snapshot vs retail's per-frame live pose is register row AP-84 (equivalent for the door lifecycle: closed == default pose; open == ETHEREAL bypasses collision, #150). Pins: FromSetup_PartPoseOverride_ReplacesPlacementFrames / _NoOverride_KeepsPlacementFrames / _ShortOverride_FallsBackPerPart. Suites: Core 2539 / App 713 / UI 425 / Net 385 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2312259a93
commit
355e389d4c
5 changed files with 160 additions and 6 deletions
|
|
@ -38,10 +38,22 @@ public static class ShadowShapeBuilder
|
|||
/// every radius, height, and local offset.</param>
|
||||
/// <param name="hasPhysicsBsp">Predicate: does the GfxObj with this id
|
||||
/// have a non-null PhysicsBSP? Production: <c>id => cache.GetGfxObj(id)?.BSP?.Root is not null</c>.</param>
|
||||
/// <param name="partPoseOverride">#175: per-part pose override for the
|
||||
/// BSP part shapes — the entity's motion-table DEFAULT-STATE pose (the
|
||||
/// closed pose for doors). Retail collision tests each part's LIVE
|
||||
/// <c>CPhysicsPart</c> pose, which for an idle entity is the motion
|
||||
/// table's default state, NOT the Setup's placement frame — the two
|
||||
/// differ on e.g. the Facility Hub double door (Setup 0x02000C9D:
|
||||
/// placement poses the panels AJAR at yaw −150°/−30°, y −0.44 m; the
|
||||
/// closed pose is straight). Null / short lists fall back to the
|
||||
/// placement frame per part (entities with no motion table, and the
|
||||
/// CylSphere/Sphere shapes, are unaffected — retail poses those from
|
||||
/// the setup too).</param>
|
||||
public static IReadOnlyList<ShadowShape> FromSetup(
|
||||
Setup setup,
|
||||
float entScale,
|
||||
Func<uint, bool> hasPhysicsBsp)
|
||||
Func<uint, bool> hasPhysicsBsp,
|
||||
IReadOnlyList<Frame>? partPoseOverride = null)
|
||||
{
|
||||
if (setup is null) throw new ArgumentNullException(nameof(setup));
|
||||
if (hasPhysicsBsp is null) throw new ArgumentNullException(nameof(hasPhysicsBsp));
|
||||
|
|
@ -84,15 +96,21 @@ public static class ShadowShapeBuilder
|
|||
}
|
||||
|
||||
// 3. Parts — one BSP shape per part with a non-null PhysicsBSP.
|
||||
// Pose priority per part: partPoseOverride (the motion-table
|
||||
// default-state pose, #175) → placement frame → identity.
|
||||
AnimationFrame? placementFrame = ResolvePlacementFrame(setup);
|
||||
for (int i = 0; i < setup.Parts.Count; i++)
|
||||
{
|
||||
uint gfxId = (uint)setup.Parts[i];
|
||||
if (!hasPhysicsBsp(gfxId)) continue;
|
||||
|
||||
Frame partFrame = placementFrame is not null && i < placementFrame.Frames.Count
|
||||
? placementFrame.Frames[i]
|
||||
: new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity };
|
||||
Frame partFrame;
|
||||
if (partPoseOverride is not null && i < partPoseOverride.Count)
|
||||
partFrame = partPoseOverride[i];
|
||||
else if (placementFrame is not null && i < placementFrame.Frames.Count)
|
||||
partFrame = placementFrame.Frames[i];
|
||||
else
|
||||
partFrame = new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity };
|
||||
|
||||
// BSP radius default; caller substitutes the real BoundingSphere.Radius
|
||||
// at registration time when available. Loose-but-safe broadphase value.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue