fix(physics): skip Setup CylSphere/Sphere shadows for landblock stabs (A1.6)

ISSUES #83 Phase A1.6. Phase A1 gated the mesh-AABB-fallback path on
!_isLandblockStab, but Setup-derived CylSphere/Sphere/Radius-fallback
registrations (lines 5910-6005) still ran for stabs. A landblock stab
whose source is a Setup (0x02xxxxxx) with defined CylSpheres got BOTH
per-part BSP shadows AND a CylSphere shadow with id=entity.Id,
producing an invisible collision cylinder at the stab origin
alongside the correct BSP walls. User reported this as "thin air" hits
outside specific Holtburg buildings.

Retail's CBuildingObj uses BSP exclusively. Setup CylSphere/Sphere
data is for scenery (trees with trunk cylinders) and creatures.

Fix: extend A1's _isLandblockStab gate to wrap the Setup-derived
registration block (cylsphere, sphere, radius-fallback). One AND
clause on the outer `if (setup is not null)`.

Probe evidence (launch-a15-verify.utf8.log):
- 0xC0A9B45D (6 hits in outdoor cell 0xA9B40029) — Setup CylSphere on
  stab. src=0x020002FC. Pre-A1.5 it would also have been registered
  in adjacent landcells.
- 0xC0A9B463 (2 hits) — Setup Sphere on stab. src=0x020000E6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-20 12:00:47 +02:00
parent 4d3bf6fe37
commit 700abad94c

View file

@ -5921,7 +5921,17 @@ public sealed class GameWindow : IDisposable
// clobber entries via Deregister.
{
var setup = _physicsDataCache.GetSetup(entity.SourceGfxObjOrSetupId);
if (setup is not null)
// ISSUES #83 / Phase A1.6 (2026-05-21): landblock stabs use BSP
// collision exclusively (retail CBuildingObj). Skip Setup-derived
// CylSphere/Sphere/Radius shadow registrations for stabs to
// prevent the same doubled-collision bug A1 fixed for the
// mesh-AABB-fallback — but on the Setup path instead. Without
// this gate, a stab whose source is 0x02xxxxxx (Setup) with
// defined CylSpheres registers BOTH per-part BSP shadows AND a
// CylSphere shadow with id = entity.Id, producing an invisible
// collision cylinder at the stab origin alongside the correct
// BSP walls.
if (setup is not null && !_isLandblockStab)
{
float entScale = entity.Scale > 0f ? entity.Scale : 1f;
uint shapeIndex = 0;