fix(physics): validate retail cell containment roots
This commit is contained in:
parent
7716c2ee89
commit
3e0f3b6206
23 changed files with 429 additions and 197 deletions
|
|
@ -608,6 +608,13 @@ public static class CellTransit
|
|||
}
|
||||
else
|
||||
{
|
||||
// CELLARRAY stores GetVisible's result beside every id.
|
||||
// Retail skips a later candidate whose cell pointer is
|
||||
// null (0052b588..0052b59f), even when a stale building
|
||||
// record for that landcell remains cached.
|
||||
if (cache.CellGraph.GetVisible(cellId) is null)
|
||||
continue;
|
||||
|
||||
// CLandCell::find_transit_cells (0x00533800):
|
||||
// add_all_outside_cells (added_outside-guarded) then the
|
||||
// building bridge for the landcell's building, if any.
|
||||
|
|
@ -666,9 +673,9 @@ public static class CellTransit
|
|||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A missing <see cref="CellPhysics"/> record is unavailable and skipped.
|
||||
/// A loaded record whose authored containment root is null retains retail's
|
||||
/// <see cref="BSPQuery.PointInsideCellBsp"/> universal-inside base case.
|
||||
/// A missing or rootless <see cref="CellPhysics"/> record is unavailable
|
||||
/// and skipped. The retail inside base case belongs to a missing positive
|
||||
/// child below a valid root, not to the root itself.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static uint FindVisibleChildCell(
|
||||
|
|
@ -705,8 +712,8 @@ public static class CellTransit
|
|||
/// <summary>
|
||||
/// <c>CEnvCell::point_in_cell</c> (cell-BSP vtable[0x84]) against a world point:
|
||||
/// transform to the cell's local frame, then <see cref="BSPQuery.PointInsideCellBsp"/>.
|
||||
/// A missing cell payload returns false; a loaded payload with a null root
|
||||
/// returns true through the retail BSP base case.
|
||||
/// A missing/rootless payload returns false. Retail also returns false
|
||||
/// before containment when <c>CEnvCell::portals</c> is null.
|
||||
/// </summary>
|
||||
private static bool PointInCell(
|
||||
PhysicsDataCache cache,
|
||||
|
|
@ -714,6 +721,7 @@ public static class CellTransit
|
|||
Vector3 worldPoint)
|
||||
{
|
||||
if (cell is null ||
|
||||
cell.Portals.Count == 0 ||
|
||||
!CollisionTraversal.HasCellContainment(cache, cell))
|
||||
{
|
||||
return false;
|
||||
|
|
@ -920,6 +928,11 @@ public static class CellTransit
|
|||
|
||||
if ((cellId & 0xFFFFu) < 0x0100u)
|
||||
{
|
||||
// Match CELLARRAY's stored GetVisible pointer: an adjacent
|
||||
// landcell id may be present because the sphere overlaps it,
|
||||
// while that landblock is not loaded yet.
|
||||
if (cache.CellGraph.GetVisible(cellId) is null)
|
||||
continue;
|
||||
// Landcell dispatch — CLandCell::find_transit_cells (0x00533800)
|
||||
// → CSortCell::find_transit_cells (0x00534060, this->building)
|
||||
// → CBuildingObj::find_building_transit_cells (0x006b5230)
|
||||
|
|
@ -994,17 +1007,13 @@ public static class CellTransit
|
|||
{
|
||||
// Interior candidate — point_in_cell via the cell BSP (vtable[0x84]).
|
||||
var cand = cache.GetCellStruct(candId);
|
||||
if (cand is null ||
|
||||
!CollisionTraversal.HasCellContainment(cache, cand))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var local = Vector3.Transform(worldSphereCenter, cand.InverseWorldTransform);
|
||||
if (CollisionTraversal.PointInsideCell(cache, cand, local))
|
||||
if (PointInCell(cache, cand, worldSphereCenter))
|
||||
return candId; // interior-wins, stop (pseudo_c:308819)
|
||||
}
|
||||
else if (outdoorResult == 0u && containingOutdoorId != 0u && outdoorPickAllowed)
|
||||
else if (outdoorResult == 0u &&
|
||||
containingOutdoorId != 0u &&
|
||||
outdoorPickAllowed &&
|
||||
cache.CellGraph.GetVisible(candId) is not null)
|
||||
{
|
||||
// Outdoor candidate — CLandCell::point_in_cell is the XY-column the
|
||||
// sphere is over (acdream landcells have no BSP point_in_cell; the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue