fix(portal): synchronize destination presentation state

This commit is contained in:
Erik 2026-07-16 21:17:13 +02:00
parent 4b1bceefbb
commit e95f55f25b
42 changed files with 2815 additions and 288 deletions

View file

@ -200,19 +200,63 @@ public sealed class PhysicsBody
}
}
/// <summary>
/// Commits the position and full cell returned by a successful transition.
/// Retail <c>CPhysicsObj::SetPositionInternal(CTransition const*)</c>
/// (<c>0x00515330</c>) always commits both
/// <c>sphere_path.curr_pos.objcell_id</c> and its frame, including EnvCells.
/// The ordinary <see cref="Position"/> setter first carries the accepted world
/// displacement into the landblock-relative frame; this method then adopts the
/// resolver's exact cell identity. Outdoor frames are canonicalized across 24 m
/// cells and 192 m landblock boundaries. Indoor frames retain their carried
/// landblock-relative origin and adopt the resolved EnvCell id.
/// </summary>
public void CommitTransitionPosition(uint resolvedCellId, Vector3 worldPosition)
{
Position = worldPosition;
if (CellPosition.ObjCellId == 0 || resolvedCellId == 0)
return;
uint cell = resolvedCellId;
Vector3 local = CellPosition.Frame.Origin;
if ((cell & 0xFFFFu) is >= 1u and <= 0x40u
&& !LandDefs.AdjustToOutside(ref cell, ref local))
{
// At the map edge retail's transition does not yield a successful
// outside cell. Preserve the carried frame instead of inventing one.
return;
}
CellPosition = new Position(
cell,
new CellFrame(local, CellPosition.Frame.Orientation));
}
// Mirror a world-position translation into the cell-relative frame. Velocity is
// frame-invariant under translation, so the same delta applies to the local origin.
// AdjustToOutside then recomputes the cell index from the local (intra-landblock 24 m
// cell crossings) AND wraps + bumps the landblock on a 192 m crossing — the outdoor
// membership + canonicalization in one call (get_outside_lcoord + lcoord_to_gid +
// [0,192) wrap). Idempotent within a cell. Runs only for a SEEDED OUTDOOR cell;
// unseeded bodies (ObjCellId==0, e.g. remote entities) and indoor cells are skipped.
// [0,192) wrap). Idempotent within a cell. A SEEDED indoor body carries the same
// landblock-relative delta but keeps its EnvCell identity until the transition result
// commits the exact destination via CommitTransitionPosition. Unseeded bodies are skipped.
private void SyncCellPositionDelta(Vector3 delta)
{
uint cell = CellPosition.ObjCellId;
if ((cell & 0xFFFFu) is not (>= 1u and <= 0x40u))
if (cell == 0)
return;
Vector3 local = CellPosition.Frame.Origin + delta;
if ((cell & 0xFFFFu) is not (>= 1u and <= 0x40u))
{
CellPosition = new Position(
cell,
new CellFrame(local, CellPosition.Frame.Orientation));
return;
}
uint adjusted = cell;
if (LandDefs.AdjustToOutside(ref adjusted, ref local))
CellPosition = new Position(adjusted, new CellFrame(local, CellPosition.Frame.Orientation));