namespace AcDream.Core.Physics;
///
/// Central routing for full retail MotionCommand values after the wire's
/// 16-bit command id has been reconstructed.
///
/// Retail/ACE split motion commands by class mask:
/// - Action and ChatEmote commands play through link/action data.
/// - Modifier commands play through modifier data.
/// - SubState commands become the new cyclic state.
/// - Style/UI/Toggle commands do not directly drive an animation overlay here.
///
/// References:
/// CMotionTable::GetObjectSequence 0x00522860,
/// CMotionInterp::DoInterpretedMotion 0x00528360,
/// ACE MotionTable.GetObjectSequence, and
/// docs/research/deepdives/r03-motion-animation.md section 3.
///
public static class AnimationCommandRouter
{
private const uint ActionMask = 0x10000000u;
private const uint ModifierMask = 0x20000000u;
private const uint SubStateMask = 0x40000000u;
private const uint ClassMask = 0xFF000000u;
///
/// Classifies a reconstructed full MotionCommand.
///
public static AnimationCommandRouteKind Classify(uint fullCommand)
{
if (fullCommand == 0)
return AnimationCommandRouteKind.None;
uint cls = fullCommand & ClassMask;
if (cls == 0x12000000u || cls == 0x13000000u)
return AnimationCommandRouteKind.ChatEmote;
if ((fullCommand & ModifierMask) != 0)
return AnimationCommandRouteKind.Modifier;
if ((fullCommand & ActionMask) != 0)
return AnimationCommandRouteKind.Action;
if ((fullCommand & SubStateMask) != 0)
return AnimationCommandRouteKind.SubState;
return AnimationCommandRouteKind.Ignored;
}
///
/// Reconstructs and routes a 16-bit wire command.
///
public static AnimationCommandRouteKind RouteWireCommand(
AnimationSequencer sequencer,
uint currentStyle,
ushort wireCommand,
float speedMod = 1f)
{
uint fullCommand = MotionCommandResolver.ReconstructFullCommand(wireCommand);
return RouteFullCommand(sequencer, currentStyle, fullCommand, speedMod);
}
///
/// Routes a full MotionCommand to the matching sequencer API.
///
public static AnimationCommandRouteKind RouteFullCommand(
AnimationSequencer sequencer,
uint currentStyle,
uint fullCommand,
float speedMod = 1f)
{
var route = Classify(fullCommand);
switch (route)
{
case AnimationCommandRouteKind.Action:
case AnimationCommandRouteKind.Modifier:
case AnimationCommandRouteKind.ChatEmote:
sequencer.PlayAction(fullCommand, speedMod);
break;
case AnimationCommandRouteKind.SubState:
sequencer.SetCycle(currentStyle, fullCommand, speedMod);
break;
}
return route;
}
}
public enum AnimationCommandRouteKind
{
None = 0,
Action,
Modifier,
ChatEmote,
SubState,
Ignored,
}