feat(pipeline): WCST combat stats baseline catalog (130th open format)

Novel replacement for the per-class per-level base-stat scaling
table that vanilla WoW scattered across CharBaseInfo.dbc +
CharStartOutfit.dbc + GtChanceTo*.dbc + the hard-coded HP/mana-
per-level constants in the server's StatSystem. Each WCST entry
binds one (classId, level) pair to base health, mana, armor, and
the five primary stats (Str/Agi/Sta/Int/Spi).

Sparse design: presets emit ~6 sample levels per class with the
runtime stat-interpolator computing intermediate levels.

Three presets:
  --gen-cst-warrior   Warrior (classId=1) sparse sample at L1/
                      10/20/30/40/60. baseMana=0 across all
                      entries (Warrior uses Rage)
  --gen-cst-mage      Mage (classId=8) same 6 levels with mana
                      growth tracking Intellect
  --gen-cst-starting  All 9 vanilla classes at level 1 — shows
                      per-class flat starting differences
                      (Warrior/Paladin high Str; Hunter/Rogue
                      high Agi; Mage/Priest/Warlock high Int;
                      Shaman/Druid balanced)

Validator catches: id+classId+level required, classId 1..11,
level 1..60, zero baseHealth (player would die instantly),
duplicate statIds, duplicate (classId,level) pairs (runtime
stat-lookup tie). Warns on classId 6/10 (DK/Monk gap unused
in vanilla), Warrior/Rogue baseMana > 0 (these classes use
Rage/Energy not mana), and per-class monotonicity violations
across all 8 stats — sorts by level, walks adjacent pairs,
flags any stat that regresses as level increases (typo guard).

Format count 129 -> 130. CLI flag count 1364 -> 1371.
This commit is contained in:
Kelsi 2026-05-10 04:08:41 -07:00
parent f41f913a2a
commit b66e41df87
10 changed files with 722 additions and 0 deletions

View file

@ -2573,6 +2573,16 @@ void printUsage(const char* argv0) {
std::printf(" Export binary .wprt to a human-editable JSON sidecar (defaults to <base>.wprt.json; emits factionAccess and portalKind as int + name string; floats preserved bit-for-bit)\n");
std::printf(" --import-wprt-json <json-path> [out-base]\n");
std::printf(" Import a .wprt.json sidecar back into binary .wprt (factionAccess int OR \"both\"/\"alliance\"/\"horde\"/\"neutral\"; portalKind int OR \"teleport\"/\"portal\")\n");
std::printf(" --gen-cst-warrior <wcst-base> [name]\n");
std::printf(" Emit .wcst Warrior (classId=1) sparse base-stats sample at levels 1/10/20/30/40/60. baseMana=0 (Warrior uses Rage)\n");
std::printf(" --gen-cst-mage <wcst-base> [name]\n");
std::printf(" Emit .wcst Mage (classId=8) sparse base-stats sample at the same 6 levels with mana growth tracking Intellect\n");
std::printf(" --gen-cst-starting <wcst-base> [name]\n");
std::printf(" Emit .wcst all 9 vanilla classes at level 1 — illustrates per-class flat starting-stat differences (Warrior/Paladin high Str, Hunter/Rogue high Agi, Mage/Priest/Warlock high Int, Shaman/Druid balanced)\n");
std::printf(" --info-wcst <wcst-base> [--json]\n");
std::printf(" Print WCST entries (statId / class / level / hp / mana / Str/Agi/Sta/Int/Spi / armor)\n");
std::printf(" --validate-wcst <wcst-base> [--json]\n");
std::printf(" Static checks: statId+classId+level required, classId 1..11, level 1..60, no zero baseHealth (player would die instantly), no duplicate statIds, no duplicate (classId,level) pairs (runtime stat-lookup tie); warns on classId 6/10 (DK/Monk gap unused in vanilla), Warrior/Rogue baseMana > 0 (these classes use Rage/Energy not mana — likely typo), and per-class monotonicity violations across all 8 stats (any stat regressing as level increases)\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");