fix(physics): always register CylSphere alongside BSP parts

Previously CylSphere was only registered when no BSP parts existed.
Retail tests BOTH: CylSphere as the broad collision volume (trunk)
plus BSP parts for polygon-level collision. This ensures the trunk
cylinder catches collisions that individual BSP parts might miss,
especially at the base of large trees.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-14 13:16:18 +02:00
parent 2ebbfc4864
commit bfe6a49d67

View file

@ -1801,10 +1801,10 @@ public sealed class GameWindow : IDisposable
partIndex++; partIndex++;
} }
// If no BSP parts were registered, check for CylSphere collision // ALWAYS register CylSphere when the Setup has one, even if BSP parts
// from the Setup (trees, rocks, NPCs use cylinder collision). // were also registered. Retail tests both: CylSphere as the broad
if (partIndex == 0 || !entity.MeshRefs.Any(mr => // collision volume (trunk) plus BSP parts for precise polygon collision.
_physicsDataCache.GetGfxObj(mr.GfxObjId)?.BSP?.Root is not null)) // The CylSphere catches the base/trunk that BSP parts might miss.
{ {
var setup = _physicsDataCache.GetSetup(entity.SourceGfxObjOrSetupId); var setup = _physicsDataCache.GetSetup(entity.SourceGfxObjOrSetupId);
if (setup is not null && setup.CylSpheres.Count > 0) if (setup is not null && setup.CylSpheres.Count > 0)
@ -1813,6 +1813,7 @@ public sealed class GameWindow : IDisposable
float cylRadius = cyl.Radius > 0 ? cyl.Radius : setup.Radius; float cylRadius = cyl.Radius > 0 ? cyl.Radius : setup.Radius;
if (cylRadius > 0) if (cylRadius > 0)
{ {
// Use entity.Id directly (not partId) for the CylSphere entry.
_physicsEngine.ShadowObjects.Register( _physicsEngine.ShadowObjects.Register(
entity.Id, entity.SourceGfxObjOrSetupId, entity.Id, entity.SourceGfxObjOrSetupId,
entity.Position + new System.Numerics.Vector3(cyl.Origin.X, cyl.Origin.Y, cyl.Origin.Z), entity.Position + new System.Numerics.Vector3(cyl.Origin.X, cyl.Origin.Y, cyl.Origin.Z),
@ -1821,8 +1822,9 @@ public sealed class GameWindow : IDisposable
AcDream.Core.Physics.ShadowCollisionType.Cylinder, cyl.Height); AcDream.Core.Physics.ShadowCollisionType.Cylinder, cyl.Height);
} }
} }
else if (setup is not null && setup.Spheres.Count > 0) else if (setup is not null && setup.Spheres.Count > 0 && partIndex == 0)
{ {
// Fallback: use bounding sphere only if no BSP and no CylSphere.
var sph = setup.Spheres[0]; var sph = setup.Spheres[0];
if (sph.Radius > 0) if (sph.Radius > 0)
{ {