fix(physics): skip collision registration for phantom-Setup scenery (small plants / grass)
User asked how AC differentiates collidable vs phantom scenery. The retail signal is at the Setup level: a Setup with NO CylSpheres, NO Spheres, AND zero overall Radius is decorative -- the player walks through it. retail header docs/research/named-retail/acclient.h enum PhysicsState includes ETHEREAL_PS = 0x4 / IGNORE_COLLISIONS_PS = 0x10 for runtime-toggled flags, but the static signal for scenery is the empty Setup collision arrays. Pre-fix the mesh-bounds-fallback at GameWindow.cs:4297-4429 ran on every outdoor scenery entity regardless of Setup intent, then clamped the resulting cylinder radius to >= 0.3 m. So small plants/grass got a 0.3 m collision cylinder and blocked the player even though the Setup explicitly said no collision. Fix: before the mesh-bounds fallback, check the cached Setup. If it's a Setup-typed object (0x020xxxxx) AND CylSpheres / Spheres / Radius are all empty/zero, mark it phantom and skip the collision registration entirely. Non-phantom scenery (trees with real CylSpheres or canopy-only BSPs) still gets the mesh-bounds fallback so the player walks under canopies but bumps into trunks. Raw GfxObjs (0x010xxxxx, no Setup metadata) keep the old fallback behaviour because they don't expose phantom intent. Tests stay 1439 green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
46544ef3c1
commit
90aa74a3cb
1 changed files with 36 additions and 1 deletions
|
|
@ -4284,6 +4284,36 @@ public sealed class GameWindow : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
// L-fix3 (2026-04-28): retail "decorative / phantom" detection.
|
||||
// A Setup is phantom (no collision) when it has NO CylSpheres,
|
||||
// NO Spheres, AND zero overall Radius. Small plants, grass
|
||||
// tufts, flowers, ground-cover bushes all match — retail
|
||||
// ships them with empty collision arrays so the player walks
|
||||
// through them. Without this gate the mesh-bounds fallback
|
||||
// below assigns every plant a 0.3 m+ collision cylinder
|
||||
// (line ~4409 clamp) and they block the player.
|
||||
//
|
||||
// The gate is layered AFTER the Setup CylSphere / BSP
|
||||
// registrations above (which are no-ops for phantom Setups
|
||||
// anyway), so non-phantom scenery (trees with real
|
||||
// CylSpheres or canopy-only BSPs) still gets the
|
||||
// mesh-bounds fallback. The check is on entity.SourceGfxObjOrSetupId
|
||||
// to look up the cached Setup; if it's a raw GfxObj
|
||||
// (0x010xxxxx, no Setup metadata) we keep the old fallback
|
||||
// behaviour because GfxObjs don't expose phantom intent.
|
||||
bool isPhantomSetup = false;
|
||||
if ((entity.SourceGfxObjOrSetupId & 0xFF000000u) == 0x02000000u)
|
||||
{
|
||||
var setupInfoForPhantom = _physicsDataCache.GetSetup(entity.SourceGfxObjOrSetupId);
|
||||
if (setupInfoForPhantom is not null
|
||||
&& setupInfoForPhantom.CylSpheres.Count == 0
|
||||
&& setupInfoForPhantom.Spheres.Count == 0
|
||||
&& setupInfoForPhantom.Radius <= 0.0001f)
|
||||
{
|
||||
isPhantomSetup = true;
|
||||
}
|
||||
}
|
||||
|
||||
// VISUAL mesh-bounds collision: for SCENERY entities (IDs with
|
||||
// 0x80000000 bit set, indicating procedurally-placed scenery),
|
||||
// ALWAYS compute a cylinder from the world-space mesh AABB.
|
||||
|
|
@ -4294,7 +4324,12 @@ public sealed class GameWindow : IDisposable
|
|||
// For stabs (low IDs) and live entities, keep the existing Setup
|
||||
// CylSphere / BSP registrations — those are placed with precise
|
||||
// frame data and don't have the scenery offset issue.
|
||||
if ((_isOutdoorMesh || (entityBsp == 0 && entityCyl == 0)) && entity.MeshRefs.Count > 0)
|
||||
//
|
||||
// L-fix3: skip entirely when the Setup is phantom — retail
|
||||
// decorative meshes have no collision data on purpose.
|
||||
if (!isPhantomSetup
|
||||
&& (_isOutdoorMesh || (entityBsp == 0 && entityCyl == 0))
|
||||
&& entity.MeshRefs.Count > 0)
|
||||
{
|
||||
float entScale = entity.Scale > 0f ? entity.Scale : 1f;
|
||||
bool haveBounds = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue