fix(anim): Phase L.1c match MoveTo run speed
Retail MovementManager::PerformMovement (0x00524440) reads MoveTo speed and runRate from the packet, MovementParameters::UnPackNet (0x0052AC50) defines the layout, and CMotionInterp::apply_run_to_command (0x00527BE0) multiplies RunForward by runRate. Parse those fields for UpdateMotion/CreateObject, seed server-controlled MoveTo locomotion with the retail speed multiplier, and avoid overriding active monster MoveTo with sparse UpdatePosition-derived velocity.
This commit is contained in:
parent
4dd8d4b46e
commit
9812965183
6 changed files with 246 additions and 26 deletions
|
|
@ -185,7 +185,8 @@ public class UpdateMotionTests
|
|||
[Fact]
|
||||
public void HandlesNonInvalidMovementType_GracefullyReturnsOuterStance()
|
||||
{
|
||||
// movementType != 0 means one of the Move* variants we don't parse.
|
||||
// movementType != 0 means one of the Move* variants; a truncated
|
||||
// non-Invalid payload still returns the outer state.
|
||||
// The parser must still return a valid Parsed with the outer stance
|
||||
// and a null ForwardCommand rather than failing the whole message.
|
||||
var body = new byte[4 + 4 + 2 + 6 + 4];
|
||||
|
|
@ -205,4 +206,50 @@ public class UpdateMotionTests
|
|||
Assert.Equal((byte)7, result.Value.MotionState.MovementType);
|
||||
Assert.True(result.Value.MotionState.IsServerControlledMoveTo);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesMoveToPositionSpeedAndRunRate()
|
||||
{
|
||||
// Layout after MovementData's movementType/motionFlags/currentStyle:
|
||||
// Origin: cell + xyz (16 bytes)
|
||||
// MoveToParameters: flags, distance, min, fail, speed,
|
||||
// walk/run threshold, desired heading (28 bytes)
|
||||
// runRate: f32
|
||||
var body = new byte[4 + 4 + 2 + 6 + 4 + 16 + 28 + 4];
|
||||
int p = 0;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), 0xF74Cu); p += 4;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), 0x80001234u); p += 4;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0); p += 2;
|
||||
p += 6;
|
||||
body[p++] = 7; // MoveToPosition
|
||||
body[p++] = 0;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(p), 0x003D); p += 2;
|
||||
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), 0xA8B4000Eu); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 10f); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 20f); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 30f); p += 4;
|
||||
|
||||
const uint canWalkCanRunMoveTowards = 0x1u | 0x2u | 0x200u;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(p), canWalkCanRunMoveTowards); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 0.6f); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 0.0f); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), float.MaxValue); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 1.25f); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 15.0f); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 90.0f); p += 4;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(body.AsSpan(p), 1.5f); p += 4;
|
||||
|
||||
var result = UpdateMotion.TryParse(body);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal((byte)7, result!.Value.MotionState.MovementType);
|
||||
Assert.True(result.Value.MotionState.IsServerControlledMoveTo);
|
||||
Assert.Equal((ushort)0x003D, result.Value.MotionState.Stance);
|
||||
Assert.Null(result.Value.MotionState.ForwardCommand);
|
||||
Assert.Equal(canWalkCanRunMoveTowards, result.Value.MotionState.MoveToParameters);
|
||||
Assert.Equal(1.25f, result.Value.MotionState.MoveToSpeed);
|
||||
Assert.Equal(1.5f, result.Value.MotionState.MoveToRunRate);
|
||||
Assert.True(result.Value.MotionState.MoveToCanRun);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,32 @@ public sealed class ServerControlledLocomotionTests
|
|||
Assert.Equal(1.0f, plan.SpeedMod);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlanMoveToStart_AppliesRetailRunRate()
|
||||
{
|
||||
var plan = ServerControlledLocomotion.PlanMoveToStart(
|
||||
moveToSpeed: 1.25f,
|
||||
runRate: 1.5f,
|
||||
canRun: true);
|
||||
|
||||
Assert.True(plan.IsMoving);
|
||||
Assert.Equal(MotionCommand.RunForward, plan.Motion);
|
||||
Assert.Equal(1.875f, plan.SpeedMod);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlanMoveToStart_UsesWalkWhenRunDisallowed()
|
||||
{
|
||||
var plan = ServerControlledLocomotion.PlanMoveToStart(
|
||||
moveToSpeed: 0.75f,
|
||||
runRate: 2.0f,
|
||||
canRun: false);
|
||||
|
||||
Assert.True(plan.IsMoving);
|
||||
Assert.Equal(MotionCommand.WalkForward, plan.Motion);
|
||||
Assert.Equal(0.75f, plan.SpeedMod);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlanFromVelocity_StopsBelowRetailNoiseThreshold()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue