feat(runtime): prepare authored SetPosition movers
This commit is contained in:
parent
237d1184d2
commit
442cb8f97b
8 changed files with 1516 additions and 70 deletions
|
|
@ -0,0 +1,156 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Runtime.Entities;
|
||||
|
||||
namespace AcDream.Runtime.Physics;
|
||||
|
||||
internal enum RuntimeSetPositionMoverPreparationStatus
|
||||
{
|
||||
Prepared,
|
||||
RetrySetupUnavailable,
|
||||
RejectedAuthority,
|
||||
InvalidData,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Distinguishes a Setup payload which has not arrived yet from a completed
|
||||
/// lookup whose result is absent. Retail supplies its dummy placement sphere
|
||||
/// only inside <c>CPhysicsObj::SetPosition</c>; an unavailable lookup must not
|
||||
/// manufacture that fallback while asynchronous preparation is still live.
|
||||
/// </summary>
|
||||
internal readonly record struct RuntimeSetPositionMoverSetup(
|
||||
bool IsResolved,
|
||||
uint SetupTableId,
|
||||
FlatSetupCollision? Collision)
|
||||
{
|
||||
internal static RuntimeSetPositionMoverSetup Unavailable => default;
|
||||
|
||||
internal static RuntimeSetPositionMoverSetup ResolvedAbsent =>
|
||||
new(true, 0u, null);
|
||||
|
||||
internal static RuntimeSetPositionMoverSetup Resolved(
|
||||
uint setupTableId,
|
||||
FlatSetupCollision collision) =>
|
||||
new(
|
||||
true,
|
||||
setupTableId != 0u
|
||||
? setupTableId
|
||||
: throw new ArgumentOutOfRangeException(nameof(setupTableId)),
|
||||
collision ?? throw new ArgumentNullException(nameof(collision)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit, immutable inputs surrounding retail's authored mover shape.
|
||||
/// None of these values are inferred from presentation state.
|
||||
/// </summary>
|
||||
internal readonly record struct RuntimeSetPositionMoverPreparation(
|
||||
RuntimeSetPositionMoverSetup Setup,
|
||||
RuntimeSetPositionOperationKind Kind,
|
||||
double GameTime,
|
||||
PhysicsPlacementClass PlacementClass,
|
||||
PhysicsSetPositionFlags Flags,
|
||||
Vector3 Line = default,
|
||||
float ScatterRadiusX = 0f,
|
||||
float ScatterRadiusY = 0f,
|
||||
uint ScatterAttempts = 0u,
|
||||
float ShadowWorldOffsetX = 0f,
|
||||
float ShadowWorldOffsetY = 0f,
|
||||
RuntimePortalPlacementAuthority Portal = default);
|
||||
|
||||
/// <summary>
|
||||
/// Pure preparation port of the mover inputs consumed by retail
|
||||
/// <c>CPhysicsObj::SetPosition</c> (0x005160C0). It preserves the complete DAT
|
||||
/// sphere order; Core's SPHEREPATH port applies retail's two-sphere cap later.
|
||||
/// </summary>
|
||||
internal static class RuntimeSetPositionMoverPreparer
|
||||
{
|
||||
internal static bool TryBuild(
|
||||
RuntimeEntityRecord record,
|
||||
in CreateObject.ServerPosition acceptedPosition,
|
||||
uint canonicalSetupTableId,
|
||||
RuntimeSetPositionOperationKind acceptedKind,
|
||||
RuntimePortalPlacementAuthority acceptedPortal,
|
||||
ulong velocityAuthorityVersion,
|
||||
in RuntimeSetPositionMoverPreparation preparation,
|
||||
out RuntimeSetPositionCommand command)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
command = default;
|
||||
if (!preparation.Setup.IsResolved
|
||||
|| preparation.Kind != acceptedKind
|
||||
|| preparation.Portal != acceptedPortal
|
||||
|| (canonicalSetupTableId == 0u
|
||||
? preparation.Setup.SetupTableId != 0u
|
||||
|| preparation.Setup.Collision is not null
|
||||
: preparation.Setup.SetupTableId != canonicalSetupTableId
|
||||
|| preparation.Setup.Collision is null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CreateObject.ServerPosition position = acceptedPosition;
|
||||
Vector3 cellLocal = new(
|
||||
position.PositionX,
|
||||
position.PositionY,
|
||||
position.PositionZ);
|
||||
Vector3 world = new(
|
||||
cellLocal.X + preparation.ShadowWorldOffsetX,
|
||||
cellLocal.Y + preparation.ShadowWorldOffsetY,
|
||||
cellLocal.Z);
|
||||
Quaternion orientation = new(
|
||||
position.RotationX,
|
||||
position.RotationY,
|
||||
position.RotationZ,
|
||||
position.RotationW);
|
||||
|
||||
float scale = record.Snapshot.Physics?.Scale
|
||||
?? record.Snapshot.ObjScale
|
||||
?? 1f;
|
||||
FlatSetupCollision? setup = preparation.Setup.Collision;
|
||||
ImmutableArray<FlatCollisionSphere> spheres = setup?.Spheres
|
||||
?? ImmutableArray<FlatCollisionSphere>.Empty;
|
||||
// CPartArray::GetStepUpHeight/GetStepDownHeight are Setup properties,
|
||||
// not sphere properties. An authored Setup with no collision spheres
|
||||
// still supplies both values; only a genuinely absent Setup takes the
|
||||
// retail dummy path with exact zero steps and no scale multiplication.
|
||||
float stepUp = setup is not null ? setup.StepUpHeight * scale : 0f;
|
||||
float stepDown = setup is not null ? setup.StepDownHeight * scale : 0f;
|
||||
|
||||
EntityCollisionFlags collisionFlags =
|
||||
EntityCollisionFlagsExt.FromPwdBitfield(
|
||||
record.Snapshot.ObjectDescriptionFlags ?? 0u);
|
||||
ObjectInfoState moverFlags = collisionFlags.ToMoverState();
|
||||
if (collisionFlags.HasFlag(EntityCollisionFlags.IsPlayer))
|
||||
moverFlags |= ObjectInfoState.IsPlayer;
|
||||
|
||||
var request = new PhysicsSetPositionRequest(
|
||||
world,
|
||||
orientation,
|
||||
position.LandblockId,
|
||||
cellLocal,
|
||||
spheres,
|
||||
scale,
|
||||
stepUp,
|
||||
stepDown,
|
||||
record.FinalPhysicsState,
|
||||
moverFlags,
|
||||
record.Key?.LocalEntityId ?? 0u,
|
||||
preparation.PlacementClass,
|
||||
preparation.Flags,
|
||||
preparation.Line,
|
||||
preparation.ScatterRadiusX,
|
||||
preparation.ScatterRadiusY,
|
||||
preparation.ScatterAttempts);
|
||||
command = new RuntimeSetPositionCommand(
|
||||
request,
|
||||
preparation.Kind,
|
||||
preparation.GameTime,
|
||||
velocityAuthorityVersion,
|
||||
preparation.ShadowWorldOffsetX,
|
||||
preparation.ShadowWorldOffsetY,
|
||||
preparation.Portal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue