merge: animation overhaul branch (Opus agent, 10 commits, +32 tests)
Resolves remote-chars-lagging-forward, no-anim-speed-scaling, and monster/NPC Commands-list (waves/attacks/deaths) not animating. Adds dead-reckoning + sequence-wide velocity/omega + Commands[] list parsing + MotionCommandResolver + soft-snap residual. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
862cd5662f
8 changed files with 1273 additions and 46 deletions
|
|
@ -110,6 +110,74 @@ public class UpdateMotionTests
|
|||
Assert.Null(result.Value.MotionState.ForwardCommand);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesForwardSpeed_WhenSpeedFlagSet()
|
||||
{
|
||||
// Flags = CurrentStyle | ForwardCommand | ForwardSpeed (0x1|0x2|0x10 = 0x13)
|
||||
// Test value: 1.5× speed — matches a typical RunRate broadcast.
|
||||
var body = new byte[4 + 4 + 2 + 6 + 4 + 4 + 2 + 2 + 4];
|
||||
int p = 0;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), 0xF74Cu); p += 4;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), 0x1A2B3C4Du); p += 4;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0); p += 2;
|
||||
p += 6; // MovementData header
|
||||
body[p++] = 0;
|
||||
body[p++] = 0;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0); p += 2;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), 0x13u); p += 4;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0x003D); p += 2; // NonCombat
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0x0007); p += 2; // RunForward
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 1.5f); p += 4; // speed
|
||||
|
||||
var result = UpdateMotion.TryParse(body);
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal((ushort)0x003D, result!.Value.MotionState.Stance);
|
||||
Assert.Equal((ushort)0x0007, result.Value.MotionState.ForwardCommand);
|
||||
Assert.Equal(1.5f, result.Value.MotionState.ForwardSpeed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesCommandsList_Wave()
|
||||
{
|
||||
// A typical NPC wave broadcast:
|
||||
// - stance NonCombat (0x003D)
|
||||
// - ForwardCommand flag set, command = 0x0003 (Ready)
|
||||
// - numCommands = 1, with a single MotionItem{ cmd=0x0087 Wave, seq=0, speed=1.0 }
|
||||
//
|
||||
// Packed u32 = (flags | numCommands << 7)
|
||||
// flags = 0x01 (CurrentStyle) | 0x02 (ForwardCommand) = 0x03
|
||||
// numCommands << 7 = 1 << 7 = 0x80
|
||||
// total = 0x83
|
||||
var body = new byte[4 + 4 + 2 + 6 + 4 + 4 + 2 + 2 + 8];
|
||||
int p = 0;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), 0xF74Cu); p += 4;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), 0xDEADBEEFu); p += 4;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0); p += 2;
|
||||
p += 6;
|
||||
body[p++] = 0;
|
||||
body[p++] = 0;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0); p += 2;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), 0x83u); p += 4; // flags=0x3 + numCommands=1
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0x003D); p += 2; // stance
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0x0003); p += 2; // fwd cmd = Ready
|
||||
|
||||
// MotionItem: u16 command + u16 packedSeq + f32 speed
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0x0087); p += 2; // Wave
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0x0001); p += 2;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 1.0f); p += 4;
|
||||
|
||||
var result = UpdateMotion.TryParse(body);
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal((ushort)0x003D, result!.Value.MotionState.Stance);
|
||||
Assert.Equal((ushort)0x0003, result.Value.MotionState.ForwardCommand);
|
||||
|
||||
Assert.NotNull(result.Value.MotionState.Commands);
|
||||
Assert.Single(result.Value.MotionState.Commands!);
|
||||
var wave = result.Value.MotionState.Commands![0];
|
||||
Assert.Equal((ushort)0x0087, wave.Command);
|
||||
Assert.Equal(1.0f, wave.Speed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesNonInvalidMovementType_GracefullyReturnsOuterStance()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue