feat(runtime): own SetPosition collision reports
This commit is contained in:
parent
ec627c13a2
commit
237d1184d2
19 changed files with 3744 additions and 103 deletions
|
|
@ -81,14 +81,53 @@ public static class PhysicsObjUpdate
|
|||
Action? leaveGround = null,
|
||||
Func<bool>? isCurrent = null,
|
||||
Func<bool>? isVelocityCurrent = null)
|
||||
{
|
||||
if (!CommitSetPositionContactTransition(
|
||||
body,
|
||||
inContact,
|
||||
onWalkable,
|
||||
previousOnWalkable,
|
||||
hitGround,
|
||||
leaveGround,
|
||||
isCurrent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Position, Vector, and Movement are independently timestamped but
|
||||
// can all install m_velocityVector. If a later one arrived from a
|
||||
// callback above, retain its vector and finish the non-overlapping
|
||||
// contact/pose commit without applying this older collision response.
|
||||
if (isVelocityCurrent?.Invoke() == false)
|
||||
return isCurrent?.Invoke() ?? true;
|
||||
|
||||
HandleAllCollisions(
|
||||
body,
|
||||
collisionNormalValid,
|
||||
collisionNormal,
|
||||
previousContact,
|
||||
previousOnWalkable,
|
||||
body.OnWalkable);
|
||||
return isCurrent?.Invoke() ?? true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits retail's Contact/OnWalkable and HitGround/LeaveGround prefix,
|
||||
/// stopping immediately before <c>handle_all_collisions</c>. Runtime uses
|
||||
/// this seam to run the canonical collision-table reports without adding
|
||||
/// another per-placement delegate allocation.
|
||||
/// </summary>
|
||||
public static bool CommitSetPositionContactTransition(
|
||||
PhysicsBody body,
|
||||
bool inContact,
|
||||
bool onWalkable,
|
||||
bool previousOnWalkable,
|
||||
Action? hitGround = null,
|
||||
Action? leaveGround = null,
|
||||
Func<bool>? isCurrent = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(body);
|
||||
|
||||
// SetPositionInternal replaces Contact first but retains the source
|
||||
// OnWalkable bit through its first calc_acceleration call. A deferred
|
||||
// teleport may have parked the live body with both bits cleared, so
|
||||
// restore the captured source bit explicitly before reproducing that
|
||||
// ordering.
|
||||
if (previousOnWalkable)
|
||||
body.TransientState |= TransientStateFlags.OnWalkable;
|
||||
else
|
||||
|
|
@ -106,10 +145,6 @@ public static class PhysicsObjUpdate
|
|||
else
|
||||
body.TransientState &= ~TransientStateFlags.OnWalkable;
|
||||
|
||||
// AP-10 (Campaign P Slice P4, 2026-07-30): mirror WATER_CONTACT_TS
|
||||
// alongside CONTACT_TS/ON_WALKABLE_TS, same as ApplySetPositionContact.
|
||||
// Callers (e.g. RemoteTeleportPlacement) already set body.ContactPlaneIsWater
|
||||
// before invoking this commit.
|
||||
if (body.ContactPlaneIsWater)
|
||||
body.TransientState |= TransientStateFlags.WaterContact;
|
||||
else
|
||||
|
|
@ -128,21 +163,6 @@ public static class PhysicsObjUpdate
|
|||
return false;
|
||||
}
|
||||
body.calc_acceleration();
|
||||
|
||||
// Position, Vector, and Movement are independently timestamped but
|
||||
// can all install m_velocityVector. If a later one arrived from a
|
||||
// callback above, retain its vector and finish the non-overlapping
|
||||
// contact/pose commit without applying this older collision response.
|
||||
if (isVelocityCurrent?.Invoke() == false)
|
||||
return isCurrent?.Invoke() ?? true;
|
||||
|
||||
HandleAllCollisions(
|
||||
body,
|
||||
collisionNormalValid,
|
||||
collisionNormal,
|
||||
previousContact,
|
||||
previousOnWalkable,
|
||||
finalOnWalkable);
|
||||
return isCurrent?.Invoke() ?? true;
|
||||
}
|
||||
|
||||
|
|
@ -179,14 +199,14 @@ public static class PhysicsObjUpdate
|
|||
// is now owned by the SetPositionInternal-derived contact flags, not a Velocity.Z<=0
|
||||
// gate). A grounded corridor wall-slide keeps its tangential velocity (should_reflect
|
||||
// false), exactly as retail.
|
||||
bool sledding = body.State.HasFlag(PhysicsStateFlags.Sledding);
|
||||
bool sledding = (body.State & PhysicsStateFlags.Sledding) != 0;
|
||||
bool shouldReflect = !(prevOnWalkable && nowOnWalkable && !sledding);
|
||||
|
||||
if (body.FramesStationaryFall <= 1)
|
||||
{
|
||||
if (shouldReflect && collisionNormalValid)
|
||||
{
|
||||
if (body.State.HasFlag(PhysicsStateFlags.Inelastic))
|
||||
if ((body.State & PhysicsStateFlags.Inelastic) != 0)
|
||||
{
|
||||
body.Velocity = Vector3.Zero; // pc:282720-282722
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1563,6 +1563,32 @@ public sealed class ShadowObjectRegistry
|
|||
internal bool HasLogicalOwner(uint entityId) =>
|
||||
_entityReg.ContainsKey(entityId);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the exact collision identity consumed by retail
|
||||
/// <c>CPhysicsObj::track_object_collision</c>. Reporting must classify
|
||||
/// an encountered object from the same retained shadow registration that
|
||||
/// produced the collision; presence of an entity id alone is not enough
|
||||
/// to infer either a static/environment collision or physics state.
|
||||
/// </summary>
|
||||
internal bool TryGetCollisionOwner(
|
||||
uint entityId,
|
||||
out uint physicsState,
|
||||
out bool isStatic)
|
||||
{
|
||||
if (_entityReg.TryGetValue(
|
||||
entityId,
|
||||
out RegistrationRecord? registration))
|
||||
{
|
||||
physicsState = registration.State;
|
||||
isStatic = registration.IsStatic;
|
||||
return true;
|
||||
}
|
||||
|
||||
physicsState = 0u;
|
||||
isStatic = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
public int PrefixOwnerSlotCapacityForDiagnostics(uint landblockId) =>
|
||||
_prefixOwnerSlots.TryGetValue(
|
||||
landblockId & 0xFFFF0000u,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue