using System;
using System.Collections.Generic;
using DRWMotionCommand = DatReaderWriter.Enums.MotionCommand;
namespace AcDream.Core.Physics;
///
/// Reconstructs the 32-bit retail value from
/// a 16-bit wire value broadcast in InterpretedMotionState.Commands[].
///
///
/// The server serializes MotionCommands as u16 (ACE
/// InterpretedMotionState.cs:139), truncating the class byte (Style /
/// SubState / Modifier / Action / ChatEmote / UI / Toggle / Mappable /
/// Command — see r03 §3.1). The client must re-attach the class byte before
/// routing the command into the motion table, because the same low 16 bits
/// can map to different classes (e.g. 0x0003 is Ready as a SubState,
/// but there's no other 0x0003).
///
///
///
/// This is implemented as an eager lookup table built from all values of
/// via reflection. If the wire value matches
/// more than one enum value (different class bits), we prefer the
/// lowest-class-numbered variant that has a non-zero class byte — roughly
/// matching retail priority (Action < Modifier < SubState < Style).
///
///
///
/// Cited references:
///
/// -
/// references/ACE/Source/ACE.Server/Network/Motion/InterpretedMotionState.cs::Write
/// L138-L144 — writer emits u16 for every command field.
///
/// -
/// references/ACE/Source/ACE.Entity/Enum/CommandMasks.cs — the
/// class bit assignments: 0x80=Style, 0x40=SubState, 0x20=Modifier,
/// 0x10=Action, 0x13 and 0x12=ChatEmote (with Mappable set), etc.
///
/// -
/// docs/research/deepdives/r03-motion-animation.md §3 — complete
/// command catalogue.
///
///
///
///
public static class MotionCommandResolver
{
// Lookup table built eagerly at type-init. Sparse: only values that
// appear in the DRW enum (which came from the generated protocol XML)
// are present. ~450 entries typical.
private static readonly Dictionary s_lookup = BuildLookup();
///
/// Given a 16-bit wire value, return the full 32-bit MotionCommand
/// (class byte restored). Returns 0 if no matching enum value exists.
///
public static uint ReconstructFullCommand(ushort wireCommand)
{
if (wireCommand == 0) return 0u;
s_lookup.TryGetValue(wireCommand, out var full);
return full;
}
private static Dictionary BuildLookup()
{
var result = new Dictionary(512);
var values = Enum.GetValues(typeof(DRWMotionCommand));
foreach (DRWMotionCommand v in values)
{
uint full = (uint)v;
ushort lo = (ushort)(full & 0xFFFFu);
if (lo == 0) continue; // Invalid / unmappable
// If a value with this low-16-bit already exists, keep the one
// with the lower class byte (Action=0x10 beats SubState=0x41
// beats Style=0x80). This matches retail: the server tends to
// emit Actions and ChatEmotes far more often than Styles, so
// the Action-class reconstruction is the common case.
if (!result.TryGetValue(lo, out var existing)
|| (full >> 24) < (existing >> 24))
{
result[lo] = full;
}
}
return result;
}
}