fix(physics): port retail slope landing stop
This commit is contained in:
parent
1d8371dbe5
commit
5a0f9868a6
13 changed files with 870 additions and 103 deletions
|
|
@ -483,6 +483,54 @@ public class BSPStepUpTests
|
|||
"wall (failed step-up should preserve LastKnownContactPlane).");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Campaign P #269: retail validate_transition calls kill_velocity before
|
||||
/// restoring a last-known contact plane on a non-OK collision recovery.
|
||||
/// This is the post-landing stop mechanism when the 5% reflect points away
|
||||
/// from a slope but the next tangential sweep collides with it again.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void D1b_LastKnownPlaneCollisionRecovery_KillsVelocity()
|
||||
{
|
||||
var (root, resolved) = BSPStepUpFixtures.TallWall();
|
||||
var transition = BSPStepUpFixtures.MakeGroundedTransition(
|
||||
from: new Vector3(0.1f, 0f, 0f),
|
||||
to: new Vector3(0.6f, 0f, 0f),
|
||||
stepUpHeight: 0.04f);
|
||||
var engine = MakeTestEngine(root, resolved, terrainZ: 0f);
|
||||
|
||||
transition.FindTransitionalPosition(engine);
|
||||
|
||||
Assert.True(
|
||||
transition.ObjectInfo.VelocityKilled,
|
||||
"Retail OBJECTINFO::kill_velocity must run before restoring the " +
|
||||
"remembered floor after a collision/slide retry.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The same remembered plane must not kill velocity or re-ground a clean
|
||||
/// accepted move away from it. Retail consumes LastKnownContactPlane only
|
||||
/// inside validate_transition's non-OK recovery branch.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void D1c_LastKnownPlaneCleanAdvance_DoesNotKillOrReground()
|
||||
{
|
||||
var (root, resolved) = BSPStepUpFixtures.TallWall();
|
||||
var transition = BSPStepUpFixtures.MakeAirborneTransition(
|
||||
from: new Vector3(-2f, 0f, 2f),
|
||||
to: new Vector3(-2f, 0f, 2.25f));
|
||||
transition.CollisionInfo.LastKnownContactPlane = new Plane(Vector3.UnitZ, -1.52f);
|
||||
transition.CollisionInfo.LastKnownContactPlaneValid = true;
|
||||
var engine = MakeTestEngine(root, resolved, terrainZ: -50f);
|
||||
|
||||
transition.FindTransitionalPosition(engine);
|
||||
|
||||
Assert.False(transition.ObjectInfo.VelocityKilled);
|
||||
Assert.False(transition.CollisionInfo.ContactPlaneValid);
|
||||
Assert.False(transition.CollisionInfo.LastKnownContactPlaneValid);
|
||||
Assert.False(transition.ObjectInfo.OnWalkable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// L.2.3b regression: Path 5 dispatch must be guarded against re-entry while
|
||||
/// a step-up is already in progress. Test runs <c>FindTransitionalPosition</c>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
using System.Numerics;
|
||||
using System.Text.Json;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
|
||||
namespace AcDream.Runtime.Tests.Gameplay;
|
||||
|
||||
[Collection(PlayerPhysicsQuantumCaptureCollection.Name)]
|
||||
public sealed class PlayerPhysicsQuantumCaptureTests : IDisposable
|
||||
{
|
||||
private readonly string _path = Path.Combine(
|
||||
Path.GetTempPath(),
|
||||
$"acdream-player-physics-{Guid.NewGuid():N}.jsonl");
|
||||
|
||||
public PlayerPhysicsQuantumCaptureTests() =>
|
||||
PlayerPhysicsQuantumCapture.ResetForTest();
|
||||
|
||||
[Fact]
|
||||
public void DisabledCapture_DoesNotCreateAFile()
|
||||
{
|
||||
PhysicsBody body = CreateBody();
|
||||
|
||||
Assert.False(PlayerPhysicsQuantumCapture.IsEnabled);
|
||||
_ = PlayerPhysicsQuantumCapture.Snapshot(body);
|
||||
Assert.False(File.Exists(_path));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnabledCapture_WritesCompleteOrderedQuantum()
|
||||
{
|
||||
PhysicsBody body = CreateBody();
|
||||
PlayerPhysicsQuantumCapture.CapturePath = _path;
|
||||
|
||||
PlayerPhysicsBodyTraceSnapshot start =
|
||||
PlayerPhysicsQuantumCapture.Snapshot(body);
|
||||
body.Velocity = new Vector3(2f, 3f, -4f);
|
||||
PlayerPhysicsBodyTraceSnapshot pre =
|
||||
PlayerPhysicsQuantumCapture.Snapshot(body);
|
||||
body.UpdatePhysicsInternal(0.05f);
|
||||
PlayerPhysicsBodyTraceSnapshot post =
|
||||
PlayerPhysicsQuantumCapture.Snapshot(body);
|
||||
body.Velocity = new Vector3(1f, 1.5f, 0.2f);
|
||||
PlayerPhysicsBodyTraceSnapshot committed =
|
||||
PlayerPhysicsQuantumCapture.Snapshot(body);
|
||||
|
||||
PlayerPhysicsQuantumCapture.Log(
|
||||
dt: 0.05f,
|
||||
cellBefore: 0xAAB40011,
|
||||
cellAfter: 0xAAB40012,
|
||||
input: new MovementInput(Forward: true, Run: true),
|
||||
rootAndManagerDelta: new Vector3(0.1f, 0.2f, 0.3f),
|
||||
candidateMoved: true,
|
||||
quantumStart: start,
|
||||
preIntegration: pre,
|
||||
postIntegration: post,
|
||||
resolve: new PlayerPhysicsResolveTraceSnapshot(
|
||||
new Vector3(4f, 5f, 6f),
|
||||
0xAAB40012,
|
||||
Ok: true,
|
||||
IsOnGround: true,
|
||||
InContact: true,
|
||||
OnWalkable: true,
|
||||
CollisionNormalValid: true,
|
||||
CollisionNormal: Vector3.UnitZ),
|
||||
postCommit: committed);
|
||||
PlayerPhysicsQuantumCapture.Close();
|
||||
|
||||
string[] lines = File.ReadAllLines(_path);
|
||||
Assert.Single(lines);
|
||||
|
||||
using JsonDocument json = JsonDocument.Parse(lines[0]);
|
||||
JsonElement root = json.RootElement;
|
||||
Assert.Equal(0, root.GetProperty("sequence").GetInt64());
|
||||
Assert.Equal(0.05f, root.GetProperty("dt").GetSingle());
|
||||
Assert.Equal(0xAAB40011u, root.GetProperty("cellBefore").GetUInt32());
|
||||
Assert.Equal(0xAAB40012u, root.GetProperty("cellAfter").GetUInt32());
|
||||
Assert.True(root.GetProperty("input").GetProperty("forward").GetBoolean());
|
||||
Assert.True(root.GetProperty("input").GetProperty("run").GetBoolean());
|
||||
Assert.Equal(2f,
|
||||
root.GetProperty("preIntegration")
|
||||
.GetProperty("velocity")
|
||||
.GetProperty("x")
|
||||
.GetSingle());
|
||||
Assert.True(root.GetProperty("resolve").GetProperty("onWalkable").GetBoolean());
|
||||
Assert.Equal(0.2f,
|
||||
root.GetProperty("postCommit")
|
||||
.GetProperty("velocity")
|
||||
.GetProperty("z")
|
||||
.GetSingle());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResetForTest_ClosesWriterAndRestartsSequence()
|
||||
{
|
||||
PhysicsBody body = CreateBody();
|
||||
PlayerPhysicsQuantumCapture.CapturePath = _path;
|
||||
PlayerPhysicsBodyTraceSnapshot snapshot =
|
||||
PlayerPhysicsQuantumCapture.Snapshot(body);
|
||||
|
||||
WriteMinimal(snapshot);
|
||||
PlayerPhysicsQuantumCapture.ResetForTest();
|
||||
PlayerPhysicsQuantumCapture.CapturePath = _path;
|
||||
WriteMinimal(snapshot);
|
||||
PlayerPhysicsQuantumCapture.Close();
|
||||
|
||||
string[] lines = File.ReadAllLines(_path);
|
||||
Assert.Equal(2, lines.Length);
|
||||
Assert.All(lines, line =>
|
||||
{
|
||||
using JsonDocument json = JsonDocument.Parse(line);
|
||||
Assert.Equal(0, json.RootElement.GetProperty("sequence").GetInt64());
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
PlayerPhysicsQuantumCapture.ResetForTest();
|
||||
if (File.Exists(_path))
|
||||
File.Delete(_path);
|
||||
}
|
||||
|
||||
private void WriteMinimal(PlayerPhysicsBodyTraceSnapshot snapshot) =>
|
||||
PlayerPhysicsQuantumCapture.Log(
|
||||
0.04f,
|
||||
1,
|
||||
1,
|
||||
default,
|
||||
Vector3.Zero,
|
||||
false,
|
||||
snapshot,
|
||||
snapshot,
|
||||
snapshot,
|
||||
new PlayerPhysicsResolveTraceSnapshot(
|
||||
snapshot.Position,
|
||||
1,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
Vector3.Zero),
|
||||
snapshot);
|
||||
|
||||
private static PhysicsBody CreateBody() => new()
|
||||
{
|
||||
Position = new Vector3(1f, 2f, 3f),
|
||||
Velocity = new Vector3(4f, 5f, 6f),
|
||||
Acceleration = new Vector3(0f, 0f, -9.8f),
|
||||
GroundNormal = Vector3.UnitZ,
|
||||
ContactPlaneValid = true,
|
||||
ContactPlane = new Plane(Vector3.UnitZ, -3f),
|
||||
Friction = 0.95f,
|
||||
Elasticity = 0.05f,
|
||||
State = PhysicsStateFlags.Gravity,
|
||||
TransientState =
|
||||
TransientStateFlags.Contact | TransientStateFlags.OnWalkable,
|
||||
};
|
||||
}
|
||||
|
||||
[CollectionDefinition(Name, DisableParallelization = true)]
|
||||
public sealed class PlayerPhysicsQuantumCaptureCollection
|
||||
{
|
||||
public const string Name = "Player physics quantum capture";
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue