feat(physics #145): Slice 2a — PhysicsBody carries CellPosition; setter delta-syncs into the cell frame
Add CellPosition (retail Position type) alongside the world Vector3 Position. The Position setter mirrors each world delta into the cell-local origin and calls AdjustToOutside only when the local coord crosses a landblock boundary ([0,192) on X or Y), so the within-block cell id is preserved from the wire seed. SnapToCell seeds both positions from the wire's (cell, local) pair verbatim — no streaming center involved. Unseeded bodies (ObjCellId==0) and indoor cells are no-ops in the delta path. UpdatePhysicsInternal's existing `Position +=` desugars through the new setter automatically; no call sites changed. 4 new unit tests; full Core suite 1526 passed / 0 failed / 2 skipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c980763322
commit
afe495b9e6
2 changed files with 118 additions and 2 deletions
|
|
@ -91,8 +91,73 @@ public sealed class PhysicsBody
|
|||
// ── struct fields ──────────────────────────────────────────────────────
|
||||
// Offsets from acclient_function_map.md §PhysicsObj Struct Layout.
|
||||
|
||||
/// <summary>World-space position (no offset in struct — frame origin).</summary>
|
||||
public Vector3 Position { get; set; }
|
||||
private Vector3 _position;
|
||||
|
||||
/// <summary>World-space position (frame origin). #145: the setter mirrors its
|
||||
/// delta into <see cref="CellPosition"/> so the cell-relative frame stays exact
|
||||
/// through integration + the resolve-apply (both write here). Placement uses
|
||||
/// <see cref="SnapToCell"/> instead (no delta).</summary>
|
||||
public Vector3 Position
|
||||
{
|
||||
get => _position;
|
||||
set
|
||||
{
|
||||
Vector3 delta = value - _position;
|
||||
_position = value;
|
||||
SyncCellPositionDelta(delta);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cell-relative position (retail Position): the (cell, local∈[0,192)) pair.
|
||||
/// #145 Slice 2 — rides alongside <see cref="Position"/>, becomes authoritative
|
||||
/// in later slices. Default (ObjCellId==0) until <see cref="SnapToCell"/> seeds it.
|
||||
/// </summary>
|
||||
public Position CellPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Placement: set the world position AND seed <see cref="CellPosition"/> from the
|
||||
/// wire's (cell, local) directly — NO streaming center, NO delta. Stores the wire
|
||||
/// values verbatim; the caller has already decoded the wire into a consistent
|
||||
/// (cellId, cellLocal) pair. Caller (Slice 2b) supplies the wire (cell, localX/Y/Z)
|
||||
/// from the inbound position update / teleport arrival.
|
||||
/// </summary>
|
||||
public void SnapToCell(uint cellId, Vector3 worldPos, Vector3 cellLocal)
|
||||
{
|
||||
_position = worldPos;
|
||||
CellPosition = new Position(cellId, new CellFrame(cellLocal, 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 re-wraps into [0,192) and bumps the cell on a landblock crossing.
|
||||
// Only for a SEEDED OUTDOOR cell (player). Unseeded bodies (ObjCellId==0, e.g. remote
|
||||
// entities) and indoor cells are left untouched here.
|
||||
// We only invoke AdjustToOutside when the local coordinate leaves [0,192) — i.e. on
|
||||
// an actual landblock-boundary crossing. Within the same landblock the cell id is left
|
||||
// unchanged (the wire seed is authoritative for which cell index we occupy).
|
||||
private void SyncCellPositionDelta(Vector3 delta)
|
||||
{
|
||||
uint cell = CellPosition.ObjCellId;
|
||||
if ((cell & 0xFFFFu) is not (>= 1u and <= 0x40u))
|
||||
return;
|
||||
Vector3 local = CellPosition.Frame.Origin + delta;
|
||||
// Only call AdjustToOutside on a landblock-boundary crossing (local X or Y
|
||||
// leaves [0, BlockLength)). Within the block the cell index is kept as-is.
|
||||
if (local.X < 0f || local.X >= LandDefs.BlockLength ||
|
||||
local.Y < 0f || local.Y >= LandDefs.BlockLength)
|
||||
{
|
||||
uint adjusted = cell;
|
||||
if (LandDefs.AdjustToOutside(ref adjusted, ref local))
|
||||
CellPosition = new Position(adjusted, new CellFrame(local, CellPosition.Frame.Orientation));
|
||||
// else: map-edge (AdjustToOutside failed) — leave CellPosition unchanged.
|
||||
// Slice 3 owns proper map-edge membership; this slice is behaviour-neutral.
|
||||
}
|
||||
else
|
||||
{
|
||||
CellPosition = new Position(cell, new CellFrame(local, CellPosition.Frame.Orientation));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Orientation quaternion (struct offsets 0x60–0x80 column matrix).</summary>
|
||||
public Quaternion Orientation { get; set; } = Quaternion.Identity;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue