feat(vfx): port retail hidden and teleport presentation
Preserve canonical live-object ownership across Hidden transitions and remote teleport placement so effects, collision, streaming, and targeting remain synchronized.
This commit is contained in:
parent
a51ebc66e9
commit
1e98d81448
46 changed files with 4883 additions and 127 deletions
194
tests/AcDream.App.Tests/Physics/RemoteTeleportPlacementTests.cs
Normal file
194
tests/AcDream.App.Tests/Physics/RemoteTeleportPlacementTests.cs
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Physics;
|
||||
using AcDream.Core.Physics;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.App.Tests.Physics;
|
||||
|
||||
public sealed class RemoteTeleportPlacementTests
|
||||
{
|
||||
[Fact]
|
||||
public void Apply_HardSnapsFullFrameAndPhysicsClock()
|
||||
{
|
||||
var body = new PhysicsBody
|
||||
{
|
||||
Orientation = Quaternion.Identity,
|
||||
LastUpdateTime = 2.0,
|
||||
};
|
||||
body.SnapToCell(
|
||||
0x01010001u,
|
||||
new Vector3(3f, 4f, 5f),
|
||||
new Vector3(3f, 4f, 5f));
|
||||
var remote = new AcDream.App.Rendering.GameWindow.RemoteMotion(body);
|
||||
Quaternion orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, 0.75f);
|
||||
|
||||
RemoteTeleportPlacement.Apply(
|
||||
remote,
|
||||
body,
|
||||
new ResolveResult(
|
||||
new Vector3(210f, 220f, 12f),
|
||||
0x02020123u,
|
||||
IsOnGround: false),
|
||||
new Vector3(18f, 28f, 12f),
|
||||
orientation,
|
||||
17.5,
|
||||
previousContact: body.InContact,
|
||||
previousOnWalkable: body.OnWalkable);
|
||||
|
||||
Assert.Equal(new Vector3(210f, 220f, 12f), body.Position);
|
||||
Assert.Equal(0x02020123u, body.CellPosition.ObjCellId);
|
||||
Assert.Equal(new Vector3(18f, 28f, 12f), body.CellPosition.Frame.Origin);
|
||||
Assert.Equal(orientation, body.Orientation);
|
||||
Assert.Equal(17.5, body.LastUpdateTime);
|
||||
Assert.True(body.InWorld);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Apply_InstallsPlacementDerivedContactAndSynchronizesAirborneState()
|
||||
{
|
||||
var velocity = new Vector3(1f, 2f, 3f);
|
||||
var body = new PhysicsBody
|
||||
{
|
||||
Velocity = velocity,
|
||||
TransientState = TransientStateFlags.Active,
|
||||
State = PhysicsStateFlags.Gravity | PhysicsStateFlags.ReportCollisions,
|
||||
};
|
||||
var contactPlane = new Plane(Vector3.UnitZ, 0f);
|
||||
var remote = new AcDream.App.Rendering.GameWindow.RemoteMotion(body)
|
||||
{
|
||||
Airborne = true,
|
||||
};
|
||||
|
||||
RemoteTeleportPlacement.Apply(
|
||||
remote,
|
||||
body,
|
||||
new ResolveResult(
|
||||
new Vector3(200f, 200f, 50f),
|
||||
0x02020001u,
|
||||
IsOnGround: true,
|
||||
InContact: true,
|
||||
OnWalkable: true,
|
||||
ContactPlane: contactPlane,
|
||||
ContactPlaneCellId: 0x02020001u),
|
||||
new Vector3(8f, 8f, 50f),
|
||||
Quaternion.Identity,
|
||||
9.0,
|
||||
previousContact: false,
|
||||
previousOnWalkable: false);
|
||||
|
||||
Assert.Equal(Vector3.Zero, body.Velocity);
|
||||
Assert.True(body.InContact);
|
||||
Assert.True(body.OnWalkable);
|
||||
Assert.True(body.ContactPlaneValid);
|
||||
Assert.Equal(contactPlane, body.ContactPlane);
|
||||
Assert.False(remote.Airborne);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Apply_PendingHydrationUsesPreservedSourceFlagsBeforeCollisionResponse()
|
||||
{
|
||||
var originalVelocity = new Vector3(-3f, 0f, 0f);
|
||||
var body = new PhysicsBody
|
||||
{
|
||||
Velocity = originalVelocity,
|
||||
State = PhysicsStateFlags.Gravity | PhysicsStateFlags.ReportCollisions,
|
||||
};
|
||||
// ParkPending deliberately clears these flags while the destination
|
||||
// landblock is absent. SetPositionInternal must still receive the
|
||||
// grounded source edge captured before parking.
|
||||
body.TransientState &= ~(TransientStateFlags.Contact | TransientStateFlags.OnWalkable);
|
||||
var remote = new AcDream.App.Rendering.GameWindow.RemoteMotion(body)
|
||||
{
|
||||
Airborne = true,
|
||||
};
|
||||
|
||||
RemoteTeleportPlacement.Apply(
|
||||
remote,
|
||||
body,
|
||||
new ResolveResult(
|
||||
new Vector3(200f, 200f, 50f),
|
||||
0x02020001u,
|
||||
IsOnGround: true,
|
||||
CollisionNormalValid: true,
|
||||
CollisionNormal: Vector3.UnitX,
|
||||
InContact: true,
|
||||
OnWalkable: true,
|
||||
ContactPlane: new Plane(Vector3.UnitZ, 0f),
|
||||
ContactPlaneCellId: 0x02020001u),
|
||||
new Vector3(8f, 8f, 50f),
|
||||
Quaternion.Identity,
|
||||
9.0,
|
||||
previousContact: true,
|
||||
previousOnWalkable: true);
|
||||
|
||||
// Staying on walkable ground suppresses reflection. If the parked
|
||||
// false/false flags were used, the -X velocity would reflect to +X.
|
||||
Assert.Equal(originalVelocity, body.Velocity);
|
||||
Assert.True(body.OnWalkable);
|
||||
Assert.False(remote.Airborne);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Apply_PendingGroundToSteepContact_RestoresSourceWalkabilityForFirstAcceleration()
|
||||
{
|
||||
var body = new PhysicsBody
|
||||
{
|
||||
Omega = new Vector3(0f, 0f, 3f),
|
||||
State = PhysicsStateFlags.Gravity | PhysicsStateFlags.ReportCollisions,
|
||||
};
|
||||
// The unloaded-destination parking frame cleared both bits. Retail
|
||||
// restores the captured source OnWalkable bit through the first
|
||||
// calc_acceleration, which clears grounded angular drift, before
|
||||
// set_on_walkable installs the steep destination's false value.
|
||||
body.TransientState &= ~(TransientStateFlags.Contact | TransientStateFlags.OnWalkable);
|
||||
var remote = new AcDream.App.Rendering.GameWindow.RemoteMotion(body);
|
||||
|
||||
RemoteTeleportPlacement.Apply(
|
||||
remote,
|
||||
body,
|
||||
new ResolveResult(
|
||||
new Vector3(200f, 200f, 50f),
|
||||
0x02020001u,
|
||||
IsOnGround: false,
|
||||
InContact: true,
|
||||
OnWalkable: false,
|
||||
ContactPlane: new Plane(Vector3.Normalize(new Vector3(1f, 0f, 0.2f)), 0f),
|
||||
ContactPlaneCellId: 0x02020001u),
|
||||
new Vector3(8f, 8f, 50f),
|
||||
Quaternion.Identity,
|
||||
9.0,
|
||||
previousContact: true,
|
||||
previousOnWalkable: true);
|
||||
|
||||
Assert.Equal(Vector3.Zero, body.Omega);
|
||||
Assert.True(body.InContact);
|
||||
Assert.False(body.OnWalkable);
|
||||
Assert.True(remote.Airborne);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Apply_RejectsMalformedPlacementWithoutMutatingBody()
|
||||
{
|
||||
var original = new Vector3(7f, 8f, 9f);
|
||||
var body = new PhysicsBody { Position = original };
|
||||
var remote = new AcDream.App.Rendering.GameWindow.RemoteMotion(body);
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
RemoteTeleportPlacement.Apply(
|
||||
remote,
|
||||
body,
|
||||
new ResolveResult(
|
||||
new Vector3(float.NaN, 0f, 0f),
|
||||
0,
|
||||
IsOnGround: false,
|
||||
Ok: false),
|
||||
Vector3.Zero,
|
||||
Quaternion.Identity,
|
||||
double.NaN,
|
||||
previousContact: false,
|
||||
previousOnWalkable: false));
|
||||
|
||||
Assert.Equal(original, body.Position);
|
||||
Assert.Equal(0u, body.CellPosition.ObjCellId);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue