feat(pipeline): WPHM player movement-to-animation map (127th open format)

Novel replacement for the implicit movementState->animation
binding that vanilla WoW baked into per-race M2 model files.
Each WPHM entry binds one (raceId, genderId, movementState)
tuple to a base M2 animation sequence id, an optional variant
sequence (drunk-walk, wounded-run), and a blend transition
duration in milliseconds.

8-state machine: Idle / Walk / Run / Swim / Fly / Sit / Mount /
Death. Three presets each emit the full 16 bindings (M+F):
  --gen-phm-human   16 bindings with drunk-walk variant on Walk
  --gen-phm-orc     16 bindings with AttackRun variant on Run
                    for war-stance flavor
  --gen-phm-undead  16 bindings with canonical shambling variant
                    (anim 38) on Run for low-health renderer
                    override + slower swim transition (undead are
                    awkward in water)

Validator catches: id required, raceId 1..10, genderId 0..1,
movementState 0..7, no duplicate mapIds, no duplicate
(race,gender,state) triples (renderer dispatch ambiguity),
baseAnimId=0 forbidden on non-Idle states (model would freeze
when entering that state). Warns on variantAnimId==baseAnimId
(no-op overhead) and transitionMs > 2000 (would feel like
animation hang).

Format count 126 -> 127. CLI flag count 1337 -> 1344.
This commit is contained in:
Kelsi 2026-05-10 03:44:31 -07:00
parent e652f8595d
commit 949a6e0182
10 changed files with 770 additions and 0 deletions

View file

@ -2531,6 +2531,16 @@ void printUsage(const char* argv0) {
std::printf(" Export binary .wspk to a human-editable JSON sidecar (defaults to <base>.wspk.json; emits spellIds as JSON int array preserving display order)\n");
std::printf(" --import-wspk-json <json-path> [out-base]\n");
std::printf(" Import a .wspk.json sidecar back into binary .wspk (spellIds array preserves order — round-trips per-tab spell lists byte-identical)\n");
std::printf(" --gen-phm-human <wphm-base> [name]\n");
std::printf(" Emit .wphm Human Male+Female 8-state machine (16 entries) with drunk-walk variant on Walk state\n");
std::printf(" --gen-phm-orc <wphm-base> [name]\n");
std::printf(" Emit .wphm Orc Male+Female 8-state machine (16 entries) with AttackRun variant on Run for war-stance flavor\n");
std::printf(" --gen-phm-undead <wphm-base> [name]\n");
std::printf(" Emit .wphm Undead Male+Female with shambling variant on Run (low-health renderer override) and slower swim transition (undead are awkward in water)\n");
std::printf(" --info-wphm <wphm-base> [--json]\n");
std::printf(" Print WPHM bindings (id / race / gender / movementState / baseAnimId / variantAnimId / transitionMs)\n");
std::printf(" --validate-wphm <wphm-base> [--json]\n");
std::printf(" Static checks: id required, raceId 1..10, genderId 0..1, movementState 0..7, no duplicate mapIds, no duplicate (race,gender,state) triples (renderer dispatch ambiguity), baseAnimId=0 forbidden on non-Idle states (would freeze model); warns on variantAnimId==baseAnimId no-op and transitionMs > 2000 animation hang\n");
std::printf(" --catalog-pluck <wXXX-file> <id> [--json]\n");
std::printf(" Extract one entry by id from any registered catalog format. Auto-detects magic, dispatches to the per-format --info-* handler internally, then prints just the matching entry. Primary-key field is auto-detected (first *Id field, or first numeric)\n");
std::printf(" --catalog-find <directory> <id> [--magic <WXXX>] [--json]\n");