using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
namespace AcDream.App.Physics;
///
/// Converts the nullable wire fields from an mt-0 UpdateMotion payload into
/// retail's wholesale value.
///
///
///
/// Retail constructs an InterpretedMotionState, whose constructor
/// supplies NonCombat/Ready/1.0 defaults, then calls UnPack over it
/// before MovementManager::move_to_interpreted_state copies every
/// field. See MovementManager::unpack_movement at 0x00524440 and
/// CMotionInterp::move_to_interpreted_state at 0x005289c0.
///
///
/// Keeping this conversion outside GameWindow gives local and remote
/// actors one definition of absent-field semantics and one command-list
/// reconstruction path.
///
///
internal static class InboundInterpretedMotionFactory
{
private const uint NonCombatStyle = 0x8000003Du;
private const uint Ready = 0x41000003u;
public static InboundInterpretedState Create(
in CreateObject.ServerMotionState wire,
uint fallbackForwardClass = 0x40000000u)
{
var result = new InboundInterpretedState
{
CurrentStyle = wire.Stance != 0
? 0x80000000u | wire.Stance
: NonCombatStyle,
ForwardCommand = ResolveForward(wire.ForwardCommand, fallbackForwardClass),
ForwardSpeed = wire.ForwardSpeed ?? 1f,
SideStepCommand = ResolveAxis(wire.SideStepCommand, 0x65000000u),
SideStepSpeed = wire.SideStepSpeed ?? 1f,
TurnCommand = ResolveAxis(wire.TurnCommand, 0x65000000u),
TurnSpeed = wire.TurnSpeed ?? 1f,
};
if (wire.Commands is { Count: > 0 } commands)
{
var actions = new List(commands.Count);
foreach (var item in commands)
{
uint command = MotionCommandResolver.ReconstructFullCommand(item.Command);
if (command == 0)
command = 0x10000000u | item.Command;
actions.Add(new InboundMotionAction(
command,
Stamp: item.PackedSequence & 0x7FFF,
Autonomous: (item.PackedSequence & 0x8000) != 0,
Speed: item.Speed));
}
result.Actions = actions;
}
return result;
}
private static uint ResolveForward(ushort? wireCommand, uint fallbackClass)
{
if (wireCommand is not { } command || command == 0)
return Ready;
uint resolved = MotionCommandResolver.ReconstructFullCommand(command);
if (resolved != 0)
return resolved;
uint commandClass = fallbackClass & 0xFF000000u;
return (commandClass != 0 ? commandClass : 0x40000000u) | command;
}
private static uint ResolveAxis(ushort? wireCommand, uint fallbackClass)
{
if (wireCommand is not { } command || command == 0)
return 0;
uint resolved = MotionCommandResolver.ReconstructFullCommand(command);
return resolved != 0 ? resolved : fallbackClass | command;
}
}