Port retail portal viewport projection and reveal behavior, preserve outbound combat style, drive remote and local grounded movement from authored CSequence root frames, and reuse the local prepared pose so animation hooks advance once. User-verified portal, observer movement, combat stance, and short-tap locomotion gates. Release build passed with 5,767 tests and five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
77 lines
2.7 KiB
C#
77 lines
2.7 KiB
C#
using AcDream.Content.Vfx;
|
|
using AcDream.Core.Physics;
|
|
using AcDream.Core.Tests.Conformance;
|
|
using DatReaderWriter;
|
|
using DatReaderWriter.DBObjs;
|
|
using DatReaderWriter.Options;
|
|
using DatReaderWriter.Types;
|
|
|
|
namespace AcDream.Core.Tests.Physics;
|
|
|
|
/// <summary>
|
|
/// Pins the retail Humanoid motion table data consumed by CSequence root motion.
|
|
/// The packet-driven remote mover must use these values, not a velocity guessed
|
|
/// from the interpreted command.
|
|
/// </summary>
|
|
public sealed class HumanoidMotionTableRootMotionTests
|
|
{
|
|
private const uint HumanoidMotionTable = 0x09000001u;
|
|
private const uint HumanoidSetup = 0x02000001u;
|
|
private const uint NonCombatStyle = 0x8000003Du;
|
|
private const uint WalkForward = 0x40000005u;
|
|
private const uint RunForward = 0x40000007u;
|
|
private const uint Ready = 0x41000003u;
|
|
private const uint InterpretedWalkForward = 0x45000005u;
|
|
|
|
[Theory]
|
|
[InlineData(WalkForward)]
|
|
[InlineData(RunForward)]
|
|
public void RetailLocomotionCycle_ExposesItsLiteralMotionDataVelocity(uint motion)
|
|
{
|
|
string? datDir = ConformanceDats.ResolveDatDir();
|
|
if (datDir is null)
|
|
return;
|
|
|
|
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
|
MotionTable table = Assert.IsType<MotionTable>(dats.Get<MotionTable>(HumanoidMotionTable));
|
|
int key = unchecked((int)((NonCombatStyle << 16) | (motion & 0x00FF_FFFFu)));
|
|
MotionData cycle = Assert.IsType<MotionData>(table.Cycles[key]);
|
|
|
|
Assert.Equal(System.Numerics.Vector3.Zero, cycle.Velocity);
|
|
}
|
|
|
|
[Fact]
|
|
public void RetailWalkSequence_ProducesAuthoredRootDisplacement()
|
|
{
|
|
string? datDir = ConformanceDats.ResolveDatDir();
|
|
if (datDir is null)
|
|
return;
|
|
|
|
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
|
Setup setup = Assert.IsType<Setup>(dats.Get<Setup>(HumanoidSetup));
|
|
MotionTable table = Assert.IsType<MotionTable>(dats.Get<MotionTable>(HumanoidMotionTable));
|
|
var sequencer = new AnimationSequencer(
|
|
setup,
|
|
table,
|
|
new RetailAnimationLoader(dats));
|
|
|
|
sequencer.SetCycle(NonCombatStyle, Ready);
|
|
sequencer.SetCycle(NonCombatStyle, InterpretedWalkForward);
|
|
|
|
float distance = 0f;
|
|
for (int i = 0; i < 120; i++)
|
|
{
|
|
var root = new Frame
|
|
{
|
|
Origin = System.Numerics.Vector3.Zero,
|
|
Orientation = System.Numerics.Quaternion.Identity,
|
|
};
|
|
sequencer.Advance(1f / 60f, root);
|
|
distance += root.Origin.Length();
|
|
}
|
|
|
|
Assert.True(
|
|
distance > 1f,
|
|
$"Installed retail WalkForward should author root travel; got {distance:F3} m over 2 s.");
|
|
}
|
|
}
|