using System;
using System.Collections.Generic;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Types;
namespace AcDream.Core.Physics.Motion;
///
/// #175: the motion table's DEFAULT-STATE part pose — the pose an idle
/// entity's parts hold (retail: CMotionTable::SetDefaultState
/// 0x005230a0 installs StyleDefaults[DefaultStyle]'s cycle; the parts
/// then sit at that animation's frames — the live CPhysicsPart pose
/// collision tests against). Used as the BSP shadow-shape part-pose override
/// at server-entity registration (doors: the CLOSED pose).
///
///
/// Cycle key arithmetic mirrors 's
/// LookupCycle (CMotionTable.cs:683): (style << 16) |
/// (substate & 0xFFFFFF) — the raw dat Cycles dictionary is
/// keyed by the COMBINED word, not the bare style (the first cut of this
/// helper looked up the bare style, always missed, and silently fell back
/// to placement frames — the #175 "not fixed" report).
///
///
public static class MotionTablePose
{
///
/// Resolve the default-state pose frames. Returns null (callers fall
/// back to placement frames) when the table has no default-style
/// substate, no matching cycle, or no animation. A pose covering FEWER
/// parts than the Setup is returned as-is —
/// falls back to the placement frame
/// PER PART beyond the override's length (e.g. a door anim that poses
/// only the panel parts, not the BSP-less frame header).
///
/// The raw dat motion table (wire MotionTableId).
/// Animation loader (production:
/// id => dats.Get<Animation>(id)).
public static IReadOnlyList? DefaultStatePartFrames(
MotionTable mt,
Func loadAnimation)
{
if (mt is null) return null;
// SetDefaultState: StyleDefaults[DefaultStyle] → the default substate.
if (!mt.StyleDefaults.TryGetValue(mt.DefaultStyle, out var defaultSubstateCmd))
return null;
// LookupCycle key (CMotionTable.cs:683 — same wrap semantics).
uint style = (uint)mt.DefaultStyle;
uint substate = (uint)defaultSubstateCmd;
int key = (int)((style << 16) | (substate & 0xFFFFFFu));
if (!mt.Cycles.TryGetValue(key, out var cycle) || cycle.Anims.Count == 0)
return null;
var animRef = cycle.Anims[0];
var anim = loadAnimation(animRef.AnimId);
if (anim is null || anim.PartFrames.Count == 0) return null;
int idx = Math.Clamp((int)animRef.LowFrame, 0, anim.PartFrames.Count - 1);
var frames = anim.PartFrames[idx].Frames;
return frames.Count > 0 ? frames : null;
}
}