Kelsidavis-WoWee/tools/editor/cli_help.cpp

2223 lines
226 KiB
C++
Raw Normal View History

#include "cli_help.hpp"
#include <cstdio>
namespace wowee {
namespace editor {
namespace cli {
void printUsage(const char* argv0) {
std::printf("Usage: %s --data <path> [options]\n\n", argv0);
std::printf("Options:\n");
std::printf(" --data <path> Path to extracted WoW data (manifest.json)\n");
std::printf(" --adt <map> <x> <y> Load an ADT tile on startup\n");
std::printf(" --convert-m2 <path> Convert M2 model to WOM open format (no GUI)\n");
std::printf(" --convert-m2-batch <srcDir>\n");
std::printf(" Bulk M2→WOM conversion across every .m2 in <srcDir> (per-file pass/fail summary)\n");
std::printf(" --convert-wmo <path> Convert WMO building to WOB open format (no GUI)\n");
std::printf(" --convert-wmo-batch <srcDir>\n");
std::printf(" Bulk WMO→WOB conversion across every .wmo in <srcDir> (skips _NNN group files)\n");
std::printf(" --convert-dbc-batch <srcDir>\n");
std::printf(" Bulk DBC→JSON conversion across every .dbc in <srcDir> (sidecars next to source)\n");
std::printf(" --migrate-data-tree <srcDir>\n");
std::printf(" Run all four bulk converters (m2/wmo/blp/dbc) end-to-end on an extracted Data tree\n");
std::printf(" --info-data-tree <srcDir> [--json]\n");
std::printf(" Per-format migration-progress report (m2 vs wom, wmo vs wob, blp vs png, dbc vs json)\n");
std::printf(" --strip-data-tree <srcDir> [--dry-run]\n");
std::printf(" Delete proprietary files (.m2/.wmo/.blp/.dbc) that already have an open sidecar\n");
std::printf(" --audit-data-tree <srcDir>\n");
std::printf(" CI gate: exit 1 if any proprietary file lacks an open sidecar (100%% migration check)\n");
std::printf(" --bench-migrate-data-tree <srcDir> [--json]\n");
std::printf(" Time each step of --migrate-data-tree (m2/wmo/blp/dbc) and report wall-clock per step\n");
std::printf(" --list-data-tree-largest <srcDir> [N]\n");
std::printf(" Top-N largest proprietary files (.m2/.wmo/.blp/.dbc) for migration prioritization\n");
std::printf(" --export-data-tree-md <srcDir> [out.md]\n");
std::printf(" Markdown migration-progress report (per-pair table, share %%, recommended next steps)\n");
std::printf(" --gen-texture <out.png> <colorHex|pattern> [W H]\n");
std::printf(" Synthesize a placeholder texture (solid hex color or 'checker'/'grid'); default 256x256\n");
std::printf(" --gen-texture-gradient <out.png> <fromHex> <toHex> [vertical|horizontal] [W H]\n");
std::printf(" Synthesize a linear gradient PNG (default vertical, 256x256)\n");
std::printf(" --gen-texture-noise <out.png> [seed] [W H]\n");
std::printf(" Synthesize a smooth value-noise PNG (deterministic from seed; default 256x256)\n");
std::printf(" --gen-texture-noise-color <out.png> <colorAHex> <colorBHex> [seed] [W H]\n");
std::printf(" Same noise pattern but blended between two colors instead of grayscale\n");
std::printf(" --gen-texture-radial <out.png> <centerHex> <edgeHex> [W H]\n");
std::printf(" Synthesize a radial gradient PNG (center→edge, smooth distance-based blend)\n");
std::printf(" --gen-texture-stripes <out.png> <colorAHex> <colorBHex> [stripePx] [diagonal|horizontal|vertical] [W H]\n");
std::printf(" Synthesize a two-color stripe pattern (default 16px diagonal, 256x256)\n");
std::printf(" --gen-texture-dots <out.png> <bgHex> <dotHex> [dotRadius] [spacing] [W H]\n");
std::printf(" Synthesize a polka-dot pattern (default radius 8, spacing 32, 256x256)\n");
std::printf(" --gen-texture-rings <out.png> <colorAHex> <colorBHex> [ringPx] [W H]\n");
std::printf(" Synthesize concentric ring pattern (target/seal style; default 16px rings, 256x256)\n");
std::printf(" --gen-texture-checker <out.png> <colorAHex> <colorBHex> [cellPx] [W H]\n");
std::printf(" Synthesize checkerboard with custom colors (gen-texture's checker is BW only)\n");
std::printf(" --gen-texture-brick <out.png> <brickHex> <mortarHex> [brickW] [brickH] [mortarPx] [W H]\n");
std::printf(" Brick wall pattern with offset rows + mortar lines (default 64×24, 4px mortar)\n");
std::printf(" --gen-texture-wood <out.png> <lightHex> <darkHex> [grainSpacing] [seed] [W H]\n");
std::printf(" Wood grain pattern with vertical streaks + knots (default spacing 12px, seed 1)\n");
std::printf(" --gen-texture-grass <out.png> <baseHex> <bladeHex> [density] [seed] [W H]\n");
std::printf(" Tiling grass texture with random blade highlights (default density=0.15, seed=1)\n");
std::printf(" --gen-texture-fabric <out.png> <warpHex> <weftHex> [threadPx] [W H]\n");
std::printf(" Woven fabric pattern with alternating warp/weft threads (default thread=4px)\n");
std::printf(" --gen-texture-cobble <out.png> <stoneHex> <mortarHex> [stonePx] [seed] [W H]\n");
std::printf(" Cobblestone street pattern: irregular packed stones (default stone=24px, seed 1)\n");
std::printf(" --gen-texture-marble <out.png> <baseHex> <veinHex> [seed] [veinSharpness] [W H]\n");
std::printf(" Marble pattern with sinusoidal veining (default seed 1, sharpness 8)\n");
std::printf(" --gen-texture-metal <out.png> <baseHex> [seed] [orientation] [W H]\n");
std::printf(" Brushed metal: directional anisotropic noise (orientation: horizontal|vertical)\n");
std::printf(" --gen-texture-leather <out.png> <baseHex> [seed] [grainSize] [W H]\n");
std::printf(" Leather grain: irregular pebbled bumps via cellular noise (default grain=4px)\n");
std::printf(" --gen-texture-sand <out.png> <baseHex> [seed] [rippleSpacing] [W H]\n");
std::printf(" Sand dunes: per-pixel grain noise + sinusoidal ripple bands (default ripple=24px)\n");
std::printf(" --gen-texture-snow <out.png> <baseHex> [seed] [sparkleDensity] [W H]\n");
std::printf(" Snow: soft cool-white base + scattered bright sparkle pixels (default density=0.005)\n");
std::printf(" --gen-texture-lava <out.png> <darkHex> <hotHex> [seed] [crackScale] [W H]\n");
std::printf(" Lava: dark cooled crust with bright glowing cracks via Worley cell boundaries\n");
std::printf(" --gen-texture-tile <out.png> <tileHex> <groutHex> [tilePx] [groutPx] [W H]\n");
std::printf(" Square stone tiles with grout grid (default 32px tiles, 2px grout)\n");
std::printf(" --gen-texture-bark <out.png> <baseHex> <crackHex> [seed] [crackDensity] [W H]\n");
std::printf(" Tree bark: vertical wavy streaks + dark vertical cracks (default density=0.04)\n");
std::printf(" --gen-texture-clouds <out.png> <skyHex> <cloudHex> [seed] [coverage] [W H]\n");
std::printf(" Sky with puffy clouds: multi-octave noise thresholded by coverage (default 0.5)\n");
std::printf(" --gen-texture-stars <out.png> <bgHex> <starHex> [seed] [density] [W H]\n");
std::printf(" Night sky with scattered stars (varied brightness, density 0..1, default 0.005)\n");
std::printf(" --gen-texture-vines <out.png> <wallHex> <vineHex> [seed] [vineCount] [W H]\n");
std::printf(" Wall with climbing vines: meandering vertical paths via random walk (default 8 vines)\n");
std::printf(" --gen-texture-mosaic <out.png> <colorAHex> <colorBHex> <colorCHex> [tilePx] [seed] [W H]\n");
std::printf(" 3-color mosaic: small square tiles with random color picks + grout (default 16px)\n");
std::printf(" --gen-texture-rust <out.png> <metalHex> <rustHex> [seed] [coverage] [W H]\n");
std::printf(" Metal with rust patches: noise blob threshold + per-pixel grain (default coverage=0.4)\n");
std::printf(" --gen-texture-circuit <out.png> <pcbHex> <traceHex> [seed] [traceCount] [W H]\n");
std::printf(" Sci-fi PCB pattern: orthogonal traces with right-angle turns + via dots (default 24 traces)\n");
std::printf(" --gen-texture-coral <out.png> <waterHex> <coralHex> [seed] [branchCount] [W H]\n");
std::printf(" Coral reef: branching tree shapes via random angle walks (default 12 branches)\n");
std::printf(" --gen-texture-flame <out.png> <darkHex> <hotHex> [seed] [W H]\n");
std::printf(" Flame: vertical color gradient from dark base to hot top with noise flicker\n");
std::printf(" --gen-texture-tartan <out.png> <colorAHex> <colorBHex> <colorCHex> [bandPx] [W H]\n");
std::printf(" Tartan plaid: 3-color crossing bands forming Scottish-style overlap pattern\n");
std::printf(" --gen-texture-argyle <out.png> <colorAHex> <colorBHex> <stitchHex> [cellPx] [W H]\n");
std::printf(" Argyle: 45-rotated lozenge checkerboard with diagonal stitch lines (sweater knit)\n");
std::printf(" --gen-texture-herringbone <out.png> <bgHex> <lineHex> [stripH] [lineSpacing] [lineWidth] [W H]\n");
std::printf(" Herringbone: slanted parallel lines that flip direction every strip (chevron / fish-bone)\n");
std::printf(" --gen-texture-scales <out.png> <bgHex> <scaleHex> <rimHex> [cellW] [cellH] [W H]\n");
std::printf(" Scales: half-row-staggered overlapping circles forming fish/dragon/chain-mail look\n");
std::printf(" --gen-texture-stained-glass <out.png> <leadHex> <colorAHex> <colorBHex> <colorCHex> [cells] [W H]\n");
std::printf(" Stained glass: Voronoi cells in 3-color rotation, separated by dark lead lines\n");
std::printf(" --gen-texture-shingles <out.png> <baseHex> <shadowHex> <seamHex> [shingleW] [shingleH] [shadowH] [W H]\n");
std::printf(" Roof shingles: half-offset rows of rectangular tiles with shadow band + vertical seams\n");
std::printf(" --gen-texture-frost <out.png> <bgHex> <iceHex> [seeds] [rayLen] [W H]\n");
std::printf(" Frost: scattered crystal nuclei with 6-spike rosettes that fade with distance\n");
std::printf(" --gen-texture-parquet <out.png> <woodAHex> <woodBHex> <gapHex> [cellSize] [gapW] [W H]\n");
std::printf(" Parquet: basket-weave wood floor pattern with checkered horizontal/vertical plank pairs\n");
std::printf(" --gen-texture-bubbles <out.png> <bgHex> <fillHex> <rimHex> [count] [minR] [maxR] [rimW] [W H]\n");
std::printf(" Bubbles: scattered overlapping circles with bright rims (foam, water, magic)\n");
std::printf(" --gen-texture-spider-web <out.png> <bgHex> <webHex> [spokes] [rings] [W H]\n");
std::printf(" Spider web: N radial spokes + M concentric polygonal rings centered on the image\n");
std::printf(" --gen-texture-gingham <out.png> <bgHex> <stripeHex> <crossHex> [spacing] [width] [W H]\n");
std::printf(" Gingham: 3-tone fabric — bg + perpendicular stripes + darker color where they cross\n");
std::printf(" --gen-texture-lattice <out.png> <bgHex> <lineHex> [spacing] [width] [W H]\n");
std::printf(" Lattice: ±45° diagonal grid forming diamond openings (garden trellis / mesh fence)\n");
std::printf(" --gen-texture-honeycomb <out.png> <fillHex> <borderHex> [hexSide] [W H]\n");
std::printf(" Honeycomb: hexagonal cells via Voronoi over a triangular seed lattice\n");
std::printf(" --gen-texture-cracked <out.png> <bgHex> <crackHex> [seeds] [maxLength] [W H]\n");
std::printf(" Cracked: branching random walks form fissures (mud / glass / dry earth)\n");
std::printf(" --gen-texture-runes <out.png> <bgHex> <runeHex> [gridSpacing] [W H]\n");
std::printf(" Runes: scattered angular glyphs of 3-5 strokes each (8 cardinal/diagonal angles)\n");
std::printf(" --gen-texture-leopard <out.png> <bgHex> <spotHex> [count] [radius] [W H]\n");
std::printf(" Leopard: irregular spots (4 sub-circles each) for animal-print fabric/fur\n");
std::printf(" --gen-texture-zebra <out.png> <bgHex> <stripeHex> [period] [amplitude] [wavelength] [W H]\n");
std::printf(" Zebra: wavy parallel stripes via sinusoidal y-shift (animal-print stripes)\n");
std::printf(" --gen-texture-knit <out.png> <bgHex> <stitchHex> [cellW] [cellH] [strokeW] [W H]\n");
std::printf(" Knit: V-stitch chevron-zigzag pattern reading as knitted fabric stitches\n");
std::printf(" --gen-texture-chainmail <out.png> <bgHex> <ringHex> [cellW] [cellH] [ringR] [strokeW] [W H]\n");
std::printf(" Chainmail: brick-offset ring outlines for armor/mail textures (interlocking metal rings)\n");
std::printf(" --gen-texture-planks <out.png> <bgHex> <seamHex> [plankH] [grains/plank] [seed] [W H]\n");
std::printf(" Planks: horizontal floor boards with per-plank tint, grain streaks, and stagger seams\n");
std::printf(" --gen-texture-corrugated <out.png> <bgHex> <hiHex> [period] [v|h] [W H]\n");
std::printf(" Corrugated: smooth cosine ridges between bg and hi (sheet-metal roofing / siding)\n");
std::printf(" --gen-texture-rope <out.png> <bgHex> <ropeHex> [period] [strandW] [W H]\n");
std::printf(" Rope: two interleaved sinusoidal strands with cylindrical highlight (twisted cordage)\n");
std::printf(" --gen-texture-caustics <out.png> <bgHex> <hiHex> [period] [W H]\n");
std::printf(" Caustics: 4 superimposed sine waves (x/y/x+y/x-y) producing diamond-mesh water shimmer\n");
std::printf(" --gen-texture-starburst <out.png> <bgHex> <rayHex> [rays] [beamWidth] [W H]\n");
std::printf(" Starburst: N rays radiating from center with linear falloff (sun / holy symbol / mosaic hub)\n");
std::printf(" --gen-texture-studs <out.png> <bgHex> <studHex> [stride] [studR] [W H]\n");
std::printf(" Studs: rivet grid with derived inner highlight (3D rivet/stud appearance for armor + leather)\n");
std::printf(" --gen-texture-moss <out.png> <bgHex> <mossHex> [stride] [density 0-100] [seed] [W H]\n");
std::printf(" Moss: irregular spots scattered on a hash-jittered grid (forest floor / weathered stone / swamp)\n");
std::printf(" --gen-texture-woodgrain <out.png> <lightHex> <darkHex> [spacing] [seed] [W H]\n");
std::printf(" Woodgrain: concentric annual rings centered off-image with per-ring jitter (end-cut wood)\n");
std::printf(" --gen-texture-carbon <out.png> <bgHex> <fiberHex> [cellSize] [W H]\n");
std::printf(" Carbon-fiber: 2x2 alternating-orientation weave with sin² fiber highlights (sci-fi/tech panels)\n");
std::printf(" --gen-texture-pinstripe <out.png> <bgHex> <lineHex> [stride] [lineW] [featureEvery] [W H]\n");
std::printf(" Pinstripe: thin vertical lines at every stride; every Nth stripe is doubled-thick feature line\n");
std::printf(" --gen-texture-camo <out.png> <aHex> <bHex> [cellSize] [threshold] [seed] [W H]\n");
std::printf(" Camouflage: 2-octave value noise thresholded into hard bg/fg blobs (woodland disruption pattern)\n");
std::printf(" --gen-texture-snake-skin <out.png> <bgHex> <scaleHex> [cellW] [cellH] [outlineW] [W H]\n");
std::printf(" Snake skin: brick-offset diamond scales (L1 metric) with derived dark outline (reptile / dragon hide)\n");
std::printf(" --gen-texture-mesh-screen <out.png> <bgHex> <wireHex> [stride] [wireW] [W H]\n");
std::printf(" Mesh-screen: orthogonal H+V wire grid (window screen / chain-link / sci-fi grille)\n");
std::printf(" --gen-texture-bamboo <out.png> <bgHex> <bambooHex> [stalkW] [nodeY] [nodeBand] [seed] [W H]\n");
std::printf(" Bamboo: vertical stalks with sin² shading + horizontal node bands (jungle / asian-themed)\n");
std::printf(" --gen-texture-blueprint <out.png> <bgHex> <lineHex> [minorStride] [majorEvery] [minorW] [majorW] [W H]\n");
std::printf(" Blueprint: minor + major grid lines (technical drawing / drafting paper / engineer's table)\n");
std::printf(" --gen-texture-rust-streaks <out.png> <bgHex> <rustHex> [streakCount] [seed] [W H]\n");
std::printf(" Rust streaks: vertical drip bands fading down from hash-jittered tops (weathered metal / hull stains)\n");
std::printf(" --gen-texture-plaid <out.png> <bgHex> <bandHex> [stride] [bandW] [W H]\n");
std::printf(" Plaid: 2 sets of crossed translucent bands; intersections darken via combined half-alpha\n");
std::printf(" --gen-texture-diamond-grid <out.png> <bgHex> <fillHex> [cellW] [cellH] [fillFrac] [W H]\n");
std::printf(" Diamond grid: axis-aligned solid diamonds in cells with bg gaps (clean tile / mosaic / floor inlay)\n");
std::printf(" --gen-texture-houndstooth <out.png> <toothHex> <bgHex> [cellSize] [W H]\n");
std::printf(" Houndstooth: classic textile broken-check pattern via seamless 8x8 motif (Scottish weave)\n");
std::printf(" --gen-texture-chevron <out.png> <bgHex> <lineHex> [period] [stride] [lineW] [W H]\n");
std::printf(" Chevron: stack of V-shape stripes with sharp seams (military / sportswear / heraldic banners)\n");
std::printf(" --gen-texture-dunes <out.png> <bgHex> <lineHex> [verticalSpacing] [period] [amp] [lineW] [W H]\n");
std::printf(" Dunes: stack of parallel sinusoidal curves (desert ground / shallow-water sand / wave ripples)\n");
std::printf(" --gen-texture-swirl <out.png> <bgHex> <armHex> [armCount] [spiralFactor] [armWidth] [W H]\n");
std::printf(" Swirl: N-arm logarithmic spiral (magic sigils / summoning circles / ritual floor markings)\n");
std::printf(" --gen-texture-ironbark <out.png> <baseHex> <crackHex> [streakSpacing] [plateY] [crackW] [seed] [W H]\n");
std::printf(" Ironbark: vertical wood streaks + horizontal plate bands (mature hardwood / sycamore / ironwood)\n");
std::printf(" --gen-texture-mold <out.png> <bgHex> <moldHex> [stride] [thresholdFrac] [seed] [W H]\n");
std::printf(" Mold: Worley-noise field patches (cellars / dungeon walls / sewer overflow / fungal growth)\n");
std::printf(" --gen-texture-embroidery <out.png> <bgHex> <threadHex> [cellSize] [strokeW] [W H]\n");
std::printf(" Embroidery: grid of cross-stitch X marks (counted-thread textile / sampler / folk-art trim)\n");
std::printf(" --gen-texture-lightbeam <out.png> <bgHex> <beamHex> [beamHalfW] [vFadeFrac] [u|d] [W H]\n");
std::printf(" Lightbeam: vertical sun-ray gradient fading horizontally + vertically (sunbeam / holy radiance)\n");
std::printf(" --gen-texture-dewdrops <out.png> <bgHex> <dropHex> [dropCount] [maxR] [seed] [W H]\n");
std::printf(" Dewdrops: scattered translucent water drops with radial brightness (grass / glass / leaf surfaces)\n");
std::printf(" --gen-texture-pinwheel <out.png> <aHex> <bHex> [sectors] [W H]\n");
std::printf(" Pinwheel: alternating colored triangular wedges radiating from center (mandala / wind-rose / wheel inlay)\n");
std::printf(" --gen-texture-scratched-metal <out.png> <baseHex> <scratchHex> [scratchCount] [maxLen] [seed] [W H]\n");
std::printf(" Scratched metal: base color overlaid with N angled hash-derived line segments (worn armor / blades)\n");
std::printf(" --gen-texture-crackle <out.png> <baseHex> <crackHex> [stride] [crackW] [seed] [W H]\n");
std::printf(" Crackle: fine Voronoi cell-boundary cracks (dried mud / parched earth / aged leather)\n");
std::printf(" --gen-texture-star <out.png> <bgHex> <starHex> [points] [innerFrac] [W H]\n");
std::printf(" Star: solid N-pointed star polygon centered (medallions / shields / religious symbols)\n");
std::printf(" --gen-texture-halftone <out.png> <bgHex> <dotHex> [stride] [maxR] [v|h|r] [W H]\n");
std::printf(" Halftone: grid of dots whose radii grow with a v/h/r gradient (comic / newspaper print look)\n");
std::printf(" --gen-texture-bayer <out.png> <aHex> <bHex> [cellSize] [W H]\n");
std::printf(" Bayer: 4x4 ordered-dither matrix tiled (retro / 8-bit / monochrome-CRT effects)\n");
std::printf(" --gen-texture-moon <out.png> <bgHex> <moonHex> [moonR] [phase] [W H]\n");
std::printf(" Moon disc with optional crescent shadow (phase 0=full, =moonR=half, >moonR=crescent)\n");
std::printf(" --gen-texture-damask <out.png> <bgHex> <fgHex> [cell] [W H]\n");
std::printf(" Damask: 4-petal floral motif tiled per cell (palace wallpaper / noble-faction tapestry)\n");
std::printf(" --gen-texture-snowflake <out.png> <bgHex> <fgHex> [cell] [W H]\n");
std::printf(" Snowflake: 6-fold symmetric ice crystal stamped per cell (winter zones / frost spell effects)\n");
std::printf(" --gen-texture-gear-cog <out.png> <bgHex> <fgHex> [cell] [teeth] [W H]\n");
std::printf(" Gear cog: mechanical wheel with N alternating teeth + center axle hole (engineering / clockwork themes)\n");
std::printf(" --add-texture-to-zone <zoneDir> <png-path> [renameTo]\n");
std::printf(" Copy an existing PNG into <zoneDir> (optionally renaming it on the way in)\n");
std::printf(" --gen-mesh <wom-base> <cube|plane|sphere|cylinder|torus|cone|ramp> [size]\n");
std::printf(" Synthesize a procedural WOM primitive with proper normals, UVs, and bounds\n");
std::printf(" --gen-mesh-textured <wom-base> <cube|plane|sphere|cylinder|torus|cone|ramp> <colorHex|pattern> [size]\n");
std::printf(" Compose a procedural mesh + matching PNG texture wired into the WOM's batch\n");
std::printf(" --gen-mesh-stairs <wom-base> <steps> [stepHeight] [stepDepth] [width]\n");
std::printf(" Procedural straight staircase along +X with N steps (default 5 / 0.2 / 0.3 / 1.0)\n");
std::printf(" --gen-mesh-grid <wom-base> <subdivisions> [size]\n");
std::printf(" Subdivided flat plane on XY (NxN cells, 2N² triangles); useful for LOD demos\n");
std::printf(" --gen-mesh-disc <wom-base> [radius] [segments]\n");
std::printf(" Flat circular disc on XY centered at origin (default radius 1.0, 32 segments)\n");
std::printf(" --gen-mesh-tube <wom-base> [outerRadius] [innerRadius] [height] [segments]\n");
std::printf(" Hollow cylinder/pipe along Y axis (default 1.0/0.7/2.0, 24 segments)\n");
std::printf(" --gen-mesh-capsule <wom-base> [radius] [cylHeight] [segments] [stacks]\n");
std::printf(" Capsule along Y axis: cylinder body with hemispherical caps (default 0.5/1.0/16/8)\n");
std::printf(" --gen-mesh-arch <wom-base> [openingWidth] [openingHeight] [thickness] [depth] [segments]\n");
std::printf(" Doorway arch: two columns + semicircular top (default 1.0/1.5/0.2/0.3, 12 segs)\n");
std::printf(" --gen-mesh-pyramid <wom-base> [sides] [baseRadius] [height]\n");
std::printf(" N-sided polygonal pyramid with apex at +Y (default 4 sides, 1.0/1.0)\n");
std::printf(" --gen-mesh-fence <wom-base> [posts] [postSpacing] [postHeight] [railThick]\n");
std::printf(" Repeating fence: N posts along +X with two horizontal rails between\n");
std::printf(" --gen-mesh-tree <wom-base> [trunkRadius] [trunkHeight] [foliageRadius]\n");
std::printf(" --gen-mesh-rock <wom-base> [radius] [roughness] [subdiv] [seed]\n");
std::printf(" Procedural boulder via subdivided octahedron + smooth noise displacement\n");
std::printf(" --gen-mesh-pillar <wom-base> [radius] [height] [flutes] [capScale]\n");
std::printf(" Fluted classical column with concave flutes + flared cap/base (default 12 flutes)\n");
std::printf(" --gen-mesh-bridge <wom-base> [length] [width] [planks] [railHeight]\n");
std::printf(" Plank bridge with two side rails (default 6 planks across, rails on)\n");
std::printf(" --gen-mesh-tower <wom-base> [radius] [height] [battlements] [battlementH]\n");
std::printf(" Round castle tower with crenellated battlements (default 8 teeth, 0.5m tall)\n");
std::printf(" --gen-mesh-house <wom-base> [width] [depth] [height] [roofHeight]\n");
std::printf(" Simple house: cube body + pyramid roof (default 4×4×3 with 2m roof)\n");
std::printf(" --gen-mesh-fountain <wom-base> [basinRadius] [basinHeight] [spoutRadius] [spoutHeight]\n");
std::printf(" Round basin + center spout column (default 1.5/0.5 basin, 0.2/1.5 spout)\n");
std::printf(" --gen-mesh-statue <wom-base> [pedestalSize] [bodyHeight] [headRadius]\n");
std::printf(" Humanoid placeholder: pedestal block + tall body cylinder + head sphere\n");
std::printf(" --gen-mesh-altar <wom-base> [topRadius] [topHeight] [steps] [stepStride]\n");
std::printf(" Round altar: stacked stepped discs descending from a flat top (default 3 steps)\n");
std::printf(" --gen-mesh-portal <wom-base> [width] [height] [postThickness] [lintelHeight]\n");
std::printf(" Doorway frame: two side posts + top lintel (default 2.5w × 4h)\n");
std::printf(" --gen-mesh-archway <wom-base> [width] [pillarHeight] [thickness] [archSegs]\n");
std::printf(" Semicircular arched doorway: two pillars + curved keystone vault (default 12 segs)\n");
std::printf(" --gen-mesh-barrel <wom-base> [topRadius] [midRadius] [height] [hoopThickness]\n");
std::printf(" Tapered barrel: bulges in middle + 2 rim hoops (default 0.4/0.5/1.0/0.06)\n");
std::printf(" --gen-mesh-chest <wom-base> [width] [depth] [bodyHeight] [lidHeight]\n");
std::printf(" Treasure chest: body box + lid box + 3 iron bands + lock plate (default 1.4×0.9×0.9)\n");
std::printf(" --gen-mesh-anvil <wom-base> [length] [width] [hornLength] [bodyHeight]\n");
std::printf(" Blacksmith anvil: stepped pedestal + flat work surface + horn taper (default 1.0×0.4×0.5×0.5)\n");
std::printf(" --gen-mesh-mushroom <wom-base> [stalkRadius] [stalkHeight] [capRadius]\n");
std::printf(" Mushroom: cylindrical stalk + hemispherical cap (default 0.1/0.6/0.4)\n");
std::printf(" --gen-mesh-cart <wom-base> [bedLength] [bedWidth] [bedHeight] [wheelRadius]\n");
std::printf(" Wooden cart: rectangular bed + 2 cylindrical wheels (default 1.6/0.8/0.5/0.35)\n");
std::printf(" --gen-mesh-banner <wom-base> [poleHeight] [poleRadius] [flagWidth] [flagHeight]\n");
std::printf(" Banner: vertical pole + rectangular hanging flag (default 3.0/0.05/0.8/1.2)\n");
std::printf(" --gen-mesh-grave <wom-base> [tabletWidth] [tabletHeight] [tabletThickness] [baseWidth]\n");
std::printf(" Tombstone: low rectangular base + vertical tablet (default 0.6/1.0/0.15/0.8)\n");
std::printf(" --gen-mesh-bench <wom-base> [length] [seatHeight] [seatThickness] [seatWidth]\n");
std::printf(" Wooden bench: seat plank + 2 leg slabs (default 1.5/0.5/0.06/0.4)\n");
std::printf(" --gen-mesh-shrine <wom-base> [size] [pillarHeight] [pillarRadius] [roofThickness]\n");
std::printf(" Small canopy: square base + 4 corner pillars + flat roof slab (default 1.5/2/0.1/0.15)\n");
std::printf(" --gen-mesh-totem <wom-base> [width] [segments] [segmentHeight]\n");
std::printf(" Tribal totem: stack of N alternating-width carved blocks (default w=0.5, 5 segs, h=0.5)\n");
std::printf(" --gen-mesh-cage <wom-base> [width] [height] [barsPerSide] [barRadius]\n");
std::printf(" Cage: top/bottom frames + 4 corner posts + N bars per side (default 1.5×2.0, 5 bars)\n");
std::printf(" --gen-mesh-throne <wom-base> [seatWidth] [seatHeight] [backHeight] [pedestalSize]\n");
std::printf(" Throne: pedestal + seat + backrest + 2 armrests (default 0.8/0.5/1.5/1.2)\n");
std::printf(" --gen-mesh-coffin <wom-base> [length] [width] [height]\n");
std::printf(" Hexagonal coffin: narrow head + wide shoulder + tapered foot prism (default 2.0/0.8/0.6)\n");
std::printf(" --gen-mesh-bookshelf <wom-base> [width] [height] [depth] [shelves]\n");
std::printf(" Bookshelf: 5-panel cabinet with N-1 shelves and rows of varied book boxes (default 1.5/2.0/0.4/4)\n");
std::printf(" --gen-mesh-tent <wom-base> [length] [width] [height] [doorH] [doorW]\n");
std::printf(" Tent: A-frame canvas tent — ridge along X, two sloped roof panels, two gables, door notch on +X (default 1.6/1.0/0.9/0.5/0.4)\n");
std::printf(" --gen-mesh-firepit <wom-base> [ringR] [stones] [stoneSize] [logLen] [logThick]\n");
std::printf(" Firepit: ring of N stone cubes around 2 crossed log boxes (default 0.5/8/0.10/0.45/0.05)\n");
std::printf(" --gen-mesh-woodpile <wom-base> [logR] [logLen] [sides]\n");
std::printf(" Woodpile: 6 cylindrical logs in a 3-2-1 pyramid stack along Z axis (default 0.10/0.80/12)\n");
std::printf(" --gen-mesh-canopy <wom-base> [width] [depth] [height] [postR] [panelT] [drape]\n");
std::printf(" Canopy: 4-post awning with flat top panel and optional drape lips (default 1.6/1.2/2.0/0.05/0.03/0.15)\n");
std::printf(" --gen-mesh-haystack <wom-base> [baseR] [height] [layers] [sides]\n");
std::printf(" Haystack: terraced stack of N frustums tapering to an apex point (default 0.6/0.9/3/12)\n");
std::printf(" --gen-mesh-dock <wom-base> [length] [width] [height] [pilings/side] [pilingW] [deckT]\n");
std::printf(" Dock: flat plank deck on N pairs of square pilings (default 3.0/1.0/0.6/3/0.10/0.10)\n");
std::printf(" --gen-mesh-pergola <wom-base> [length] [width] [height] [postR] [beamT] [crossbeams]\n");
std::printf(" Pergola: 4 corner posts + 2 perimeter beams + N cross beams (open lattice top, no panel)\n");
std::printf(" --gen-mesh-chimney <wom-base> [width] [depth] [height] [capH] [capExtra]\n");
std::printf(" Chimney: rectangular brick shaft topped by a slightly wider rain-cap (default 0.45/0.45/1.8/0.10/0.05)\n");
std::printf(" --gen-mesh-bedroll <wom-base> [length] [radius] [sides] [pillowSize]\n");
std::printf(" Bedroll: horizontal closed cylinder along Z axis with optional pillow box at +Z (camp set dressing)\n");
std::printf(" --gen-mesh-workbench <wom-base> [length] [depth] [height] [legR] [topT] [viseSize] [trayH]\n");
std::printf(" Workbench: 4-legged top slab with optional vise at +X end and back tool tray (blacksmith / crafter)\n");
std::printf(" --gen-mesh-crate-stack <wom-base> [crateSize] [columns] [rows] [layers] [gap]\n");
std::printf(" Crate stack: N×M×K cube grid with small gap between crates (warehouses, cargo holds, dockyards)\n");
std::printf(" --gen-mesh-watchpost <wom-base> [postH] [postW] [platformSize] [platformT] [railingH] [railingW]\n");
std::printf(" Watchpost: tall pole + square platform + 4 corner railing posts (sentry / scout outpost)\n");
std::printf(" --gen-mesh-water-trough <wom-base> [length] [width] [height] [wallT]\n");
std::printf(" Water trough: 4-walled rectangular basin with flat floor (stable / farm / tavern set dressing)\n");
std::printf(" --gen-mesh-training-dummy <wom-base> [baseH] [postW] [torsoSize] [armSpan] [armT] [headSize]\n");
std::printf(" Training dummy: post + cubic torso + cross-bar arms + optional head (sparring / drill yard)\n");
std::printf(" --gen-mesh-hitching-post <wom-base> [span] [height] [postW] [barT] [capH]\n");
std::printf(" Hitching post: 2 vertical posts + horizontal cross-bar + optional decorative caps (stable / town square)\n");
std::printf(" --gen-mesh-outhouse <wom-base> [width] [depth] [height] [doorH] [doorW] [roofOverhang] [roofT]\n");
std::printf(" Outhouse: solid body + inset door slab on +Z + overhanging roof slab (privy / tool shed)\n");
std::printf(" --gen-mesh-forge <wom-base> [width] [depth] [baseH] [hoodH] [hoodInset] [chimneyH] [chimneyW]\n");
std::printf(" Blacksmith forge: stone hearth + smaller hood + optional chimney (smithy / armorer set dressing)\n");
std::printf(" --gen-mesh-archery-target <wom-base> [faceR] [faceT] [sides] [postH] [postW] [beamT]\n");
std::printf(" Archery target: round face cylinder on a 2-post stand with cross-beam (training yard / fair scene)\n");
std::printf(" --gen-mesh-gravel-pile <wom-base> [stoneCount] [baseR] [pileH] [maxStoneSize] [seed]\n");
std::printf(" Gravel pile: hash-distributed stone cubes in a conical heap (mining / construction / rubble)\n");
std::printf(" --gen-mesh-stone-bench <wom-base> [length] [depth] [seatH] [seatT] [supportW] [supportInset]\n");
std::printf(" Stone bench: long seat slab on 2 block supports near the ends (park / temple / ruined city)\n");
std::printf(" --gen-mesh-mine-cart <wom-base> [length] [width] [bodyH] [wallT] [wheelR] [wheelInset]\n");
std::printf(" Mine cart: open-top bin (5-piece basin) on 4 wheel boxes (mines / dwarven forges / junk yards)\n");
std::printf(" --gen-mesh-hitching-rail <wom-base> [length] [height] [posts] [postW] [barT]\n");
std::printf(" Hitching rail: long horizontal bar on N evenly-spaced posts (taverns / stockyards / market days)\n");
std::printf(" --gen-mesh-pillar-row <wom-base> [count] [span] [height] [pillarW] [capH] [capExtra]\n");
std::printf(" Pillar row: N evenly-spaced rectangular pillars with optional square caps (colonnade / temple ruin)\n");
std::printf(" --gen-mesh-statue-base <wom-base> [bodyW] [bodyH] [plinthExtra] [plinthH] [capitalExtra] [capitalH]\n");
std::printf(" Statue base: 3-tier pedestal (plinth + body + capital) for monuments / hero memorials\n");
std::printf(" --gen-mesh-bird-bath <wom-base> [stemR] [stemH] [basinR] [basinH] [sides]\n");
std::printf(" Bird bath: thin cylindrical stem topped by a wide shallow basin disc (small garden water feature)\n");
std::printf(" --gen-mesh-planter-box <wom-base> [length] [width] [height] [wallT] [soilTopFrac]\n");
std::printf(" Planter box: long open-top wood basin + visible soil-fill block (window sills / kitchen / balcony)\n");
std::printf(" --gen-mesh-urn <wom-base> [bodyR] [bodyH] [footR] [footH] [neckR] [neckH] [lipR] [lipH] [sides]\n");
std::printf(" Urn: 4-tier vertical pottery vessel (foot + body + neck + lip) — temple / mausoleum / kitchen storage\n");
std::printf(" --gen-mesh-candle <wom-base> [waxR] [waxH] [saucerR] [saucerH] [sides]\n");
std::printf(" Candle: thin wax pillar on optional saucer base (set saucerR=0 to skip) — chapels / vigil scenes\n");
std::printf(" --gen-mesh-lantern <wom-base> [baseR] [baseH] [globeR] [globeH] [neckR] [neckH] [capR] [capH] [sides]\n");
std::printf(" Lantern: 4-tier base + glass-globe + neck + cap stack (hand lantern / oil lamp silhouette)\n");
std::printf(" --gen-mesh-chalice <wom-base> [footR] [footH] [stemR] [stemH] [bowlR] [bowlH] [sides]\n");
std::printf(" Chalice: ceremonial 3-tier foot + stem + bowl goblet (chapel / treasure / ritual scene)\n");
std::printf(" --gen-mesh-standing-torch <wom-base> [postR] [postH] [bowlR] [bowlH] [sides]\n");
std::printf(" Standing torch: tall thin post + wider fire-bowl on top (hall lining / dungeon entry / ceremony path)\n");
std::printf(" --gen-mesh-scroll-case <wom-base> [bodyR] [bodyH] [capR] [capH] [sides]\n");
std::printf(" Scroll case: thin tall cylinder + optional wider cap (set capR=0 to skip) — libraries / mage scenes\n");
std::printf(" --gen-mesh-stove <wom-base> [bodyR] [bodyH] [chimneyR] [chimneyH] [sides]\n");
std::printf(" Pot-bellied stove: round cylindrical body + thin chimney column on top (cottage / workshop heating)\n");
std::printf(" --gen-mesh-well-pail <wom-base> [bodyR] [bodyH] [handleW] [handleT] [handleArc] [sides]\n");
std::printf(" Wooden well-pail: closed cylindrical body + thin horizontal handle bar above (water-haul / mop-bucket prop)\n");
std::printf(" --gen-mesh-mug <wom-base> [bodyR] [bodyH] [handleW] [handleH] [handleArm] [sides]\n");
std::printf(" Drinking mug / tankard: closed cylinder body + side handle slab (tavern / banquet / inn dressing)\n");
std::printf(" --gen-mesh-mortar-pestle <wom-base> [bowlR] [bowlH] [pestleR] [pestleH] [sides]\n");
std::printf(" Mortar + pestle: wide squat cylinder (bowl) + thin tall cylinder rising from inside (alchemy / kitchen)\n");
std::printf(" --gen-mesh-rune-stone <wom-base> [baseW] [baseD] [baseH] [stoneW] [stoneD] [stoneH]\n");
std::printf(" Rune stone: wide flat base block + tall narrow monolith (druid grove / witch shrine / ancient ruins)\n");
std::printf(" --gen-camp-pack <outDir>\n");
std::printf(" Convenience: emit tent + firepit + bedroll + canopy + woodpile + haystack into outDir as 6 .wom files\n");
std::printf(" --gen-blacksmith-pack <outDir>\n");
std::printf(" Convenience: emit forge + anvil + workbench + water-trough + crate-stack + hitching-post into outDir\n");
std::printf(" --gen-village-pack <outDir>\n");
std::printf(" Convenience: emit house + outhouse + chimney + hitching-post + well + signpost + haystack into outDir\n");
std::printf(" --gen-temple-pack <outDir>\n");
std::printf(" Convenience: emit altar + shrine + brazier + pillar + statue + portal + podium into outDir\n");
std::printf(" --gen-graveyard-pack <outDir>\n");
std::printf(" Convenience: emit grave + tombstone + coffin + statue + stone-bench + gravel-pile + cage into outDir\n");
std::printf(" --gen-garden-pack <outDir>\n");
std::printf(" Convenience: emit pergola + fountain + stone-bench + shrine + beehive + scarecrow + well into outDir\n");
std::printf(" --gen-dock-pack <outDir>\n");
std::printf(" Convenience: emit dock + crate-stack + barrel + canopy + bench + signpost + hitching-post into outDir\n");
std::printf(" --gen-tavern-pack <outDir>\n");
std::printf(" Convenience: emit house + chimney + table + bench + barrel + bookshelf + signpost into outDir\n");
std::printf(" --gen-mining-pack <outDir>\n");
std::printf(" Convenience: emit gravel-pile + crate-stack + mine-cart + pillar-row + lantern + workbench + hitching-post\n");
std::printf(" --gen-arena-pack <outDir>\n");
std::printf(" Convenience: emit training-dummy + archery-target + workbench + crate-stack + bench + water-trough + hitching-rail\n");
std::printf(" --gen-kitchen-pack <outDir>\n");
std::printf(" Convenience: emit stove + cauldron + prep-table + stool + barrel + mug + mortar (tavern back-of-house)\n");
std::printf(" --gen-mesh-table <wom-base> [width] [depth] [height] [legThick] [topThick]\n");
std::printf(" Table: flat top slab on 4 corner legs (default 1.6/1.0/0.85/0.10/0.06)\n");
std::printf(" --gen-mesh-lamppost <wom-base> [poleH] [poleT] [baseSize] [lanternSize] [lanternH]\n");
std::printf(" Lamppost: square base + tall pole + lantern body + cap (default 3.0/0.12/0.4/0.35/0.5)\n");
std::printf(" --gen-mesh-bed <wom-base> [length] [width] [legH] [matThick] [headH] [footH]\n");
std::printf(" Bed: 4 legs + mattress + headboard + footboard + pillow (default 2.0/1.2/0.30/0.20/1.0/0.4)\n");
std::printf(" --gen-mesh-ladder <wom-base> [height] [width] [rungs] [railThick] [rungThick]\n");
std::printf(" Ladder: 2 vertical rails + N evenly-spaced horizontal rungs (default 3.0/0.6/8/0.06/0.04)\n");
std::printf(" --gen-mesh-well <wom-base> [outerSize] [wallH] [wallT] [postH] [postT]\n");
std::printf(" Well: 4 stone walls in a hollow square + 2 roof posts + cross beam (default 1.4/0.8/0.15/1.6/0.12)\n");
std::printf(" --gen-mesh-signpost <wom-base> [poleH] [poleT] [baseSize] [signWidth] [signHeight]\n");
std::printf(" Signpost: base + pole + sign board + cap (default 2.5/0.10/0.30/0.80/0.35)\n");
std::printf(" --gen-mesh-mailbox <wom-base> [postH] [postT] [boxL] [boxW] [boxH]\n");
std::printf(" Mailbox: post + horizontal box + side flag (default 1.10/0.08/0.45/0.20/0.20)\n");
std::printf(" --gen-mesh-tombstone <wom-base> [width] [height] [depth] [baseScale]\n");
std::printf(" Tombstone: base plinth + tall slab + decorative crown (default 0.60/1.10/0.18/1.45)\n");
std::printf(" --gen-mesh-crate <wom-base> [size] [postRadius]\n");
std::printf(" Crate: cube body + 4 corner reinforcement posts (default 0.80/0.05)\n");
std::printf(" --gen-mesh-stool <wom-base> [seatSize] [seatThick] [legHeight] [legThick]\n");
std::printf(" Stool: small backless seat on 4 corner legs (default 0.36/0.04/0.45/0.04)\n");
std::printf(" --gen-mesh-cauldron <wom-base> [rimWidth] [bodyHeight] [legHeight]\n");
std::printf(" Cauldron: 4 legs + narrow bottom + wider mid + widest rim tiers (default 0.80/0.70/0.10)\n");
std::printf(" --gen-mesh-gate <wom-base> [openingWidth] [postHeight] [postT] [railT]\n");
std::printf(" Gate: 2 vertical posts + 3 horizontal rails (default 1.80/1.30/0.10/0.06)\n");
std::printf(" --gen-mesh-beehive <wom-base> [baseWidth] [height] [plateH]\n");
std::printf(" Beehive (skep): 4 tapered tiers + entrance notch on +Z face (default 0.70/0.85/0.05)\n");
std::printf(" --gen-mesh-weathervane <wom-base> [postH] [postT] [baseSize] [armLen] [arrowLen]\n");
std::printf(" Weathervane: base + post + N-S/E-W cross arms + arrow with tail (default 1.50/0.05/0.30/0.40/0.55)\n");
std::printf(" --gen-mesh-scarecrow <wom-base> [bodyH] [armSpan] [postT] [headSize] [hatSize]\n");
std::printf(" Scarecrow: cruciform body + cross arms + head + brimmed hat (default 1.80/1.40/0.06/0.22/0.32)\n");
std::printf(" --gen-mesh-sundial <wom-base> [baseSize] [baseH] [gnomonH] [gnomonT]\n");
std::printf(" Sundial: square base + central gnomon slab + 4 cardinal hour markers (default 0.80/0.06/0.35/0.04)\n");
std::printf(" --gen-mesh-podium <wom-base> [baseSize] [stepH] [steps] [lecternSize]\n");
std::printf(" Podium: stepped pyramid + lectern at back of top platform (default 1.60/0.20/3/0.30)\n");
std::printf(" --gen-mesh-brazier <wom-base> [bowlSize] [stemH] [stemT] [baseSize]\n");
std::printf(" Brazier: base + stem + bowl + 3 flame boxes for fire-pit lighting (default 0.55/0.80/0.10/0.35)\n");
std::printf(" --gen-mesh-archway-double <wom-base> [openingW] [openingH] [postT] [lintelT]\n");
std::printf(" Double archway: 3 posts + 2 lintels for twin-opening passages (default 1.40/2.40/0.18/0.20)\n");
std::printf(" Procedural tree: cylindrical trunk + spherical foliage (default 0.1/2.0/0.7)\n");
std::printf(" --displace-mesh <wom-base> <heightmap.png> [scale]\n");
std::printf(" Offset each vertex along its normal by heightmap brightness × scale (default 1.0)\n");
std::printf(" --gen-mesh-from-heightmap <wom-base> <heightmap.png> [scaleXZ] [scaleY]\n");
std::printf(" Convert a grayscale PNG into a heightmap mesh (W×H verts, 2(W-1)(H-1) tris)\n");
std::printf(" --export-mesh-heightmap <wom-base> <out.png> <W> <H>\n");
std::printf(" Extract a grayscale heightmap PNG from a row-major W×H heightmap mesh\n");
std::printf(" --add-texture-to-mesh <wom-base> <png-path> [batchIdx]\n");
std::printf(" Bind an existing PNG into a WOM's texturePaths and point batchIdx (default 0) at it\n");
std::printf(" --scale-mesh <wom-base> <factor>\n");
std::printf(" Uniformly scale every vertex and bounds by <factor> (factor > 0)\n");
std::printf(" --translate-mesh <wom-base> <dx> <dy> <dz>\n");
std::printf(" Offset every vertex and bounds by (dx, dy, dz)\n");
std::printf(" --strip-mesh <wom-base> [--bones] [--anims] [--all]\n");
std::printf(" Drop bones / animations from a WOM in place (smaller file, static-only use)\n");
std::printf(" --rotate-mesh <wom-base> <x|y|z> <degrees>\n");
std::printf(" Rotate every vertex + normal around the chosen axis by <degrees>\n");
std::printf(" --center-mesh <wom-base>\n");
std::printf(" Translate so the bounds center lands at origin (no scale/rotation change)\n");
std::printf(" --flip-mesh-normals <wom-base>\n");
std::printf(" Invert every vertex normal (use for inside-out meshes or two-sided pre-flip)\n");
std::printf(" --mirror-mesh <wom-base> <x|y|z>\n");
std::printf(" Mirror every vertex + normal across the chosen axis (also flips winding)\n");
std::printf(" --smooth-mesh-normals <wom-base>\n");
std::printf(" Recompute per-vertex normals as area-weighted averages of incident face normals\n");
std::printf(" --merge-meshes <a-base> <b-base> <out-base>\n");
std::printf(" Combine two WOMs into one (vertex/index buffers concatenated, batches preserved)\n");
std::printf(" --add-item <zoneDir> <name> [id] [quality] [displayId] [itemLevel]\n");
std::printf(" Append one item entry to <zoneDir>/items.json (auto-creates the file)\n");
std::printf(" --random-populate-zone <zoneDir> [--seed N] [--creatures N] [--objects N]\n");
std::printf(" Add random creatures/objects to a zone (seeded for reproducibility)\n");
std::printf(" --random-populate-items <zoneDir> [--seed N] [--count N] [--max-quality Q]\n");
std::printf(" Generate random items.json entries (seeded; quality cap defaults to epic=4)\n");
std::printf(" --gen-zone-texture-pack <zoneDir> [--seed N]\n");
std::printf(" Drop a starter texture pack (grass/dirt/stone/brick/wood/water) into <zoneDir>/textures/\n");
std::printf(" --gen-zone-mesh-pack <zoneDir> [--seed N]\n");
std::printf(" Drop a starter WOM mesh pack (rock/tree/fence) into <zoneDir>/meshes/\n");
std::printf(" --gen-zone-starter-pack <zoneDir> [--seed N]\n");
std::printf(" Run both texture-pack + mesh-pack in one pass — full open-format bootstrap\n");
std::printf(" --gen-project-starter-pack <projectDir> [--seed N]\n");
std::printf(" Run starter-pack + audio-pack across every zone — full project-scope bootstrap\n");
std::printf(" --info-zone-summary <zoneDir> [--json]\n");
std::printf(" One-glance health digest for a zone: pack counts/bytes + audit pass/fail\n");
std::printf(" --info-zone-deps <zoneDir> [--json]\n");
std::printf(" Find textures referenced by WOMs but missing from <zoneDir>/textures/ (broken-ref audit)\n");
std::printf(" --info-project-deps <projectDir>\n");
std::printf(" Run --info-zone-deps across every zone; reports per-zone PASS/FAIL + grand total\n");
std::printf(" --info-project-summary <projectDir> [--json]\n");
std::printf(" One-glance status table per zone in a project (BOOTSTRAPPED/PARTIAL/EMPTY)\n");
std::printf(" --gen-zone-readme <zoneDir> [--out <path>]\n");
std::printf(" Auto-generate README.md from zone.json + asset inventory (writes README.md by default)\n");
std::printf(" --gen-project-readme <projectDir> [--out <path>]\n");
std::printf(" Auto-generate PROJECT.md with per-zone status + asset count rollup\n");
std::printf(" --validate-zone-pack <zoneDir> [--json]\n");
std::printf(" Audit a zone's open-format asset pack: textures/meshes/audio counts + WOM validity\n");
std::printf(" --validate-project-packs <projectDir>\n");
std::printf(" Run validate-zone-pack across every zone in a project; exits 1 if any fails\n");
std::printf(" --gen-audio-tone <out.wav> <freqHz> <durationSec> [sampleRate] [waveform]\n");
std::printf(" Synthesize a procedural WAV (PCM-16 mono). Waveform: sine|square|triangle|saw\n");
std::printf(" --gen-audio-noise <out.wav> <durationSec> [sampleRate] [color] [seed] [amplitude]\n");
std::printf(" Synthesize procedural noise WAV. Color: white|pink|brown (default white, amp 0.5)\n");
std::printf(" --gen-audio-sweep <out.wav> <startHz> <endHz> <durationSec> [sampleRate] [shape]\n");
std::printf(" Synthesize frequency sweep (chirp) WAV. Shape: linear|exp (default linear)\n");
std::printf(" --gen-zone-audio-pack <zoneDir>\n");
std::printf(" Drop a starter WAV pack (drone/chime/click/alert) into <zoneDir>/audio/\n");
std::printf(" --gen-random-zone <name> [tx ty] [--seed N] [--creatures N] [--objects N] [--items N]\n");
std::printf(" End-to-end: scaffold-zone + random-populate-zone + random-populate-items\n");
std::printf(" --gen-random-project <count> [--prefix N] [--seed N] [--creatures N] [--objects N] [--items N]\n");
std::printf(" Generate <count> random zones at once (names like Zone1, Zone2...; tile coords step)\n");
std::printf(" --info-zone-audio <zoneDir> [--json]\n");
std::printf(" Print zone audio config (music + ambience tracks, volumes)\n");
std::printf(" --info-project-audio <projectDir> [--json]\n");
std::printf(" Audio config table across every zone (which zones have music/ambience set)\n");
std::printf(" --snap-zone-to-ground <zoneDir>\n");
std::printf(" Re-snap every creature/object in a zone to actual terrain height\n");
std::printf(" --audit-zone-spawns <zoneDir> [--threshold yards]\n");
std::printf(" List spawns whose Z is more than <threshold> yards off from the terrain (default 5)\n");
std::printf(" --list-zone-spawns <zoneDir> [--json]\n");
std::printf(" Combined creature+object listing for a zone (kind, name, position, key fields)\n");
std::printf(" --diff-zone-spawns <aZoneDir> <bZoneDir>\n");
std::printf(" Compare two zones' creature+object lists (added/removed/moved)\n");
std::printf(" --info-spawn <zoneDir> <creature|object> <index> [--json]\n");
std::printf(" Detailed view of a single creature/object spawn by index\n");
std::printf(" --list-project-spawns <projectDir> [--json]\n");
std::printf(" Combined creature+object listing across every zone (zone column added)\n");
std::printf(" --audit-project-spawns <projectDir> [--threshold yards]\n");
std::printf(" Run --audit-zone-spawns across every zone (per-zone summary + total)\n");
std::printf(" --snap-project-to-ground <projectDir>\n");
std::printf(" Run --snap-zone-to-ground across every zone (per-zone summary + totals)\n");
std::printf(" --list-items <zoneDir> [--json]\n");
std::printf(" Print every item in <zoneDir>/items.json with quality colors and key fields\n");
std::printf(" --export-zone-items-md <zoneDir> [out.md]\n");
std::printf(" Render items.json as a Markdown table grouped by quality (rare/epic/etc.)\n");
std::printf(" --export-project-items-md <projectDir> [out.md]\n");
std::printf(" Project-wide items markdown: per-zone sections, project quality histogram\n");
std::printf(" --export-project-items-csv <projectDir> [out.csv]\n");
std::printf(" Single CSV with every item across every zone (zone column added for grouping)\n");
std::printf(" --info-item <zoneDir> <id|index> [--json]\n");
std::printf(" Detail view for one item (lookup by id, or by index if prefixed with '#')\n");
std::printf(" --set-item <zoneDir> <id|#index> [--name S] [--quality N] [--displayId N] [--itemLevel N] [--stackable N]\n");
std::printf(" Edit fields on an existing item in place; only specified flags are changed\n");
std::printf(" --remove-item <zoneDir> <index>\n");
std::printf(" Remove item at given 0-based index from <zoneDir>/items.json\n");
std::printf(" --copy-zone-items <fromZoneDir> <toZoneDir> [--merge]\n");
std::printf(" Copy items from one zone to another (default replaces; --merge appends with re-id)\n");
std::printf(" --clone-item <zoneDir> <index> [newName]\n");
std::printf(" Duplicate the item at index, assign next free id (and optional name override)\n");
std::printf(" --validate-items <zoneDir>\n");
std::printf(" Schema check on items.json: duplicate ids, quality range, required fields\n");
std::printf(" --validate-project-items <projectDir>\n");
std::printf(" Run --validate-items across every zone (per-zone PASS/FAIL + aggregate)\n");
std::printf(" --info-project-items <projectDir> [--json]\n");
std::printf(" Aggregate item counts and quality histogram across every zone in a project\n");
std::printf(" --convert-dbc-json <dbc-path> [out.json]\n");
std::printf(" Convert one DBC file to wowee JSON sidecar format\n");
std::printf(" --convert-json-dbc <json-path> [out.dbc]\n");
std::printf(" Convert a wowee JSON DBC back to binary DBC for private-server compat\n");
std::printf(" --convert-blp-png <blp-path> [out.png]\n");
std::printf(" Convert one BLP texture to PNG sidecar\n");
std::printf(" --convert-blp-batch <srcDir>\n");
std::printf(" Bulk BLP→PNG conversion across every .blp in <srcDir> (sidecars next to source)\n");
std::printf(" --migrate-wom <wom-base> [out-base]\n");
std::printf(" Upgrade an older WOM (v1/v2) to WOM3 with a default single-batch entry\n");
std::printf(" --migrate-zone <zoneDir>\n");
std::printf(" Run --migrate-wom in-place on every WOM under <zoneDir>\n");
std::printf(" --migrate-project <projectDir>\n");
std::printf(" Run --migrate-zone across every zone in <projectDir>\n");
std::printf(" --migrate-jsondbc <path> [out.json]\n");
std::printf(" Auto-fix a JSON DBC sidecar: add missing format/source, sync recordCount\n");
std::printf(" --list-zones [--json] List discovered custom zones and exit\n");
std::printf(" --zone-stats <projectDir> [--json]\n");
std::printf(" Aggregate counts across every zone in <projectDir>\n");
std::printf(" --info-tilemap <projectDir> [--json]\n");
std::printf(" ASCII-render the 64x64 WoW ADT grid showing tile claims by zone\n");
std::printf(" --list-project-orphans <projectDir> [--json]\n");
std::printf(" Find .wom/.wob files in zones not referenced by any objects.json or doodad list\n");
std::printf(" --remove-project-orphans <projectDir> [--dry-run]\n");
std::printf(" Delete the orphan .wom/.wob files surfaced by --list-project-orphans\n");
std::printf(" --list-zone-deps <zoneDir> [--json]\n");
std::printf(" List external M2/WMO model paths a zone references (objects + WOB doodads)\n");
std::printf(" --export-zone-deps-md <zoneDir> [out.md]\n");
std::printf(" Markdown dep table for a zone (with on-disk presence column)\n");
std::printf(" --export-zone-spawn-png <zoneDir> [out.png]\n");
std::printf(" Top-down PNG of creature + object spawn positions (per-tile-bounded)\n");
std::printf(" --check-zone-refs <zoneDir> [--json]\n");
std::printf(" Verify every referenced model/quest NPC actually exists; exit 1 on missing refs\n");
std::printf(" --check-project-refs <projectDir> [--json]\n");
std::printf(" Run --check-zone-refs across every zone in <projectDir>\n");
std::printf(" --check-zone-content <zoneDir> [--json]\n");
std::printf(" Sanity-check creature/object/quest fields for plausible values\n");
std::printf(" --check-project-content <projectDir> [--json]\n");
std::printf(" Run --check-zone-content across every zone in <projectDir>\n");
std::printf(" --for-each-zone <projectDir> -- <cmd...>\n");
std::printf(" Run <cmd...> for every zone in <projectDir>; '{}' in cmd is replaced with the zone path\n");
std::printf(" --for-each-tile <zoneDir> -- <cmd...>\n");
std::printf(" Run <cmd...> for every tile in <zoneDir>; '{}' replaced with the tile-base path\n");
std::printf(" --scaffold-zone <name> [tx ty] Create a blank zone in custom_zones/<name>/ and exit\n");
std::printf(" --mvp-zone <name> [tx ty]\n");
std::printf(" Scaffold + add a creature + object + quest (with objective+reward) for quick demos\n");
std::printf(" --add-tile <zoneDir> <tx> <ty> [baseHeight]\n");
std::printf(" Add a new ADT tile to an existing zone (extends the manifest's tiles list)\n");
std::printf(" --remove-tile <zoneDir> <tx> <ty>\n");
std::printf(" Remove a tile from a zone (drops manifest entry + deletes WHM/WOT/WOC files)\n");
std::printf(" --list-tiles <zoneDir> [--json]\n");
std::printf(" List every tile in a zone manifest with on-disk file presence\n");
std::printf(" --add-creature <zoneDir> <name> <x> <y> <z> [displayId] [level]\n");
std::printf(" Append one creature spawn to <zoneDir>/creatures.json and exit\n");
std::printf(" --add-object <zoneDir> <m2|wmo> <gamePath> <x> <y> <z> [scale]\n");
std::printf(" Append one object placement to <zoneDir>/objects.json and exit\n");
std::printf(" --add-quest <zoneDir> <title> [giverId] [turnInId] [xp] [level]\n");
std::printf(" Append one quest to <zoneDir>/quests.json and exit\n");
std::printf(" --add-quest-objective <zoneDir> <questIdx> <kill|collect|talk|explore|escort|use> <targetName> [count]\n");
std::printf(" Append one objective to a quest by index\n");
std::printf(" --remove-quest-objective <zoneDir> <questIdx> <objIdx>\n");
std::printf(" Remove the objective at given 0-based index from a quest\n");
std::printf(" --clone-quest <zoneDir> <questIdx> [newTitle]\n");
std::printf(" Duplicate a quest (with all objectives + rewards) and append it\n");
std::printf(" --clone-creature <zoneDir> <idx> [newName] [dx dy dz]\n");
std::printf(" Duplicate a creature spawn (defaults: '<orig> (copy)' offset by 5 yards)\n");
std::printf(" --clone-object <zoneDir> <idx> [dx dy dz]\n");
std::printf(" Duplicate an object placement (defaults: offset by 5 yards X)\n");
std::printf(" --add-quest-reward-item <zoneDir> <questIdx> <itemPath> [more...]\n");
std::printf(" Append item reward(s) to a quest's reward.itemRewards list\n");
std::printf(" --set-quest-reward <zoneDir> <questIdx> [--xp N] [--gold N] [--silver N] [--copper N]\n");
std::printf(" Update XP/coin reward fields on a quest by index\n");
std::printf(" --remove-creature <zoneDir> <index>\n");
std::printf(" Remove creature at given 0-based index from <zoneDir>/creatures.json\n");
std::printf(" --remove-object <zoneDir> <index>\n");
std::printf(" Remove object at given 0-based index from <zoneDir>/objects.json\n");
std::printf(" --remove-quest <zoneDir> <index>\n");
std::printf(" Remove quest at given 0-based index from <zoneDir>/quests.json\n");
std::printf(" --copy-zone <srcDir> <newName>\n");
std::printf(" Duplicate a zone to custom_zones/<slug>/ with renamed slug-prefixed files\n");
std::printf(" --rename-zone <srcDir> <newName>\n");
std::printf(" In-place rename (zone.json + slug-prefixed files + dir); no copy\n");
std::printf(" --remove-zone <zoneDir> [--confirm]\n");
std::printf(" Delete a zone directory entirely (requires --confirm to actually delete)\n");
std::printf(" --clear-zone-content <zoneDir> [--creatures] [--objects] [--quests] [--all]\n");
std::printf(" Wipe one or more content files (terrain + manifest preserved)\n");
std::printf(" --strip-zone <zoneDir> [--dry-run]\n");
std::printf(" Remove derived outputs (.glb/.obj/.stl/.html/.dot/.csv/ZONE.md/DEPS.md)\n");
std::printf(" --strip-project <projectDir> [--dry-run]\n");
std::printf(" Run --strip-zone across every zone (per-zone counts + aggregate freed bytes)\n");
std::printf(" --gen-makefile <zoneDir> [out.mk]\n");
std::printf(" Generate a Makefile that rebuilds every derived output for a zone\n");
std::printf(" --gen-project-makefile <projectDir> [out.mk]\n");
std::printf(" Generate a top-level Makefile that delegates to each zone's per-zone Makefile\n");
std::printf(" --repair-project <projectDir> [--dry-run]\n");
std::printf(" Run --repair-zone across every zone (manifest drift fixes, per-zone summary)\n");
std::printf(" --repair-zone <zoneDir> [--dry-run]\n");
std::printf(" Auto-fix manifest/disk drift (missing tiles in manifest, hasCreatures flag)\n");
std::printf(" --build-woc <wot-base> Generate a WOC collision mesh from WHM/WOT and exit\n");
std::printf(" --regen-collision <zoneDir> Rebuild every WOC under a zone dir and exit\n");
std::printf(" --fix-zone <zoneDir> Re-parse + re-save zone JSONs to apply latest scrubs/caps and exit\n");
std::printf(" --export-png <wot-base> Render heightmap, normal-map, and zone-map PNG previews\n");
std::printf(" --export-obj <wom-base> [out.obj]\n");
std::printf(" Convert a WOM model to Wavefront OBJ for use in Blender/MeshLab\n");
std::printf(" --export-glb <wom-base> [out.glb]\n");
std::printf(" Convert a WOM model to glTF 2.0 binary (.glb) — modern industry standard\n");
std::printf(" --export-stl <wom-base> [out.stl]\n");
std::printf(" Convert a WOM model to ASCII STL — works with any 3D printer slicer\n");
std::printf(" --import-stl <stl-path> [wom-base]\n");
std::printf(" Convert an ASCII STL back into WOM (round-trips with --export-stl)\n");
std::printf(" --export-wob-glb <wob-base> [out.glb]\n");
std::printf(" Convert a WOB building to glTF 2.0 binary (one mesh, per-group primitives)\n");
std::printf(" --export-whm-glb <wot-base> [out.glb]\n");
std::printf(" Convert WHM heightmap to glTF 2.0 binary terrain mesh (per-chunk primitives)\n");
std::printf(" --bake-zone-glb <zoneDir> [out.glb]\n");
std::printf(" Bake every WHM tile in a zone into one glTF (one node per tile)\n");
std::printf(" --bake-zone-stl <zoneDir> [out.stl]\n");
std::printf(" Bake every WHM tile in a zone into one STL for 3D-printing the terrain\n");
std::printf(" --bake-zone-obj <zoneDir> [out.obj]\n");
std::printf(" Bake every WHM tile in a zone into one Wavefront OBJ (one g-block per tile)\n");
std::printf(" --bake-project-obj <projectDir> [out.obj]\n");
std::printf(" Bake every zone in a project into one Wavefront OBJ (one g-block per zone)\n");
std::printf(" --bake-project-stl <projectDir> [out.stl]\n");
std::printf(" Bake every zone in a project into one ASCII STL for full-project printing\n");
std::printf(" --bake-project-glb <projectDir> [out.glb]\n");
std::printf(" Bake every zone in a project into one glTF 2.0 (one mesh per zone)\n");
std::printf(" --bake-wom-collision <wom-base> [out.woc] [--weld <eps>] [--steep <deg>]\n");
std::printf(" Convert a WOM into a WOC collision file (raycast / walkability mesh) with optional vertex weld\n");
std::printf(" --bake-wob-collision <wob-base> [out.woc] [--weld <eps>] [--steep <deg>]\n");
std::printf(" Convert a multi-group WOB building into a single WOC collision file (weld is per-group)\n");
std::printf(" --bake-zone-collision <zoneDir> [out.woc] [--weld <eps>] [--steep <deg>]\n");
std::printf(" Walk every .wom + .wob under zoneDir, weld each independently, append to one shared WOC\n");
std::printf(" --audit-watertight <zoneDir|projectDir> [--weld <eps>] [--json] [--summary]\n");
std::printf(" Walk every .wom under root, run welded watertight check; --summary prints a one-line rollup\n");
std::printf(" --audit-watertight-wob <zoneDir|projectDir> [--weld <eps>] [--json] [--summary]\n");
std::printf(" Walk every .wob, check that EVERY group is closed (per-group weld); --summary prints one-line rollup\n");
std::printf(" --import-obj <obj-path> [wom-base]\n");
std::printf(" Convert a Wavefront OBJ back into WOM (round-trips with --export-obj)\n");
std::printf(" --export-wob-obj <wob-base> [out.obj]\n");
std::printf(" Convert a WOB building to Wavefront OBJ (one group per WOB group)\n");
std::printf(" --import-wob-obj <obj-path> [wob-base]\n");
std::printf(" Convert a Wavefront OBJ back into WOB (round-trips with --export-wob-obj)\n");
std::printf(" --export-woc-obj <woc-path> [out.obj]\n");
std::printf(" Convert a WOC collision mesh to OBJ for visualization (per-flag color groups)\n");
std::printf(" --export-whm-obj <wot-base> [out.obj]\n");
std::printf(" Convert a WHM heightmap to OBJ terrain mesh (9x9 outer grid per chunk)\n");
std::printf(" --validate <zoneDir> [--json]\n");
std::printf(" Score zone open-format completeness and exit\n");
std::printf(" --validate-wom <wom-base> [--json]\n");
std::printf(" Deep-check a WOM file for index/bone/batch/bound invariants\n");
std::printf(" --validate-wob <wob-base> [--json]\n");
std::printf(" Deep-check a WOB file for group/portal/doodad invariants\n");
std::printf(" --validate-woc <woc-path> [--json]\n");
std::printf(" Deep-check a WOC collision mesh for finite verts and degeneracy\n");
std::printf(" --validate-whm <wot-base> [--json]\n");
std::printf(" Deep-check a WHM/WOT terrain pair for NaN heights and bad placements\n");
std::printf(" --validate-all <dir> [--json]\n");
std::printf(" Recursively run all per-format validators on every file\n");
std::printf(" --validate-project <projectDir> [--json]\n");
std::printf(" Run --validate-all on every zone in <projectDir>; exit 1 if any zone fails\n");
std::printf(" --bench-validate-project <projectDir> [--json]\n");
std::printf(" Time --validate-project per zone; report avg/min/max latency\n");
std::printf(" --bench-bake-project <projectDir> [--json]\n");
std::printf(" Time WHM/WOT load per zone (proxy for bake cost); report timings\n");
std::printf(" --validate-glb <path> [--json]\n");
std::printf(" Verify a glTF 2.0 binary's structure (magic, chunks, JSON, accessors)\n");
std::printf(" --check-glb-bounds <path> [--json]\n");
std::printf(" Verify position accessor min/max in a .glb actually matches the data\n");
std::printf(" --validate-stl <path> [--json]\n");
std::printf(" Verify an ASCII STL's structure (solid framing, facet/vertex shape, no NaN)\n");
std::printf(" --validate-png <path> [--json]\n");
std::printf(" Verify a PNG's structure (signature, chunks, CRC, IHDR/IDAT/IEND order)\n");
std::printf(" --validate-blp <path> [--json]\n");
std::printf(" Verify a BLP texture (magic, dimensions, mip offsets within file)\n");
std::printf(" --validate-jsondbc <path> [--json]\n");
std::printf(" Verify a JSON DBC sidecar's full schema (per-cell types, row width, format tag)\n");
std::printf(" --info-glb <path> [--json]\n");
std::printf(" Print glTF 2.0 binary metadata (chunks, mesh/primitive counts, accessors)\n");
std::printf(" --info-glb-bytes <path> [--json]\n");
std::printf(" Per-section + per-bufferView byte breakdown of a .glb file\n");
std::printf(" --info-glb-tree <path>\n");
std::printf(" Render glTF structure as a tree (scenes/nodes/meshes/primitives)\n");
std::printf(" --zone-summary <zoneDir> [--json]\n");
std::printf(" One-shot validate + creature/object/quest counts and exit\n");
std::printf(" --info-zone-tree <zoneDir>\n");
std::printf(" Render a hierarchical tree view of a zone's contents (no --json)\n");
std::printf(" --info-project-tree <projectDir>\n");
std::printf(" Tree view of every zone in a project with quick counts (no --json)\n");
std::printf(" --info-project-bytes <projectDir> [--json]\n");
std::printf(" Per-zone byte rollup with proprietary-vs-open category split (size audit)\n");
std::printf(" --validate-project-open-only <projectDir>\n");
std::printf(" Exit 1 if any proprietary Blizzard assets (.m2/.wmo/.blp/.dbc) remain — release gate\n");
std::printf(" --audit-project <projectDir>\n");
std::printf(" Run validate-project + open-only + check-project-refs together; one PASS/FAIL\n");
std::printf(" --bench-audit-project <projectDir>\n");
std::printf(" Time each --audit-project sub-step; shows where the slow ones are\n");
std::printf(" --info-zone-bytes <zoneDir> [--json]\n");
std::printf(" Per-file size breakdown grouped by category, sorted largest-first\n");
std::printf(" --info-project-extents <projectDir> [--json]\n");
std::printf(" Combined spatial bounding box across every zone (per-zone table + project union)\n");
std::printf(" --info-zone-extents <zoneDir> [--json]\n");
std::printf(" Compute the zone's bounding box (XY tile range, Z height min/max)\n");
std::printf(" --info-project-water <projectDir> [--json]\n");
std::printf(" Aggregate water-layer stats across every zone (per-zone breakdown + project totals)\n");
std::printf(" --info-zone-water <zoneDir> [--json]\n");
std::printf(" Aggregate water-layer stats across all tiles (layer count, types, area)\n");
std::printf(" --info-project-density <projectDir> [--json]\n");
std::printf(" Per-zone content density rollup (creatures/objects/quests per tile, project totals)\n");
std::printf(" --info-zone-density <zoneDir> [--json]\n");
std::printf(" Per-tile density (creatures/objects/quests per tile + overall avg)\n");
std::printf(" --export-zone-summary-md <zoneDir> [out.md]\n");
std::printf(" Render a markdown documentation page for a zone (manifest + content)\n");
std::printf(" --export-zone-csv <zoneDir> [outDir]\n");
std::printf(" Emit creatures.csv / objects.csv / quests.csv / items.csv for spreadsheet workflows\n");
std::printf(" --export-zone-checksum <zoneDir> [out.sha256]\n");
std::printf(" Emit a SHA-256 manifest of every source file in a zone (for integrity checks)\n");
std::printf(" --export-project-checksum <projectDir> [out.sha256]\n");
std::printf(" Project-wide SHA-256 manifest (paths are zone-relative) + single project fingerprint\n");
std::printf(" --validate-project-checksum <projectDir> [in.sha256]\n");
std::printf(" Verify PROJECT_SHA256SUMS in-tool (cross-platform, no sha256sum dependency)\n");
std::printf(" --export-zone-html <zoneDir> [out.html]\n");
std::printf(" Emit a single-file HTML viewer next to the zone .glb (model-viewer based)\n");
std::printf(" --export-project-html <projectDir> [out.html]\n");
std::printf(" Generate an index.html linking to every zone's HTML viewer in <projectDir>\n");
std::printf(" --export-project-md <projectDir> [out.md]\n");
std::printf(" Generate a README.md indexing every zone with counts + viewer/bake status\n");
std::printf(" --export-quest-graph <zoneDir> [out.dot]\n");
std::printf(" Render quest-chain DAG as Graphviz DOT (pipe to `dot -Tpng -o quests.png`)\n");
std::printf(" --info <wom-base> [--json]\n");
std::printf(" Print WOM file metadata (version, counts) and exit\n");
std::printf(" --info-batches <wom-base> [--json]\n");
std::printf(" Per-batch breakdown of a WOM3 (index range, texture, blend mode, flags)\n");
std::printf(" --info-textures <wom-base> [--json]\n");
std::printf(" List every texture path referenced by a WOM (with on-disk presence)\n");
std::printf(" --info-doodads <wob-base> [--json]\n");
std::printf(" List every doodad placement in a WOB (model path, position, rotation, scale)\n");
std::printf(" --info-attachments <m2-path> [--json]\n");
std::printf(" List M2 attachment points (weapon mounts, etc.) with bone + offset\n");
std::printf(" --info-particles <m2-path> [--json]\n");
std::printf(" List M2 particle + ribbon emitters (texture, blend, bone)\n");
std::printf(" --info-sequences <m2-path> [--json]\n");
std::printf(" List M2 animation sequences (id, duration, flags)\n");
std::printf(" --info-bones <m2-path> [--json]\n");
std::printf(" List M2 bones with parent tree, key-bone IDs, pivot offsets\n");
std::printf(" --export-bones-dot <wom-base> [out.dot]\n");
std::printf(" Render WOM bone hierarchy as Graphviz DOT (pipe to `dot -Tpng -o bones.png`)\n");
std::printf(" --list-project-meshes <projectDir> [--json]\n");
std::printf(" Project-wide WOM inventory across every zone (vert/tri totals + per-zone breakdown)\n");
std::printf(" --list-project-audio <projectDir> [--json]\n");
std::printf(" Project-wide WAV inventory across every zone (duration/bytes per zone + grand total)\n");
std::printf(" --list-project-textures <projectDir> [--json]\n");
std::printf(" Aggregate texture refs across every WOM in a project (deduped, with zone breakdown)\n");
std::printf(" --list-zone-meshes <zoneDir> [--json]\n");
std::printf(" List every WOM in <zoneDir> with vert/tri/bone/anim/batch counts and file size\n");
std::printf(" --list-zone-audio <zoneDir> [--json]\n");
std::printf(" List every WAV under <zoneDir>/audio/ with format/duration/sample-rate/size\n");
std::printf(" --list-zone-textures <zoneDir> [--json]\n");
std::printf(" Aggregate texture refs across all WOM models in a zone (deduped)\n");
std::printf(" --info-zone-models-total <zoneDir> [--json]\n");
std::printf(" Aggregate WOM/WOB stats across a zone (verts, tris, bones, batches, doodads)\n");
std::printf(" --list-zone-meshes-detail <zoneDir> [--json]\n");
std::printf(" Per-mesh listing of every .wom in a zone, sorted by triangle count\n");
std::printf(" --info-mesh <wom-base> [--json]\n");
std::printf(" Single-mesh detail: bounds, version, batches, bones, textures, attachments in one view\n");
std::printf(" --info-mesh-storage-budget <wom-base> [--json]\n");
std::printf(" Estimated bytes-per-category breakdown for a single WOM (vertices/indices/bones/...)\n");
std::printf(" --info-mesh-stats <wom-base> [--json]\n");
std::printf(" Geometric stats: total surface area, triangle area histogram, edge use, watertight check\n");
std::printf(" --list-project-meshes-detail <projectDir> [--json]\n");
std::printf(" Per-mesh listing across every zone in a project (sorted by triangle count)\n");
std::printf(" --info-project-models-total <projectDir> [--json]\n");
std::printf(" Aggregate WOM/WOB stats across an entire project (per-zone breakdown + totals)\n");
std::printf(" --info-wob <wob-base> [--json]\n");
std::printf(" Print WOB building metadata (groups, portals, doodads) and exit\n");
std::printf(" --info-wob-stats <wob-base> [--weld <eps>] [--json]\n");
std::printf(" Per-group + aggregate geometric stats (surface area, edges, watertight) for a WOB building\n");
std::printf(" --info-woc <woc-path> [--json]\n");
std::printf(" Print WOC collision metadata (triangle counts, bounds) and exit\n");
std::printf(" --info-wol <wol-base> [--json]\n");
std::printf(" Print WOL lighting keyframes (zone name + per-time-of-day ambient/directional/fog) and exit\n");
std::printf(" --info-wol-at <wol-base> <HH:MM|minutes>\n");
std::printf(" Sample the WOL's interpolated lighting at a specific time-of-day (linear blend between keyframes)\n");
std::printf(" --validate-wol <wol-base> [--json]\n");
std::printf(" Walk every keyframe; check time bounds + sort order + fogEnd > fogStart + finite color components\n");
std::printf(" --gen-light <wol-base> [zoneName]\n");
std::printf(" Emit a starter .wol with the canonical 4-keyframe day/night cycle (midnight + dawn + noon + dusk)\n");
std::printf(" --gen-light-cave <wol-base> [zoneName]\n");
std::printf(" Emit a single-keyframe .wol with dim cool ambient + heavy short-range fog (cave / mine interior)\n");
std::printf(" --gen-light-dungeon <wol-base> [zoneName]\n");
std::printf(" Emit a single-keyframe .wol with warm torchlit ambient + medium fog (dungeon / crypt interior)\n");
std::printf(" --gen-light-night <wol-base> [zoneName]\n");
std::printf(" Emit a single-keyframe .wol with moonlit directional + far fog (always-night zone / shadow realm)\n");
std::printf(" --export-wol-json <wol-base> [out.json]\n");
std::printf(" Export binary .wol to a human-editable JSON sidecar (defaults to <base>.wol.json)\n");
std::printf(" --import-wol-json <json-path> [out-base]\n");
std::printf(" Import a .wol.json sidecar back into binary .wol (round-trip with --export-wol-json)\n");
std::printf(" --export-wow-json <wow-base> [out.json]\n");
std::printf(" Export binary .wow to a human-editable JSON sidecar (defaults to <base>.wow.json)\n");
std::printf(" --import-wow-json <json-path> [out-base]\n");
std::printf(" Import a .wow.json sidecar back into binary .wow (accepts type-name string OR typeId int)\n");
std::printf(" --info-wow <wow-base> [--json]\n");
std::printf(" Print WOW weather entries (zone + per-state type / intensity / weight / duration) and exit\n");
std::printf(" --validate-wow <wow-base> [--json]\n");
std::printf(" Walk every WOW entry; check typeId / intensity bounds [0,1] / weight > 0 / duration min ≤ max\n");
std::printf(" --validate-wom <wom-base> [--json]\n");
std::printf(" Static sanity checks on .wom: index range, bone refs, bound box, batch coverage, animation track count\n");
std::printf(" --gen-world-map <womx-base> [mapName]\n");
std::printf(" Emit .womx world-tile manifest: 64x64 continent grid with all tiles present (open WDT replacement)\n");
std::printf(" --gen-world-map-instance <womx-base> [mapName]\n");
std::printf(" Emit .womx world-tile manifest: 4x4 instance grid (small-world / dungeon scale)\n");
std::printf(" --gen-world-map-arena <womx-base> [mapName]\n");
std::printf(" Emit .womx world-tile manifest: 1x1 single-tile arena (smallest valid world)\n");
std::printf(" --info-womx <womx-base> [--json]\n");
std::printf(" Print WOMX manifest (worldType / gridSize / tilesPresent / defaultLightId / defaultWeatherId)\n");
std::printf(" --validate-womx <womx-base> [--json]\n");
std::printf(" Static checks on .womx: gridSize 1..128, worldType in range, tileBitmap matches expected size\n");
std::printf(" --export-womx-json <womx-base> [out.json]\n");
std::printf(" Export binary .womx to a human-editable JSON sidecar (rows of '1'/'0' strings, easy to hand-edit)\n");
std::printf(" --import-womx-json <json-path> [out-base]\n");
std::printf(" Import a .womx.json sidecar back into binary .womx (round-trip with --export-womx-json)\n");
std::printf(" --gen-sound-catalog <wsnd-base> [name]\n");
std::printf(" Emit .wsnd starter catalog (one entry per kind: sfx/music/ambient/ui/voice/spell/combat)\n");
std::printf(" --gen-sound-catalog-ambient <wsnd-base> [name]\n");
std::printf(" Emit .wsnd wilderness catalog: looped birds + wind + 3 footstep variants\n");
std::printf(" --gen-sound-catalog-tavern <wsnd-base> [name]\n");
std::printf(" Emit .wsnd tavern catalog: fire-crackle + crowd murmur + drink-clink + door-creak + lute music\n");
std::printf(" --info-wsnd <wsnd-base> [--json]\n");
std::printf(" Print WSND catalog entries (id / kind / flags / volume / 3D distances / file path / label)\n");
std::printf(" --validate-wsnd <wsnd-base> [--json]\n");
std::printf(" Static checks: kind in 0..6, finite distances, 3D needs max>min>=0, no duplicate sound IDs\n");
std::printf(" --export-wsnd-json <wsnd-base> [out.json]\n");
std::printf(" Export binary .wsnd to a human-editable JSON sidecar (defaults to <base>.wsnd.json)\n");
std::printf(" --import-wsnd-json <json-path> [out-base]\n");
std::printf(" Import a .wsnd.json sidecar back into binary .wsnd (accepts either kind int OR kindName string)\n");
feat(pipeline): add WSPN (Wowee Spawn Point catalog) format Novel open replacement for AzerothCore-style scattered creature_template / gameobject SQL spawn tables PLUS the ADT MDDF / MODF doodad-placement chunks. The 11th open format, and the first that covers the live world-content side (atmosphere + sounds + spawns now form the runtime "what fills this zone" picture). A WSPN file holds all spawn points for a zone in a single table, with kind discriminating creature vs game object vs static doodad. The same format powers: • server runtime — knows what NPCs / objects to spawn • editor — draws spawn markers • renderer — reads the doodad subset directly to draw static props without going through a server roundtrip Format: • magic "WSPN", version 1, little-endian • per entry: kind / entryId / position(3f) / rotation(3f) / scale / flags / respawnSec / factionId / questIdRequired / wanderRadius / label Flags packed: disabled (0x01), event-only (0x02), quest-phased (0x04). Reserved bits for future per-entry encoding extensions. API: WoweeSpawnsLoader::save / load / exists; presets makeStarter (1 each kind), makeCamp (4-bandit ring + chest + 2 tents), makeVillage (6 NPCs + 2 signs + 4 corner trees). CLI added (5 flags, 473 documented total now): --gen-spawns / --gen-spawns-camp / --gen-spawns-village --info-wspn / --validate-wspn Validator catches: out-of-range kind, NaN/inf coords, non-positive scale, doodad with non-zero respawn (static prop misuse), creature with respawn=0 (won't respawn after kill), entryId=0 (orphan reference). All 3 presets save / load / re-validate clean. Doodad and game-object entries explicitly set wanderRadius=0 so the generated catalogs are noise-free.
2026-05-09 14:57:53 -07:00
std::printf(" --gen-spawns <wspn-base> [name]\n");
std::printf(" Emit .wspn starter spawn catalog: 1 creature + 1 game object + 1 doodad near origin\n");
std::printf(" --gen-spawns-camp <wspn-base> [name]\n");
std::printf(" Emit .wspn bandit-camp catalog: 4 creatures around a wander ring + 1 chest + 2 tents\n");
std::printf(" --gen-spawns-village <wspn-base> [name]\n");
std::printf(" Emit .wspn village catalog: 6 friendly NPCs (mixed roles) + 2 signs + 4 corner trees\n");
std::printf(" --info-wspn <wspn-base> [--json]\n");
std::printf(" Print WSPN spawn entries (kind / entryId / position / respawn / wander radius / label)\n");
std::printf(" --validate-wspn <wspn-base> [--json]\n");
std::printf(" Static checks: kind 0..2, finite position/scale/wander, doodads have respawn=0, no orphan entryId=0\n");
std::printf(" --export-wspn-json <wspn-base> [out.json]\n");
std::printf(" Export binary .wspn to a human-editable JSON sidecar (defaults to <base>.wspn.json)\n");
std::printf(" --import-wspn-json <json-path> [out-base]\n");
std::printf(" Import a .wspn.json sidecar back into binary .wspn (accepts either kind int OR kindName string)\n");
feat(pipeline): add WIT (Wowee Item Template) format Novel open replacement for Blizzard's Item.dbc + ItemDisplayInfo.dbc + the SQL item_template tables that AzerothCore-style servers store item definitions in. The 12th open format added to the editor. A WIT file holds the catalog of all items in a content pack: weapons, armor, consumables, quest items, trade goods. Each entry pairs gameplay metadata (stats, level reqs, flags, weapon damage / speed) with display metadata (displayId for icon / model, quality color), so the runtime can render inventory tooltips and equip slots from a single load. Format: • magic "WITM", version 1, little-endian • per item: itemId / displayId / quality / itemClass / itemSubClass / inventoryType / flags / requiredLevel / itemLevel / sellPrice / buyPrice / maxStack / durability / damageMin / damageMax / attackSpeedMs / statCount + stats[] / name / description Enums: • Quality: Poor..Heirloom (8 levels) • Class: Consumable, Weapon, Armor, Quest, ... (13) • InventoryType: Head..Cloak..Weapon2H (18 slots) • Flags: Unique, BoP, BoE, QuestItem, Conjured, ... • StatType: Stamina, Strength, Intellect, Defense, ... API: WoweeItemLoader::save / load / exists / findById; presets makeStarter (4-item demo), makeWeapons (5 items common -> legendary), makeArmor (6-piece mail set with BoE flag). CLI added (5 flags, 480 documented total now): --gen-items / --gen-items-weapons / --gen-items-armor --info-wit / --validate-wit Validator catches: itemId=0, duplicate itemIds, weapons with 0 damage or attackSpeed, weapons with non-weapon slot, equippables with durability=0 or maxStack>1, sell price >= buy price (vendor would lose money), out-of-range quality. All 3 presets save / load / re-validate clean. Info-table output includes a gold/silver/copper price formatter for hand-readability.
2026-05-09 15:04:48 -07:00
std::printf(" --gen-items <wit-base> [name]\n");
std::printf(" Emit .wit starter item catalog: 1 weapon + 1 chest + 1 potion + 1 quest item\n");
std::printf(" --gen-items-weapons <wit-base> [name]\n");
std::printf(" Emit .wit weapon catalog: 5 entries spanning common -> legendary, both 1H and 2H\n");
std::printf(" --gen-items-armor <wit-base> [name]\n");
std::printf(" Emit .wit full mail-armor set: head + chest + legs + feet + hands + cloak (BoE)\n");
std::printf(" --info-wit <wit-base> [--json]\n");
std::printf(" Print WIT item entries (id / ilvl / quality / class / slot / buy price / name)\n");
std::printf(" --validate-wit <wit-base> [--json]\n");
std::printf(" Static checks: itemId>0 + unique, weapon damage>0 + min<=max, equippable durability>0, sell<buy\n");
std::printf(" --export-wit-json <wit-base> [out.json]\n");
std::printf(" Export binary .wit to a human-editable JSON sidecar (defaults to <base>.wit.json)\n");
std::printf(" --import-wit-json <json-path> [out-base]\n");
std::printf(" Import a .wit.json sidecar back into binary .wit (accepts quality/class/slot int OR name string)\n");
feat(pipeline): add WLOT (Wowee Loot Table) format Novel open replacement for AzerothCore-style creature_loot_template / gameobject_loot_template SQL tables. The 13th open format added to the editor. Pairs naturally with the WIT item catalog from the preceding commit: each loot drop's itemId references an entry in a WIT file, so a content pack ships both the item definitions and the loot tables that reference them. The runtime composes WIT + WLOT + WSPN to drive the full "creature dies, drops items" flow without any SQL. Format: • magic "WLOT", version 1, little-endian • per table: creatureId / flags / dropCount / moneyMin..Max / itemDropCount + drops[] • per drop: itemId / chancePercent (float, 0..100) / minQty / maxQty / drop_flags Table flags: QuestOnly, GroupOnly, Pickpocket Drop flags: QuestRequired, GroupRollOnly, AlwaysDrop dropCount is the slot budget — how many distinct drops to roll per kill. Each item drop is rolled independently against its chancePercent (so dropCount=2 with 4 candidate drops at varying chances gives the classic "up to 2 distinct items per kill" behavior). Drops with the AlwaysDrop flag bypass the slot budget — used for guaranteed quest items. API: WoweeLootLoader::save / load / exists / findByCreatureId; presets makeStarter (1 table, 1 drop), makeBandit (4 candidates, dropCount=2, matches the camp spawns from WSPN at creatureId=1000), makeBoss (6 candidates including guaranteed quest item via AlwaysDrop and a group-only epic at 5%). CLI added (5 flags, 486 documented total now): --gen-loot / --gen-loot-bandit / --gen-loot-boss --info-wlot / --validate-wlot Validator catches: creatureId=0, duplicates, chance not in 0..100, NaN chance, money min > max, minQty > maxQty, dropCount=0 with non-empty drops list (silent dead config). All 3 presets save / load / re-validate clean. The bandit table's creatureId=1000 deliberately matches WSPN's makeCamp creatureId so the open-format demo content pack already has working cross-references.
2026-05-09 15:11:08 -07:00
std::printf(" --gen-loot <wlot-base> [name]\n");
std::printf(" Emit .wlot starter loot catalog: 1 creature with 1 drop slot, 1 item @ 50%%, 0..50 copper\n");
std::printf(" --gen-loot-bandit <wlot-base> [name]\n");
std::printf(" Emit .wlot bandit loot table: dropCount=2, 4 candidate items, 5..50 copper\n");
std::printf(" --gen-loot-boss <wlot-base> [name]\n");
std::printf(" Emit .wlot elite boss table: dropCount=4, 6 candidates incl. quest item + group-only epic, 50..200 silver\n");
std::printf(" --info-wlot <wlot-base> [--json]\n");
std::printf(" Print WLOT loot tables (creatureId / dropCount / money range / per-drop chance + qty + flags)\n");
std::printf(" --validate-wlot <wlot-base> [--json]\n");
std::printf(" Static checks: creatureId>0 + unique, chance in 0..100, minQty<=maxQty, money min<=max\n");
std::printf(" --export-wlot-json <wlot-base> [out.json]\n");
std::printf(" Export binary .wlot to a human-editable JSON sidecar (defaults to <base>.wlot.json)\n");
std::printf(" --import-wlot-json <json-path> [out-base]\n");
std::printf(" Import a .wlot.json sidecar back into binary .wlot (accepts flag int OR flagsList strings)\n");
feat(pipeline): add WCRT (Wowee Creature Template) format Novel open replacement for the AzerothCore-style creature_template SQL table PLUS the Blizzard CreatureTemplate / CreatureFamily / CreatureType.dbc trio. The 14th open format added to the editor. This is the canonical metadata side of creatures shared across every spawn instance: HP, level range, faction, behavior flags, NPC role bits (vendor / trainer / quest-giver / innkeeper), base damage, equipped gear references. Cross-references with the previously-added formats: WSPN.entry.entryId -> WCRT.entry.creatureId WLOT.entry.creatureId -> WCRT.entry.creatureId WCRT.entry.equipped* -> WIT.entry.itemId The 4-format set (WIT + WLOT + WSPN + WCRT) now lets a content pack define a complete RPG zone's creature ecosystem: what creatures are, where they spawn, what they drop, and what gear they carry — entirely in open formats with no SQL dependencies. Format: • magic "WCRT", version 1, little-endian • per entry: creatureId / displayId / name / subname / minLevel..maxLevel / baseHealth + healthPerLevel / baseMana + manaPerLevel / factionId / npcFlags / typeId / familyId / damageMin..Max / attackSpeedMs / baseArmor / walkSpeed + runSpeed / gossipId / equippedMain + equippedOffhand + equippedRanged / aiFlags Enums: • TypeId: Beast / Dragon / Demon / Elemental / Giant / Undead / Humanoid / Critter / Mechanical • FamilyId: Wolf / Cat / Bear / Boar / Raptor / Hyena / Spider / Gorilla / Crab (for Beast types) • NpcFlags: Vendor / QuestGiver / Trainer / Banker / Innkeeper / FlightMaster / Auctioneer / Repair / Stable • Behavior: Passive / Aggressive / FleeLowHp / CallHelp / NoLeash API: WoweeCreatureLoader::save / load / exists / findById; presets makeStarter (1 innkeeper), makeBandit (creatureId=1000 matches WSPN/WLOT bandit references, equips WIT itemId=1001 sword), makeMerchants (creatureIds 4001/4002/4003 match WSPN village labels). CLI added (5 flags, 493 documented total): --gen-creatures / --gen-creatures-bandit / --gen-creatures-merchants --info-wcrt / --validate-wcrt Validator catches: creatureId=0, duplicates, level=0, minLevel>maxLevel, baseHealth=0, damageMin>damageMax, attackSpeed=0, non-positive walk/runSpeed, behavior flag contradictions (passive+aggressive), vendor with aggressive behavior (player can't trade).
2026-05-09 15:18:44 -07:00
std::printf(" --gen-creatures <wcrt-base> [name]\n");
std::printf(" Emit .wcrt starter creature template: 1 friendly innkeeper (vendor + repair flags)\n");
std::printf(" --gen-creatures-bandit <wcrt-base> [name]\n");
std::printf(" Emit .wcrt bandit (creatureId=1000, matches WSPN camp + WLOT bandit table, equips WIT sword)\n");
std::printf(" --gen-creatures-merchants <wcrt-base> [name]\n");
std::printf(" Emit .wcrt 3-NPC village set (innkeeper / smith / alchemist, matches WSPN village creatureIds)\n");
std::printf(" --info-wcrt <wcrt-base> [--json]\n");
std::printf(" Print WCRT entries (id / level / hp / type / faction / npc-flags / name + subname)\n");
std::printf(" --validate-wcrt <wcrt-base> [--json]\n");
feat(pipeline): add WQT (Wowee Quest Template) format Novel open replacement for AzerothCore-style quest_template SQL tables PLUS the Blizzard Quest.dbc / QuestObjective.dbc trio. The 15th open format added to the editor — and the last gameplay-graph piece the catalog needed. Cross-references with previously-added formats: WQT.giverCreatureId -> WCRT.entry.creatureId WQT.turninCreatureId -> WCRT.entry.creatureId WQT.objective.targetId -> WCRT (kill) / WIT (collect) / WOB (interact) WQT.rewardItem.itemId -> WIT.entry.itemId WQT.prevQuestId -> WQT.entry.questId (intra-format) WQT.nextQuestId -> WQT.entry.questId Together with WIT / WCRT / WLOT / WSPN / WOMX / WOL / WOW / WSND, a content pack can now ship a complete RPG zone (terrain + props + atmosphere + sounds + creatures + items + loot + spawns + quests) entirely in open formats with no SQL or .dbc dependencies. 15 of 15 expected slots filled. Format: • magic "WQTM", version 1, little-endian • per quest: questId / title / objective / description / minLevel..maxLevel + questLevel / requiredClass+RaceMask / prev+nextQuestId / giver+turninCreatureId / objectives[] / xpReward + moneyCopperReward / rewardItems[] / flags Per-objective: kind (kill/collect/interact/visit/escort/cast), targetId, quantity Per-reward: itemId, qty, pickFlags (AutoGiven / PlayerChoice) Quest flags: Daily / Weekly / Raid / Group / AutoComplete / AutoAccept / Repeatable / ClassQuest / Pvp API: WoweeQuestLoader::save / load / exists / findById; presets makeStarter (1 simple kill quest, references the bandit creatureId=1000), makeChain (3-quest chain with prev/next links + AutoComplete bridge + player-choice rewards), makeDaily (Daily+Repeatable+AutoAccept combo). CLI added (5 flags, 500 documented total — round milestone): --gen-quests / --gen-quests-chain / --gen-quests-daily --info-wqt / --validate-wqt Validator catches: questId=0+duplicates, level=0, maxLevel<minLevel, empty title, no objectives without AutoComplete (player can't finish), no rewards at all, Daily without Repeatable (incoherent), targetId=0, quantity=0, unknown objective kind, reward itemId=0 or qty=0. The 3-quest chain demo exercises every major feature: • multiple objective kinds (visit / collect / kill) • prev/next chain links • AutoComplete dialogue-bridge quest • PlayerChoice reward (1 of 2 weapons)
2026-05-09 15:25:02 -07:00
std::printf(" Static checks: creatureId>0+unique, level/hp>0, min<=max, attackSpeed>0, behavior flag conflicts\n");
std::printf(" --export-wcrt-json <wcrt-base> [out.json]\n");
std::printf(" Export binary .wcrt to a human-editable JSON sidecar (defaults to <base>.wcrt.json)\n");
std::printf(" --import-wcrt-json <json-path> [out-base]\n");
std::printf(" Import a .wcrt.json sidecar back into binary .wcrt (accepts type/family/flag int OR name forms)\n");
feat(pipeline): add WQT (Wowee Quest Template) format Novel open replacement for AzerothCore-style quest_template SQL tables PLUS the Blizzard Quest.dbc / QuestObjective.dbc trio. The 15th open format added to the editor — and the last gameplay-graph piece the catalog needed. Cross-references with previously-added formats: WQT.giverCreatureId -> WCRT.entry.creatureId WQT.turninCreatureId -> WCRT.entry.creatureId WQT.objective.targetId -> WCRT (kill) / WIT (collect) / WOB (interact) WQT.rewardItem.itemId -> WIT.entry.itemId WQT.prevQuestId -> WQT.entry.questId (intra-format) WQT.nextQuestId -> WQT.entry.questId Together with WIT / WCRT / WLOT / WSPN / WOMX / WOL / WOW / WSND, a content pack can now ship a complete RPG zone (terrain + props + atmosphere + sounds + creatures + items + loot + spawns + quests) entirely in open formats with no SQL or .dbc dependencies. 15 of 15 expected slots filled. Format: • magic "WQTM", version 1, little-endian • per quest: questId / title / objective / description / minLevel..maxLevel + questLevel / requiredClass+RaceMask / prev+nextQuestId / giver+turninCreatureId / objectives[] / xpReward + moneyCopperReward / rewardItems[] / flags Per-objective: kind (kill/collect/interact/visit/escort/cast), targetId, quantity Per-reward: itemId, qty, pickFlags (AutoGiven / PlayerChoice) Quest flags: Daily / Weekly / Raid / Group / AutoComplete / AutoAccept / Repeatable / ClassQuest / Pvp API: WoweeQuestLoader::save / load / exists / findById; presets makeStarter (1 simple kill quest, references the bandit creatureId=1000), makeChain (3-quest chain with prev/next links + AutoComplete bridge + player-choice rewards), makeDaily (Daily+Repeatable+AutoAccept combo). CLI added (5 flags, 500 documented total — round milestone): --gen-quests / --gen-quests-chain / --gen-quests-daily --info-wqt / --validate-wqt Validator catches: questId=0+duplicates, level=0, maxLevel<minLevel, empty title, no objectives without AutoComplete (player can't finish), no rewards at all, Daily without Repeatable (incoherent), targetId=0, quantity=0, unknown objective kind, reward itemId=0 or qty=0. The 3-quest chain demo exercises every major feature: • multiple objective kinds (visit / collect / kill) • prev/next chain links • AutoComplete dialogue-bridge quest • PlayerChoice reward (1 of 2 weapons)
2026-05-09 15:25:02 -07:00
std::printf(" --gen-quests <wqt-base> [name]\n");
std::printf(" Emit .wqt starter quest: 'Kill 10 Defias Bandits' giver=4001 (matches WCRT village innkeeper)\n");
std::printf(" --gen-quests-chain <wqt-base> [name]\n");
std::printf(" Emit .wqt 3-quest chain: Investigate -> Recover -> Report (chained via prev/next questId)\n");
std::printf(" --gen-quests-daily <wqt-base> [name]\n");
std::printf(" Emit .wqt daily repeatable quest with the Daily + Repeatable + AutoAccept flag combo\n");
std::printf(" --info-wqt <wqt-base> [--json]\n");
std::printf(" Print WQT entries (questId / level / giver / objectives / rewards / chain links)\n");
std::printf(" --validate-wqt <wqt-base> [--json]\n");
std::printf(" Static checks: questId>0+unique, level>0+min<=max, title not empty, no rewards warning, daily needs repeatable\n");
std::printf(" --export-wqt-json <wqt-base> [out.json]\n");
std::printf(" Export binary .wqt to a human-editable JSON sidecar (defaults to <base>.wqt.json)\n");
std::printf(" --import-wqt-json <json-path> [out-base]\n");
std::printf(" Import a .wqt.json sidecar back into binary .wqt (accepts kind/flag int OR name forms)\n");
feat(pipeline): add WGOT (Wowee Game Object Template) format Novel open replacement for AzerothCore-style gameobject_template SQL tables PLUS the Blizzard GameObjectDisplayInfo.dbc / GameObject types metadata. The 16th open format added to the editor. Game objects are the non-creature interactable scenery: chests (with loot), doors, buttons, mailboxes, herb / ore gathering nodes, fishing pools, signposts, mounts. Each has a displayId for the model, a typeId driving its interaction logic, and optional cross-references to a lock (future WLCK) and loot table (existing WLOT). Cross-references with previously-added formats: WSPN.entry.entryId (kind=GameObject) -> WGOT.entry.objectId WGOT.entry.lootTableId -> WLOT.entry.creatureId (loot tables are universal — chests and creatures both key by ID) The dungeon preset's Bandit Strongbox uses lootTableId=2000 to match WLOT's bandit chest table id, so the demo content stack already wires together: spawn (WSPN object kind 2000) -> object template (WGOT 2000) -> loot table (WLOT 2000). Format: • magic "WGOT", version 1, little-endian • per object: objectId / displayId / name / typeId / size / castBarCaption / requiredSkill + requiredSkillValue / lockId / lootTableId / minOpenTimeMs..maxOpenTimeMs / flags Enums: • TypeId (16): Door / Button / Chest / Container / QuestGiver / Text / Trap / Goober / Transport / Mailbox / MineralNode / HerbNode / FishingNode / Mount / Sign / Bonfire • Flags: Disabled / ScriptOnly / UsableFromMount / Despawn / Frozen / QuestGated API: WoweeGameObjectLoader::save / load / exists / findById; presets makeStarter (chest + mailbox + sign), makeDungeon (door + button + 2 chests + trap with proper WLOT cross-references), makeGather (Peacebloom herb + Tin Vein ore + fishing pool with skill requirements). CLI added (5 flags, 507 documented total now): --gen-objects / --gen-objects-dungeon / --gen-objects-gather --info-wgot / --validate-wgot Validator catches: objectId=0 + duplicates, size<=0, minOpenTime>maxOpenTime, gathering node without skill requirement (anyone can harvest — usually a typo), chest without loot table (script must populate), requiredSkillValue set without requiredSkill (incoherent).
2026-05-09 15:31:49 -07:00
std::printf(" --gen-objects <wgot-base> [name]\n");
std::printf(" Emit .wgot starter object catalog: 1 chest + 1 mailbox + 1 sign\n");
std::printf(" --gen-objects-dungeon <wgot-base> [name]\n");
std::printf(" Emit .wgot dungeon set: door + button + 2 chests (bandit + boss) + spike trap (cross-refs WLOT)\n");
std::printf(" --gen-objects-gather <wgot-base> [name]\n");
std::printf(" Emit .wgot gathering nodes: Peacebloom (herb) + Tin Vein (ore) + Schools of Fish, with skill reqs\n");
std::printf(" --info-wgot <wgot-base> [--json]\n");
std::printf(" Print WGOT entries (objectId / type / lock / loot / required skill / name)\n");
std::printf(" --validate-wgot <wgot-base> [--json]\n");
std::printf(" Static checks: objectId>0+unique, size>0, time min<=max, gathering needs skill, chest warns on no loot\n");
std::printf(" --export-wgot-json <wgot-base> [out.json]\n");
std::printf(" Export binary .wgot to a human-editable JSON sidecar (defaults to <base>.wgot.json)\n");
std::printf(" --import-wgot-json <json-path> [out-base]\n");
std::printf(" Import a .wgot.json sidecar back into binary .wgot (accepts type/flag int OR name forms)\n");
feat(pipeline): add WFAC (Wowee Faction Catalog) format Novel open replacement for Blizzard's Faction.dbc + FactionTemplate.dbc + the AzerothCore-style reputation_reward / reputation_spillover SQL tables. The 17th open format added to the editor. Combines the "displayable Faction" (player-facing name + reputation thresholds for friendly/honored/revered/exalted) with the "FactionTemplate matrix" (which factions are hostile to which) into one entry. The runtime walks the catalog to answer two questions: • "Will faction A attack faction B on sight?" -> enemy list • "What rep tier is the player with X?" -> thresholds Cross-references with previously-added formats: WCRT.entry.factionId -> WFAC.entry.factionId WFAC.entry.parentFactionId -> WFAC.entry.factionId WFAC.entry.enemies[] -> WFAC.entry.factionId WFAC.entry.friends[] -> WFAC.entry.factionId The starter preset's factionId 35 (Friendly) and 14 (Hostile) deliberately match the WCRT preset defaults, so the demo content stack is consistent: WCRT.makeBandit's factionId=14 has a real entry in WFAC.makeStarter that declares it hostile to friendly NPCs (35) and players (1). Format: • magic "WFAC", version 1, little-endian • per faction: factionId / parentFactionId / name / description / reputationFlags / baseReputation / 7 ascending tier thresholds (hostile..exalted) / enemies[] / friends[] Enums: • ReputationFlags: VisibleOnTab / AtWarDefault / Hidden / NoReputation / IsHeader (group label) • Tier (canonical): Hated / Hostile / Unfriendly / Neutral / Friendly / Honored / Revered / Exalted API: WoweeFactionLoader::save / load / exists / findById + WoweeFaction::isHostile(a, b); presets makeStarter (3-faction demo matching WCRT defaults), makeAlliance (header + Stormwind / Darnassus / Ironforge with reciprocal friend lists + Defias enemy), makeWildlife (4 beast factions, each hostile to player but ignoring other beasts). CLI added (5 flags, 514 documented total now): --gen-factions / --gen-factions-alliance / --gen-factions-wildlife --info-wfac / --validate-wfac Validator catches: factionId=0 + duplicates, empty name, threshold ordering violations (hostile must be < unfriendly < neutral < ... < exalted), self-listed as enemy or friend, faction in both enemies and friends (incoherent).
2026-05-09 15:37:59 -07:00
std::printf(" --gen-factions <wfac-base> [name]\n");
std::printf(" Emit .wfac starter: 3 factions (Friendly id=35 / Hostile id=14 / Player id=1) matching WCRT defaults\n");
std::printf(" --gen-factions-alliance <wfac-base> [name]\n");
std::printf(" Emit .wfac Alliance set: header + Stormwind + Darnassus + Ironforge (reciprocal friends) + Defias enemy\n");
std::printf(" --gen-factions-wildlife <wfac-base> [name]\n");
std::printf(" Emit .wfac wildlife: wolves + bears + spiders + kobolds (each hostile to player, ignores other beasts)\n");
std::printf(" --info-wfac <wfac-base> [--json]\n");
std::printf(" Print WFAC entries (id / parent / flags / enemy + friend counts / name)\n");
std::printf(" --validate-wfac <wfac-base> [--json]\n");
std::printf(" Static checks: factionId>0+unique, name not empty, threshold ordering, no self-enemy, no enemy/friend overlap\n");
std::printf(" --export-wfac-json <wfac-base> [out.json]\n");
std::printf(" Export binary .wfac to a human-editable JSON sidecar (defaults to <base>.wfac.json)\n");
std::printf(" --import-wfac-json <json-path> [out-base]\n");
std::printf(" Import a .wfac.json sidecar back into binary .wfac (accepts flag int OR flagsList strings)\n");
feat(pipeline): add WLCK (Wowee Lock Template) format Novel open replacement for Blizzard's Lock.dbc. The 18th open format added to the editor. Closes the cross-reference gap from WGOT.entry.lockId — until now that field pointed to a format that didn't exist yet. A lock is a multi-channel security check. Each lock has up to 5 independent channels; a player can open the lock by satisfying ANY ONE channel: • Item — requires a specific key item (WIT cross-ref) • Lockpick — requires the lockpicking skill at minimum rank (rogue / engineering profession) • Spell — requires casting a specific spell • Damage — can be forced open with attack damage Cross-references with previously-added formats: WGOT.entry.lockId -> WLCK.entry.lockId WLCK.channel.targetId (Item) -> WIT.entry.itemId WLCK.channel.targetId (Lockpick) -> future WSKL skillId WLCK.channel.targetId (Spell) -> future WSPL spellId The starter and dungeon presets' lockIds (1 and 2) deliberately match WGOT.makeDungeon's iron-door lockId=1 and bandit-strongbox lockId=2, so the demo content stack already wires together: WSPN spawn -> WGOT object template -> WLCK lock template -> WIT key items. Format: • magic "WLCK", version 1, little-endian • per lock: lockId / name / flags / 5 fixed channel slots • per channel: kind / skillRequired / targetId • all 5 slots written even when unused (kind=None + zeroed fields), keeping the per-entry size constant for fast random access Enums: • ChannelKind: None / Item / Lockpick / Spell / Damage • Flags: DestructOnOpen / RespawnOnKey / TrapOnFail API: WoweeLockLoader::save / load / exists / findById; presets makeStarter (Iron Door + Wooden Chest), makeDungeon (matches WGOT cross-references; light/heavy lockpicks + boss-key-only seal), makeProfessions (4-tier rogue lockpick progression at ranks 1/100/175/250). CLI added (5 flags, 521 documented total now): --gen-locks / --gen-locks-dungeon / --gen-locks-professions --info-wlck / --validate-wlck Validator catches: lockId=0 + duplicates, all-None channels (lock can never open), Item/Spell/Lockpick channels with targetId=0 (no resource referenced), unknown channel kind, skillRequired set on non-Lockpick channel (silently ignored at runtime — flag as warning).
2026-05-09 15:44:26 -07:00
std::printf(" --gen-locks <wlck-base> [name]\n");
std::printf(" Emit .wlck starter: 2 locks (Iron Door key+force, Wooden Chest force-only) — lockId=1 matches WGOT\n");
std::printf(" --gen-locks-dungeon <wlck-base> [name]\n");
std::printf(" Emit .wlck dungeon set: light/heavy lockpicks (lockId=2 matches WGOT bandit chest), boss-key seal\n");
std::printf(" --gen-locks-professions <wlck-base> [name]\n");
std::printf(" Emit .wlck profession-keyed locks at lockpick rank 1/100/175/250 (junkbox tier progression)\n");
std::printf(" --info-wlck <wlck-base> [--json]\n");
std::printf(" Print WLCK lock entries with per-channel detail (kind / target / required skill rank)\n");
std::printf(" --validate-wlck <wlck-base> [--json]\n");
std::printf(" Static checks: lockId>0+unique, at least 1 active channel, item/spell/lockpick need targetId\n");
std::printf(" --export-wlck-json <wlck-base> [out.json]\n");
std::printf(" Export binary .wlck to a human-editable JSON sidecar (defaults to <base>.wlck.json)\n");
std::printf(" --import-wlck-json <json-path> [out-base]\n");
std::printf(" Import a .wlck.json sidecar back into binary .wlck (accepts kind/flag int OR name forms)\n");
feat(pipeline): add WSKL (Wowee Skill Catalog) format Novel open replacement for Blizzard's SkillLine.dbc + SkillLineCategory.dbc + the AzerothCore-style player skill base tables. The 19th open format added to the editor. Defines every player-trackable skill: weapon proficiencies (Swords, Axes, Bows), professions (Mining, Alchemy, Cooking), languages (Common, Dwarvish), class specializations (Fire, Frost, Holy, Protection), armor proficiencies (Mail, Plate), and secondary skills (First Aid, Lockpicking, Riding). Cross-references with previously-added formats: WLCK.channel.targetId (kind=Lockpick) -> WSKL.entry.skillId WGOT.entry.requiredSkill -> WSKL.entry.skillId The starter preset's skillIds 186 (Mining) and 633 (Lockpicking) deliberately match the canonical IDs already referenced by WGOT.makeGather and WLCK.makeDungeon — so the demo content stack now wires together end-to-end: WGOT herb-node requires skill 186 -> WSKL Mining at rank 1+; WLCK bandit-strongbox channel requires skill 633 -> WSKL Lockpicking at rank 1+. Format: • magic "WSKL", version 1, little-endian • per skill: skillId / name / description / categoryId / canTrain / maxRank / rankPerLevel / iconPath Enums: • CategoryId (8): Weapon / Class / Profession / SecondaryProfession / Language / ArmorProficiency / Riding / WeaponSpec API: WoweeSkillLoader::save / load / exists / findById; presets makeStarter (5-skill demo with cross-referenced canonical IDs), makeProfessions (12 classic professions: 9 primary + 3 secondary), makeWeapons (16 weapon skills with canonical SkillLine IDs and rankPerLevel=5 auto-grow). CLI added (5 flags, 528 documented total now): --gen-skills / --gen-skills-professions / --gen-skills-weapons --info-wskl / --validate-wskl Validator catches: skillId=0 + duplicates, empty name, maxRank=0, unknown categoryId, suspicious maxRank=1 on non-Language skill (only languages cap at 1), weapon skill with rankPerLevel=0 (won't auto-grow on use).
2026-05-09 15:50:25 -07:00
std::printf(" --gen-skills <wskl-base> [name]\n");
std::printf(" Emit .wskl starter: Swords + Common + First Aid + Mining (id=186) + Lockpicking (id=633) — matches WGOT/WLCK\n");
std::printf(" --gen-skills-professions <wskl-base> [name]\n");
std::printf(" Emit .wskl 12 classic professions (9 primary + 3 secondary) with canonical SkillLine IDs\n");
std::printf(" --gen-skills-weapons <wskl-base> [name]\n");
std::printf(" Emit .wskl all 16 weapon skills with rankPerLevel=5 auto-grow (use-trained, not trainer-trained)\n");
std::printf(" --info-wskl <wskl-base> [--json]\n");
std::printf(" Print WSKL entries (id / category / max rank / per-level grow / trainer-required / name)\n");
std::printf(" --validate-wskl <wskl-base> [--json]\n");
std::printf(" Static checks: skillId>0+unique, name not empty, maxRank>0, weapon needs rankPerLevel>0\n");
std::printf(" --export-wskl-json <wskl-base> [out.json]\n");
std::printf(" Export binary .wskl to a human-editable JSON sidecar (defaults to <base>.wskl.json)\n");
std::printf(" --import-wskl-json <json-path> [out-base]\n");
std::printf(" Import a .wskl.json sidecar back into binary .wskl (accepts category int OR categoryName string)\n");
feat(pipeline): add WSPL (Wowee Spell Catalog) format Novel open replacement for Blizzard's Spell.dbc + SpellEffect.dbc + the AzerothCore-style spell_dbc / spell_proc tables. The 20th open format added to the editor — completes the canonical-data side of the gameplay graph. Each entry holds the metadata side of a spell: name, description, school, range, mana / cast / cooldown times, plus a single primary effect. The simplified effect model (one effectKind + min/max value + misc field) covers the common cases (damage / heal / buff / debuff / teleport / summon / dispel) without needing to reproduce the full multi-effect graph that classic Spell.dbc carries. Cross-references with previously-added formats: WLCK.channel.targetId (kind=Spell) -> WSPL.entry.spellId WQT.objective.targetId (kind=SpellCast) -> WSPL.entry.spellId WCRT.equippedMain (item with on-use) -> WIT -> WSPL Format: • magic "WSPL", version 1, little-endian • per spell: spellId / name / description / iconPath / school / targetType / effectKind / cast & cooldown & GCD ms / manaCost / range min..max / minLevel / maxStacks / durationMs / effectValueMin..Max / effectMisc / flags Enums: • School (7): Physical / Holy / Fire / Nature / Frost / Shadow / Arcane • TargetType (6): Self / Single / Cone / AoeFromSelf / Line / Ground • EffectKind (7): Damage / Heal / Buff / Debuff / Teleport / Summon / Dispel • Flags: Passive / Hidden / Channeled / Ranged / AreaOfEffect / Triggered / UnitTargetOnly / FriendlyOnly / HostileOnly API: WoweeSpellLoader::save / load / exists / findById; presets makeStarter (Strike + Lesser Heal + Power Word: Fortitude + Hearthstone, one per major effect kind), makeMage (Frostbolt 116 + Fireball 133 + Arcane Intellect 1459 + Blink 1953, canonical Classic spellIds), makeWarrior (Heroic Strike 78 + Thunder Clap 6343 + Battle Shout 6673 + Mortal Strike 12294). CLI added (5 flags, 535 documented total now): --gen-spells / --gen-spells-mage / --gen-spells-warrior --info-wspl / --validate-wspl Validator catches: spellId=0 + duplicates, empty name, school out of range, effectKind out of range, NaN range, range/value min>max, FriendlyOnly+HostileOnly conflict (incoherent), friendly-only with damage/debuff effect (incoherent), hostile-only with heal/buff effect, buff/debuff effect with durationMs=0 (instant fade — almost certainly authoring oversight). The validator caught a real preset-emitter authoring error during initial smoke testing — buff spells were setting effectValueMin without effectValueMax (validator's range check immediately flagged it), prompting an in-batch fix to set both fields. This is exactly the catch-the-typo purpose validators serve.
2026-05-09 15:58:09 -07:00
std::printf(" --gen-spells <wspl-base> [name]\n");
std::printf(" Emit .wspl starter spell catalog: 4 spells covering damage / heal / buff / teleport effect kinds\n");
std::printf(" --gen-spells-mage <wspl-base> [name]\n");
std::printf(" Emit .wspl mage spell set: Frostbolt + Fireball + Arcane Intellect + Blink (canonical IDs 116/133/1459/1953)\n");
std::printf(" --gen-spells-warrior <wspl-base> [name]\n");
std::printf(" Emit .wspl warrior spell set: Heroic Strike + Thunder Clap + Battle Shout + Mortal Strike\n");
std::printf(" --info-wspl <wspl-base> [--json]\n");
std::printf(" Print WSPL spell entries (id / school / effect / cast/cd / mana / range / damage range / name)\n");
std::printf(" --validate-wspl <wspl-base> [--json]\n");
std::printf(" Static checks: spellId>0+unique, name not empty, school 0..6, range/value min<=max, friendly+hostile incoherent\n");
std::printf(" --export-wspl-json <wspl-base> [out.json]\n");
std::printf(" Export binary .wspl to a human-editable JSON sidecar (defaults to <base>.wspl.json)\n");
std::printf(" --import-wspl-json <json-path> [out-base]\n");
std::printf(" Import a .wspl.json sidecar back into binary .wspl (accepts school/target/effect/flag int OR name forms)\n");
feat(pipeline): add WACH (Wowee Achievement Catalog) format Novel open replacement for Blizzard's Achievement.dbc + AchievementCriteria.dbc + AchievementCategory.dbc + the AzerothCore-style character_achievement / character_achievement_progress SQL tables. The 21st open format added to the editor. Each achievement carries display metadata (name, description, icon, points, faction restriction) plus a list of criteria the player must satisfy. Criteria mirror the WQT objective model (kind + targetId + quantity), so the runtime can reuse the same progress-tracking machinery for both quests and achievements. Cross-references with previously-added formats — every criterion kind has a real format target: WACH.criteria.targetId (kind=KillCreature) -> WCRT.creatureId WACH.criteria.targetId (kind=CompleteQuest) -> WQT.questId WACH.criteria.targetId (kind=LootItem) -> WIT.itemId WACH.criteria.targetId (kind=CastSpell) -> WSPL.spellId WACH.criteria.targetId (kind=ReachSkillLevel) -> WSKL.skillId WACH.criteria.targetId (kind=EarnReputation) -> WFAC.factionId WACH.criteria.targetId (kind=CompleteAchievement) -> WACH.achievementId (meta-achievements) Format: • magic "WACH", version 1, little-endian • per achievement: id / categoryId / name / description / iconPath / titleReward / points / minLevel / faction / flags / criteria[] • per criterion: criteriaId / kind / targetId / quantity / description Enums: • CriteriaKind (9): KillCreature / CompleteQuest / LootItem / ReachLevel / EarnReputation / CastSpell / ReachSkillLevel / VisitArea / CompleteAchievement • Faction: Both / Alliance / Horde • Flags: HiddenUntilEarned / ServerFirst / RealmFirst / Tracking / Counter / Account API: WoweeAchievementLoader::save / load / exists / findById; presets makeStarter (3 simple kill/quest/level demos), makeBandit (3 with WCRT/WGOT/WQT cross-refs), makeMeta (3 base + 1 meta-achievement granting "the Versatile" title, exercising CompleteAchievement criterion kind that lets achievements depend on other achievements). CLI added (5 flags, 542 documented total now): --gen-achievements / --gen-achievements-bandit / --gen-achievements-meta --info-wach / --validate-wach Validator catches: achievementId=0 + duplicates, empty name, faction out of range, no criteria (achievement can never be earned), criterion quantity=0, unknown criterion kind, targetId=0 on criterion kinds that need a real resource reference (everything except ReachLevel which uses the quantity field for the level number). The bandit preset's cross-references close the gameplay graph end-to-end: kill 50 creatureId=1000 (matches WCRT/ WSPN/WLOT bandit), loot objectId=2000 (matches WGOT bandit strongbox), complete questId=1 (matches WQT Bandit Trouble). The meta preset closes a separate loop: 3 sub-achievements covering Mining (skillId=186), Lockpicking (skillId=633), and Frostbolt cast count (spellId=116) — each pointing at a real WSKL/WSPL entry that already exists in the demo content stack.
2026-05-09 16:04:30 -07:00
std::printf(" --gen-achievements <wach-base> [name]\n");
std::printf(" Emit .wach starter: 3 achievements (First Blood / Helping Hand / Coming of Age)\n");
std::printf(" --gen-achievements-bandit <wach-base> [name]\n");
std::printf(" Emit .wach bandit-themed: 3 achievements with cross-refs to WCRT/WGOT/WQT bandit IDs\n");
std::printf(" --gen-achievements-meta <wach-base> [name]\n");
std::printf(" Emit .wach 3 base achievements + 1 meta-achievement (Jack of All Trades, title 'the Versatile')\n");
std::printf(" --info-wach <wach-base> [--json]\n");
std::printf(" Print WACH entries (id / points / faction / flags / criteria with kind+target+qty)\n");
std::printf(" --validate-wach <wach-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name not empty, faction 0..2, criteria need targetId+quantity>0\n");
std::printf(" --export-wach-json <wach-base> [out.json]\n");
std::printf(" Export binary .wach to a human-editable JSON sidecar (defaults to <base>.wach.json)\n");
std::printf(" --import-wach-json <json-path> [out-base]\n");
std::printf(" Import a .wach.json sidecar back into binary .wach (accepts kind/faction/flag int OR name forms)\n");
feat(pipeline): add WTRN (Wowee Trainer / Vendor catalog) format Novel open replacement for AzerothCore-style npc_trainer + npc_vendor SQL tables PLUS the Blizzard TrainerSpells.dbc family. The 22nd open format added to the editor. Unifies trainer spell lists and vendor item inventories into one per-NPC entry. A creature flagged Trainer or Vendor in WCRT references a WTRN entry that lists what they teach / sell. The same NPC can be both — kindMask is a bitmask covering the Trainer (0x01) and Vendor (0x02) kinds. This format closes a major cross-format gap: WCRT.npcFlags already had Vendor / Trainer bits, but until now there was no format defining what a vendor sells or what a trainer teaches. Now an NPC marked Vendor in WCRT has a real inventory, and an NPC marked Trainer has a real spell list. Cross-references — every WTRN field has a real format target: WTRN.entry.npcId -> WCRT.entry.creatureId WTRN.spell.spellId -> WSPL.entry.spellId WTRN.spell.requiredSkillId -> WSKL.entry.skillId WTRN.item.itemId -> WIT.entry.itemId Format: • magic "WTRN", version 1, little-endian • per NPC: npcId / kindMask / greeting + spells[] + items[] • per spell offer: spellId / moneyCostCopper / requiredSkillId / requiredSkillRank / requiredLevel • per item offer: itemId / stockCount (0xFFFFFFFF = unlimited) / restockSec / extendedCost / moneyCostCopper (0 = inherit from WIT.buyPrice) API: WoweeTrainerLoader::save / load / exists / findByNpc; presets makeStarter (innkeeper 4001 as both trainer + vendor: teaches First Aid + sells starter items), makeMageTrainer (NPC 4003 teaches the WSPL mage spells at scaling cost), makeWeaponVendor (NPC 4002 sells WIT weapons with mixed unlimited/finite stock + restock timers). CLI added (5 flags, 551 documented total now): --gen-trainers / --gen-trainers-mage / --gen-trainers-weapons --info-wtrn / --validate-wtrn Validator catches: npcId=0 + duplicates, kindMask=0 (NPC offers nothing), Trainer flag without spells, Vendor flag without items, spells/items present without the matching kind bit (silently ignored at runtime), spellId=0 / itemId=0 in offers, finite stock with restockSec=0 (single-fill — usually intentional but worth surfacing). The 3 presets deliberately use npcIds matching WCRT village merchants (4001/4002/4003) so the demo content stack is self-consistent: WCRT 4001 has the Vendor + Trainer flag, and WTRN 4001 actually defines what they sell and teach.
2026-05-09 16:12:58 -07:00
std::printf(" --gen-trainers <wtrn-base> [name]\n");
std::printf(" Emit .wtrn starter: 1 NPC (innkeeper 4001) acting as both vendor + trainer with WSKL/WIT cross-refs\n");
std::printf(" --gen-trainers-mage <wtrn-base> [name]\n");
std::printf(" Emit .wtrn mage trainer (npcId=4003): teaches Frostbolt/Fireball/Arcane Intellect/Blink at scaling cost\n");
std::printf(" --gen-trainers-weapons <wtrn-base> [name]\n");
std::printf(" Emit .wtrn weapon vendor (npcId=4002): 5 weapons with mixed unlimited/finite stock + restock timers\n");
std::printf(" --info-wtrn <wtrn-base> [--json]\n");
std::printf(" Print WTRN entries (npc / kind / spells with skill+level reqs / items with stock + restock)\n");
std::printf(" --validate-wtrn <wtrn-base> [--json]\n");
std::printf(" Static checks: npcId>0+unique, kindMask>0, Trainer needs spells, Vendor needs items, no orphan offers\n");
std::printf(" --export-wtrn-json <wtrn-base> [out.json]\n");
std::printf(" Export binary .wtrn to a human-editable JSON sidecar (defaults to <base>.wtrn.json)\n");
std::printf(" --import-wtrn-json <json-path> [out-base]\n");
std::printf(" Import a .wtrn.json sidecar back into binary .wtrn (accepts kindMask int OR kindList string array)\n");
feat(pipeline): add WGSP (Wowee Gossip Menu) format Novel open replacement for AzerothCore-style gossip_menu + gossip_menu_option + npc_text SQL tables PLUS the Blizzard NpcText.dbc family. The 23rd open format added to the editor. An NPC's dialogue tree: a menu of options the player can pick from when right-clicking the NPC. Each option may bridge to another menu, trigger a vendor / trainer interaction, offer a quest, etc. The simplified per-option model (kind + actionTarget + flags + moneyCost) covers the common cases without needing separate npc_text condition tables. Closes a major cross-format gap: WCRT.entry.gossipId has existed since batch 116 (when WCRT was added) but pointed to a format that didn't exist yet. The innkeeper preset's menuId=4001 deliberately matches WCRT's Bartleby NPC so the demo content stack can wire WCRT.gossipId = 4001 once that field is plumbed through the runtime. Cross-references: WCRT.entry.gossipId -> WGSP.entry.menuId WGSP.option.actionTarget (Submenu) -> WGSP.entry.menuId WGSP.option.actionTarget (Vendor / Trainer) -> WTRN.entry.npcId WGSP.option.actionTarget (Quest) -> WQT.entry.questId Format: • magic "WGSP", version 1, little-endian • per menu: menuId / titleText + options[] • per option: optionId / text / kind / actionTarget / requiredFlags / moneyCostCopper Enums: • OptionKind (13): Close / Submenu / Vendor / Trainer / Quest / Tabard / Banker / Innkeeper / FlightMaster / TextOnly / Script / Battlemaster / Auctioneer • OptionFlags: AllianceOnly / HordeOnly / Coinpouch / QuestGated / Closes API: WoweeGossipLoader::save / load / exists / findById; presets makeStarter (1 menu with vendor + trainer + close), makeInnkeeper (2-menu tree: main menu 4001 with hearth / vendor / flight / submenu options + lore submenu 4002 that links back), makeQuestGiver (1 menu with 2 quest options referencing WQT 1 and 100, plus a paid respec script exercising the Coinpouch flag with a 10g cost). CLI added (5 flags, 558 documented total now): --gen-gossip / --gen-gossip-innkeeper / --gen-gossip-questgiver --info-wgsp / --validate-wgsp Validator catches: menuId=0 + duplicates, empty title / options, unknown option kind, empty option text, Submenu options pointing at non-existent menuIds (intra-format cross-reference resolution), Coinpouch flag without moneyCost (misleading UI), AllianceOnly+HordeOnly conflict.
2026-05-09 16:20:07 -07:00
std::printf(" --gen-gossip <wgsp-base> [name]\n");
std::printf(" Emit .wgsp starter: 1 menu with greeting + 3 options (vendor / trainer / close)\n");
std::printf(" --gen-gossip-innkeeper <wgsp-base> [name]\n");
std::printf(" Emit .wgsp 2-menu innkeeper tree (menuId=4001 closes WCRT.gossipId gap; submenu 4002 area lore)\n");
std::printf(" --gen-gossip-questgiver <wgsp-base> [name]\n");
std::printf(" Emit .wgsp questgiver menu: 2 quest options + bank + paid respec (Coinpouch flag, 10g)\n");
std::printf(" --info-wgsp <wgsp-base> [--json]\n");
std::printf(" Print WGSP entries (menuId / title / per-option kind / target / cost / flags)\n");
std::printf(" --validate-wgsp <wgsp-base> [--json]\n");
std::printf(" Static checks: menuId>0+unique, options non-empty, Submenu actionTarget exists, Coinpouch needs cost, faction conflict\n");
std::printf(" --export-wgsp-json <wgsp-base> [out.json]\n");
std::printf(" Export binary .wgsp to a human-editable JSON sidecar (defaults to <base>.wgsp.json)\n");
std::printf(" --import-wgsp-json <json-path> [out-base]\n");
std::printf(" Import a .wgsp.json sidecar back into binary .wgsp (accepts kind/flag int OR name forms)\n");
feat(pipeline): add WTAX (Wowee Taxi catalog) format Novel open replacement for Blizzard's TaxiNodes.dbc + TaxiPath.dbc + TaxiPathNode.dbc. The 24th open format added to the editor. Defines the flight-master network: a set of named nodes (positions on the world map) plus the paths between them (sequences of waypoints with per-segment delay and a per-path gold cost). The same file holds both node and path lists — flat arrays keyed by id, with intra-format references from path.fromNodeId / toNodeId to node.nodeId. Cross-references: WCRT.entry (with FlightMaster npcFlag) ~= WTAX.nodeId (matched by world position; flight master NPCs stand at their nodes) WTAX.path.fromNodeId / toNodeId -> WTAX.entry.nodeId (intra-format graph) Format: • magic "WTAX", version 1, little-endian • nodes (each): nodeId / mapId / name / iconPath / position / faction restrictions • paths (each): pathId / from+toNodeId / moneyCostCopper / waypoints[] each with position + per-waypoint delaySec API: WoweeTaxiLoader::save / load / exists + WoweeTaxi::findNode / findPath / findPathBetween. Three preset emitters showcase different graph shapes: • makeStarter — 2 nodes + 2 paths (round-trip) • makeRegion — 4 nodes at a 500m square + 4-path directed ring (NW->NE->SE->SW->NW) • makeContinent — 6 nodes hub-spoke + 3 perimeter shortcuts; intermediate waypoints climb to altitude 120m for visual arc effect CLI added (5 flags, 564 documented total now): --gen-taxi / --gen-taxi-region / --gen-taxi-continent --info-wtax / --validate-wtax Validator catches: nodeId/pathId=0 + duplicates, empty node name, non-finite positions, fromNodeId == toNodeId (self-loop path), path references to non-existent nodes (intra-format cross-reference resolution), negative waypoint delays.
2026-05-09 16:26:27 -07:00
std::printf(" --gen-taxi <wtax-base> [name]\n");
std::printf(" Emit .wtax starter: 2 nodes (Stormwind / Goldshire) + 2 paths (round-trip, 50s each, 3 waypoints)\n");
std::printf(" --gen-taxi-region <wtax-base> [name]\n");
std::printf(" Emit .wtax 4-node region: NW/NE/SE/SW outposts on a 500m square + 4-path directed ring\n");
std::printf(" --gen-taxi-continent <wtax-base> [name]\n");
std::printf(" Emit .wtax 6-node hub-spoke continent: central crossroads + 5 outliers + 3 perimeter shortcuts (8 paths)\n");
std::printf(" --info-wtax <wtax-base> [--json]\n");
std::printf(" Print WTAX nodes (id / map / position / name) + paths (id / from->to / cost / waypoint count)\n");
std::printf(" --validate-wtax <wtax-base> [--json]\n");
std::printf(" Static checks: ids>0+unique, finite positions, paths reference real nodes, no self-loop, non-negative delays\n");
std::printf(" --export-wtax-json <wtax-base> [out.json]\n");
std::printf(" Export binary .wtax to a human-editable JSON sidecar (defaults to <base>.wtax.json)\n");
std::printf(" --import-wtax-json <json-path> [out-base]\n");
std::printf(" Import a .wtax.json sidecar back into binary .wtax (round-trip with --export-wtax-json)\n");
feat(pipeline): add WTAL (Wowee Talent catalog) format Novel open replacement for Blizzard's TalentTab.dbc + Talent.dbc + the AzerothCore-style talent_progression SQL tables. The 25th open format added to the editor. Defines class talent specialization trees: per-class set of named tabs (Arms / Fury / Protection for warrior, Fire / Frost / Arcane for mage), each with talents arranged in a row/column grid, each talent having up to 5 ranks and an optional prerequisite chain. Cross-references with previously-added formats: WTAL.talent.prereqTalentId -> WTAL.talent.talentId (intra-format chain) WTAL.talent.rankSpellIds[] -> WSPL.entry.spellId (spell granted at each rank) Format: • magic "WTAL", version 1, little-endian • per tree: treeId / name / iconPath / requiredClassMask / talents[] (row, col, maxRank, prereqTalentId+rank, rankSpellIds[5] zero-padded for unused ranks) Enums: • ClassMask: bit positions match canonical CharClasses.dbc classIds — Warrior / Paladin / Hunter / Rogue / Priest / DK / Shaman / Mage / Warlock / Druid API: WoweeTalentLoader::save / load / exists + WoweeTalent::findTree / findTalent (global lookup across all trees in the catalog). Three preset emitters showcase tree shapes: • makeStarter — 1 small tree (3-talent vertical chain) • makeWarrior — 3 trees (Arms 4 / Fury 4 / Protection 3) with WSPL cross-refs at capstones (Mortal Strike -> WSPL 12294, Battle Shout -> WSPL 6673, Thunder Clap -> WSPL 6343) • makeMage — 3 trees (Arcane / Fire / Frost) with capstones referencing Frostbolt 116 / Fireball 133 / Blink 1953 from WSPL CLI added (5 flags, 571 documented total now): --gen-talents / --gen-talents-warrior / --gen-talents-mage --info-wtal / --validate-wtal Validator catches: tree+talent ids=0 or duplicates, empty tree name, requiredClassMask=0 (every class would see this tree — usually a typo), maxRank not in 1..5, talent listing itself as prerequisite, prereqTalentId pointing at a talent that doesn't exist in this catalog (intra-format cross-reference resolution), prereqRank=0 or > the prereq talent's maxRank (catches off-by-one references), gaps in rankSpellIds progression (rank N has spell but rank N-1 doesn't — usually a typo). The validator caught a real authoring bug in the makeMage / makeWarrior presets during smoke testing — initial check was comparing prereqRank against the WRONG talent's maxRank (this talent's rather than the prereq's). Fixed in the same commit by hoisting the check into the cross-reference resolution pass where the prereq talent is in hand.
2026-05-09 16:33:45 -07:00
std::printf(" --gen-talents <wtal-base> [name]\n");
std::printf(" Emit .wtal starter: 1 small tree (3 talents in chain) for class warrior\n");
std::printf(" --gen-talents-warrior <wtal-base> [name]\n");
std::printf(" Emit .wtal warrior trees: Arms (4 talents) + Fury (4) + Protection (3) with WSPL spell cross-refs\n");
std::printf(" --gen-talents-mage <wtal-base> [name]\n");
std::printf(" Emit .wtal mage trees: Arcane (3 talents) + Fire (3) + Frost (3) with WSPL Frostbolt/Fireball/Blink refs\n");
std::printf(" --info-wtal <wtal-base> [--json]\n");
std::printf(" Print WTAL trees + per-talent grid position / max rank / prereq chain / rank-1 spellId\n");
std::printf(" --validate-wtal <wtal-base> [--json]\n");
std::printf(" Static checks: tree+talent ids>0+unique, maxRank 1..5, prereq references resolve, no self-prereq\n");
std::printf(" --export-wtal-json <wtal-base> [out.json]\n");
std::printf(" Export binary .wtal to a human-editable JSON sidecar (defaults to <base>.wtal.json)\n");
std::printf(" --import-wtal-json <json-path> [out-base]\n");
std::printf(" Import a .wtal.json sidecar back into binary .wtal (round-trip with --export-wtal-json)\n");
feat(pipeline): add WMS (Wowee Map / Area) catalog format Novel open replacement for Blizzard's Map.dbc + AreaTable.dbc + the AzerothCore-style world_zone SQL tables. The 26th open format added to the editor. Defines two related kinds of locator in one catalog: • Maps — top-level worlds (continents / instances / raids / battlegrounds / arenas) with a friendly name, type, expansion tag, and player-count cap. • Areas — sub-zones within maps with friendly names, parent- area chain, recommended level range, faction- territory marker (alliance / horde / contested / both), exploration XP, and an ambient-sound cross-reference into WSND. The runtime uses Areas for minimap labels, location strings under the player frame, "Discover Sub-zone" XP gains, and ambient-music selection on zone entry. Cross-references with previously-added formats: WMS.area.ambienceSoundId -> WSND.entry.soundId WMS.area.parentAreaId -> WMS.area.areaId (intra-format sub-zone hierarchy) WSPN entries are tied to WMS.area boundaries by world position (no direct ID — the runtime resolves position -> area at lookup time) Format: • magic "WMSX", version 1, little-endian • maps[] (each): mapId / name / shortName / mapType / expansionId / maxPlayers • areas[] (each): areaId / mapId / parentAreaId / name / minLevel..maxLevel / factionGroup / explorationXP / ambienceSoundId Enums: • MapType (5): Continent / Instance / Raid / Battleground / Arena • ExpansionId (5): Classic / Tbc / Wotlk / Cata / Mop • FactionGroup: Both / Alliance / Horde / Contested (PvP-flagging zone) API: WoweeMapsLoader::save / load / exists + WoweeMaps::findMap / findArea. Three preset emitters showcase the catalog shape: • makeStarter — 1 continent + 3 areas with parent chain (Goldshire is a sub-zone of Elwynn Forest) • makeClassic — 2 continents + Deadmines instance + 6 areas (Stormwind/Elwynn/Goldshire/Westfall/ Duskwood/Teldrassil/Deadmines) with WSND ambient-sound refs • makeBgArena — Alterac Valley (40-player BG) + Nagrand Arena (5v5 with maxPlayers=10) CLI added (5 flags, 578 documented total now): --gen-maps / --gen-maps-classic / --gen-maps-bgarena --info-wms / --validate-wms Validator catches: empty map name, unknown mapType / expansion, BG/Arena with maxPlayers=0 (no participant cap), area ids=0 + duplicates, empty area name, maxLevel < minLevel, areas referencing non-existent maps, parentAreaId chains crossing maps (sub-zones must be on the same world), self-parent.
2026-05-09 16:40:00 -07:00
std::printf(" --gen-maps <wms-base> [name]\n");
std::printf(" Emit .wms starter: 1 map (Eastern Kingdoms) + 3 areas (Stormwind / Elwynn / Goldshire) with parent chain\n");
std::printf(" --gen-maps-classic <wms-base> [name]\n");
std::printf(" Emit .wms classic set: 2 continents + Deadmines instance + 6 areas with sub-zone parent chains + WSND refs\n");
std::printf(" --gen-maps-bgarena <wms-base> [name]\n");
std::printf(" Emit .wms PvP maps: Alterac Valley (40-player BG) + Nagrand Arena (5v5)\n");
std::printf(" --info-wms <wms-base> [--json]\n");
std::printf(" Print WMS maps (id / type / expansion / max players) + areas (id / map / parent / level / faction / xp)\n");
std::printf(" --validate-wms <wms-base> [--json]\n");
std::printf(" Static checks: ids unique, areas reference real maps, parent areas exist + same map, BG/Arena needs maxPlayers\n");
std::printf(" --export-wms-json <wms-base> [out.json]\n");
std::printf(" Export binary .wms to a human-editable JSON sidecar (defaults to <base>.wms.json)\n");
std::printf(" --import-wms-json <json-path> [out-base]\n");
std::printf(" Import a .wms.json sidecar back into binary .wms (accepts mapType/expansion/faction int OR name forms)\n");
feat(pipeline): add WCHC (Wowee Character Classes/Races) format Novel open replacement for Blizzard's CharClasses.dbc + CharRaces.dbc + CharStartOutfit.dbc trio. The 27th open format added to the editor — completes the foundational character-creation surface. One file holds three flat arrays: • classes — playable classes (Warrior / Mage / etc.) with power type (mana/rage/focus/energy/runic), base HP+power scaling, faction availability • races — playable races with faction (Alliance/Horde/ Neutral), starting map+zone, default language spell, base stats, racial mount spell • outfits — starting gear loadout per (class, race, gender) triple, listing item IDs and display slots Cross-references with previously-added formats: WCHC.race.startingMapId -> WMS.map.mapId WCHC.race.startingZoneAreaId -> WMS.area.areaId WCHC.race.defaultLanguageSpellId -> WSPL.entry.spellId WCHC.race.mountSpellId -> WSPL.entry.spellId WCHC.outfit.items.itemId -> WIT.entry.itemId The starter preset's outfits use real WIT itemIds (1=Worn Shortsword, 2=Linen Vest, 3=Healing Potion) so the demo content stack is consistent: a freshly created Human Warrior in WCHC starts with WIT items 1/2/3, drops them on death into a WLOT-tracked corpse loot, and can be respawned via WSPN, etc. Format: • magic "WCHC", version 1, little-endian • classes[]: classId / name / icon / powerType / display / baseHP+perLevel / basePower+perLevel / factionAvailability • races[]: raceId / name / icon / factionId / male+female displayId / 5 base stats / startingMap+zone / defaultLanguage+mount spell IDs • outfits[]: classId+raceId+gender + items[] (each: itemId + displaySlot) Enums: • PowerType (6): Mana / Rage / Focus / Energy / RunicPower / Runes • RaceFaction (3): Alliance / Horde / Neutral • Gender: Male / Female • FactionAvailability bitmask: AvailableAlliance, AvailableHorde API: WoweeCharsLoader::save / load / exists + WoweeChars::findClass / findRace / findOutfit (by class+race+gender). CLI added (5 flags, 585 documented total now): --gen-chars / --gen-chars-alliance / --gen-chars-allraces --info-wchc / --validate-wchc Validator catches: ids unique, baseHealth=0 (instant-death character), factionAvailability=0 (no faction can pick), empty names, factionId out of range, outfit references to non-existent class/race ids (cross-format resolution), gender > 1, outfit items with itemId=0, outfit with no items (warning — naked character).
2026-05-09 16:47:04 -07:00
std::printf(" --gen-chars <wchc-base> [name]\n");
std::printf(" Emit .wchc starter: 2 classes (Warrior + Mage) + 2 races (Human + Orc) + 4 outfits with WIT cross-refs\n");
std::printf(" --gen-chars-alliance <wchc-base> [name]\n");
std::printf(" Emit .wchc Alliance set: 4 classes (Warrior/Paladin/Rogue/Mage) + 4 races (Human/Dwarf/NightElf/Gnome)\n");
std::printf(" --gen-chars-allraces <wchc-base> [name]\n");
std::printf(" Emit .wchc all 8 classic races (4 Alliance + 4 Horde) + 9 classes (no DK)\n");
std::printf(" --info-wchc <wchc-base> [--json]\n");
std::printf(" Print WCHC classes (id / power / hp scaling) + races (faction / starting zone) + outfit item lists\n");
std::printf(" --validate-wchc <wchc-base> [--json]\n");
std::printf(" Static checks: class+race ids unique, baseHealth>0, faction availability set, outfit refs resolve\n");
std::printf(" --export-wchc-json <wchc-base> [out.json]\n");
std::printf(" Export binary .wchc to a human-editable JSON sidecar (defaults to <base>.wchc.json)\n");
std::printf(" --import-wchc-json <json-path> [out-base]\n");
std::printf(" Import a .wchc.json sidecar back into binary .wchc (accepts power/faction/gender int OR name forms)\n");
feat(pipeline): add WTKN (Wowee Token catalog) format Novel open replacement for Blizzard's Currency.dbc + CurrencyCategory.dbc + CurrencyTypes.dbc + the AzerothCore- style player_currency SQL tables. The 28th open format added to the editor. Defines secondary currency tokens beyond gold: Honor Points (PvP), Arena Points (rated PvP), Marks of Honor (per battleground), faction reputation tokens, holiday-event currencies. Each token has a balance cap, optional weekly cap (regenerating earnings limit), and a category for grouping in the player's currency tab. Cross-references: WTRN.item.extendedCost -> WTKN.entry.tokenId (vendors can charge in tokens instead of copper — when extendedCost > 0 the runtime looks up the matching token) Format: • magic "WTKN", version 1, little-endian • per token: tokenId / name / description / iconPath / category / maxBalance / weeklyCap / flags Enums: • Category (6): Misc / Pvp / Reputation / Crafting / Seasonal / Holiday • Flags: AccountWide / Tradeable / HiddenUntilEarned / ResetsOnLogout / ConvertsToGold API: WoweeTokenLoader::save / load / exists / findById. Three preset emitters showcase typical token shapes: • makeStarter — 3 tokens (Honor / Marks / Stormwind Guard rep) covering Pvp + Reputation categories • makePvp — full PvP set: Honor (75k) + Arena (5k + weekly 1500) + 6 BG marks of honor for classic + TBC + WotLK battlegrounds • makeSeasonal — 4 holiday tokens (Tricky Treats / Brewfest / Coin of Ancestry / Stranger's Gift) all flagged ResetsOnLogout to make them event-bound CLI added (5 flags, 592 documented total now): --gen-tokens / --gen-tokens-pvp / --gen-tokens-seasonal --info-wtkn / --validate-wtkn Validator catches: tokenId=0 + duplicates, empty name, unknown category, weeklyCap > maxBalance (cap unreachable), ResetsOnLogout + AccountWide combo (incoherent — account state survives logout by definition).
2026-05-09 16:53:11 -07:00
std::printf(" --gen-tokens <wtkn-base> [name]\n");
std::printf(" Emit .wtkn starter: 3 tokens (Honor / Marks / Stormwind Guard) covering Pvp + Reputation categories\n");
std::printf(" --gen-tokens-pvp <wtkn-base> [name]\n");
std::printf(" Emit .wtkn full PvP set: Honor (75k cap) + Arena (5k cap, weekly 1500) + 6 BG marks of honor\n");
std::printf(" --gen-tokens-seasonal <wtkn-base> [name]\n");
std::printf(" Emit .wtkn 4 holiday tokens (Tricky Treats / Brewfest / Coin of Ancestry / Stranger's Gift) — ResetsOnLogout\n");
std::printf(" --info-wtkn <wtkn-base> [--json]\n");
std::printf(" Print WTKN entries (id / category / max balance / weekly cap / flags / name)\n");
std::printf(" --validate-wtkn <wtkn-base> [--json]\n");
std::printf(" Static checks: tokenId>0+unique, name not empty, weeklyCap<=maxBalance, no Resets+AccountWide conflict\n");
std::printf(" --export-wtkn-json <wtkn-base> [out.json]\n");
std::printf(" Export binary .wtkn to a human-editable JSON sidecar (defaults to <base>.wtkn.json)\n");
std::printf(" --import-wtkn-json <json-path> [out-base]\n");
std::printf(" Import a .wtkn.json sidecar back into binary .wtkn (accepts category/flag int OR name forms)\n");
feat(pipeline): add WTRG (Wowee Area Trigger) format Novel open replacement for Blizzard's AreaTrigger.dbc + AreaTriggerTeleport.dbc + the AzerothCore-style areatrigger_template / areatrigger_teleport SQL tables. The 29th open format added to the editor. Defines proximity-based event zones — when a player enters a defined region (box or sphere), the runtime fires the trigger's action: teleport to another map, award exploration XP for a quest, run a server script, gate an instance entrance behind a key item, mark a PvP boundary, or simply display a "Discovered: {area name}" banner. Cross-references with previously-added formats — every trigger field has a real format target: WTRG.entry.mapId / areaId -> WMS.map.mapId / WMS.area.areaId WTRG.actionTarget (Teleport) -> WMS.mapId WTRG.actionTarget (QuestExploration) -> WQT.questId WTRG.requiredQuestId -> WQT.entry.questId WTRG.requiredItemId -> WIT.entry.itemId (key) Format: • magic "WTRG", version 1, little-endian • per trigger: triggerId / mapId / areaId / name / center vec3 / shape / kind / boxDims vec3 / radius / actionTarget / dest vec3 / destOrientation / requiredQuestId / requiredItemId / minLevel Enums: • Shape (2): Box / Sphere • Kind (7): Teleport / QuestExploration / Script / InstanceEntrance / AreaName / CombatStartZone / Waypoint API: WoweeTriggerLoader::save / load / exists / findById. Three preset emitters showcase common trigger shapes: • makeStarter — area-name + quest-exploration with cross-ref to WQT 100 ("Investigate the Camp") • makeDungeon — outdoor area-name + portal-style InstanceEntrance with Deadmines key gate (WIT itemId 5200, matches WLCK.makeDungeon's Boss Vault Seal) + interior exit teleport back outdoors • makeFlightPath — 2 sphere waypoints near flight masters so the runtime can auto-open the flight UI on proximity (matches WTAX starter node positions) CLI added (5 flags, 601 documented total now): --gen-triggers / --gen-triggers-dungeon / --gen-triggers-flightpath --info-wtrg / --validate-wtrg Validator catches: triggerId=0 + duplicates, unknown shape / kind, non-finite center, sphere with radius<=0, box with all-zero half-extents, teleport / instance with dest=(0,0,0) (silently does nothing — usually a typo), QuestExploration without an actionTarget questId.
2026-05-09 17:01:43 -07:00
std::printf(" --gen-triggers <wtrg-base> [name]\n");
std::printf(" Emit .wtrg starter: 2 triggers (Goldshire area-name + bandit-camp quest exploration ref WQT 100)\n");
std::printf(" --gen-triggers-dungeon <wtrg-base> [name]\n");
std::printf(" Emit .wtrg dungeon set: outdoor area-name + Deadmines portal (key-gated) + interior exit teleport\n");
std::printf(" --gen-triggers-flightpath <wtrg-base> [name]\n");
std::printf(" Emit .wtrg flight-master proximity waypoints (Stormwind / Goldshire) for auto-open flight UI\n");
std::printf(" --info-wtrg <wtrg-base> [--json]\n");
std::printf(" Print WTRG triggers (id / map / area / kind / shape + dims / dest + facing / quest+key gates)\n");
std::printf(" --validate-wtrg <wtrg-base> [--json]\n");
std::printf(" Static checks: id>0+unique, finite center, sphere needs radius>0, box needs >=1 nonzero half-extent, quest-explore needs target\n");
std::printf(" --export-wtrg-json <wtrg-base> [out.json]\n");
std::printf(" Export binary .wtrg to a human-editable JSON sidecar (defaults to <base>.wtrg.json)\n");
std::printf(" --import-wtrg-json <json-path> [out-base]\n");
std::printf(" Import a .wtrg.json sidecar back into binary .wtrg (accepts shape/kind int OR name forms)\n");
feat(pipeline): add WTIT (Wowee Title catalog) format Novel open replacement for Blizzard's CharTitles.dbc + the AzerothCore-style character_title SQL table. The 30th open format added to the editor. Defines the player-display titles awarded for completing achievements ("the Versatile"), reaching PvP ranks ("Sergeant Major" / "Stone Guard"), participating in raids ("Champion of the Naaru"), levelling a profession ("Master Locksmith"), or seasonal events ("Brewmaster", "the Hallowed"). Closes a long-standing gap: WACH.entry.titleReward has been a free-form string since batch 116 with no formal catalog to resolve against. WTIT systematizes those strings into a real catalog — the runtime resolves WACH.titleReward to a WTIT entry by name, then displays the titleId in the player title selector. Cross-references: WACH.entry.titleReward (string) ~= WTIT.entry.name (string match — runtime resolves achievement- granted titles by looking up matching WTIT entry by name) Format: • magic "WTIT", version 1, little-endian • per title: titleId / name / nameMale / nameFemale / iconPath / prefix (suffix vs prefix display) / category / sortOrder Enums: • Category (8): Achievement / Pvp / Raid / ClassTitle / Event / Profession / Lore / Custom API: WoweeTitleLoader::save / load / exists + WoweeTitle::findById / findByName. Three preset emitters showcase typical title catalogs: • makeStarter — 4 titles (Versatile / Sergeant / Champion / Hallowed) covering 4 categories • makePvp — 28-title classic Honor System ladder (14 Alliance ranks Private->Grand Marshal + 14 Horde ranks Scout->High Warlord) • makeAchievement — 8 achievement titles including "the Versatile" matching WACH.makeMeta's achievement 250 titleReward + capstone profession titles CLI added (5 flags, 608 documented total now): --gen-titles / --gen-titles-pvp / --gen-titles-achievement --info-wtit / --validate-wtit Validator catches: titleId=0 + duplicates, empty name, unknown category, gender variants set on only one side (causes mixed-gender display when the runtime falls back to canonical for the unset side).
2026-05-09 17:07:56 -07:00
std::printf(" --gen-titles <wtit-base> [name]\n");
std::printf(" Emit .wtit starter: 4 titles covering Achievement / Pvp / Raid / Event categories\n");
std::printf(" --gen-titles-pvp <wtit-base> [name]\n");
std::printf(" Emit .wtit Honor System ladder: 14 Alliance + 14 Horde rank titles (Private..Grand Marshal / Scout..High Warlord)\n");
std::printf(" --gen-titles-achievement <wtit-base> [name]\n");
std::printf(" Emit .wtit achievement titles incl. 'the Versatile' (matches WACH meta-achievement 250 titleReward)\n");
std::printf(" --info-wtit <wtit-base> [--json]\n");
std::printf(" Print WTIT entries (id / sort / prefix vs suffix / category / canonical name)\n");
std::printf(" --validate-wtit <wtit-base> [--json]\n");
std::printf(" Static checks: titleId>0+unique, name not empty, category in 0..7, gender variants paired\n");
std::printf(" --export-wtit-json <wtit-base> [out.json]\n");
std::printf(" Export binary .wtit to a human-editable JSON sidecar (defaults to <base>.wtit.json)\n");
std::printf(" --import-wtit-json <json-path> [out-base]\n");
std::printf(" Import a .wtit.json sidecar back into binary .wtit (accepts category/prefix int OR name forms)\n");
feat(pipeline): add WSEA (Wowee Seasonal Event) format Novel open replacement for Blizzard's GameEvents.dbc + the AzerothCore-style game_event / game_event_creature / game_event_gameobject SQL tables. The 31st open format added to the editor. Calendar-based content: holidays (Hallow's End, Winter's Veil), recurring promotional events (Children's Week, Lunar Festival, Brewfest), one-time anniversaries, and XP-bonus weekends. Each event has a start date, duration, optional recurrence (yearly / monthly / weekly), faction restriction, optional XP bonus, and a reward currency cross-reference into WTKN. Cross-references with previously-added formats: WSEA.entry.tokenIdReward -> WTKN.entry.tokenId (the seasonal currency the event hands out — Tricky Treats during Hallow's End, Brewfest Tokens during Brewfest, etc.) The yearly preset's tokenIdReward values (200/201/202/203) deliberately match WTKN.makeSeasonal's seasonal token ids so the demo content stack already wires together: WSEA yearly events grant WTKN tokens that vendors can charge in via WTRN.item.extendedCost. Format: • magic "WSEA", version 1, little-endian • per event: eventId / name / description / iconPath / announceMessage / startDate (Unix epoch seconds) / duration_seconds / recurrenceDays (0=one-shot, 365=yearly) / holidayKind / factionGroup / bonusXpPercent / tokenIdReward Enums: • HolidayKind (7): Combat / Collection / Racial / Anniversary / Fishing / Cosmetic / WorldEvent • FactionGroup (3): Both / Alliance / Horde API: WoweeEventLoader::save / load / exists / findById. Three preset emitters showcase typical event shapes: • makeStarter — 3 events covering Combat / Fishing / Anniversary kinds • makeYearly — 4 yearly holidays with full WTKN cross-refs (Hallow's End / Brewfest / Lunar Festival / Winter's Veil) • makeBonusWeekends — 3 monthly Fri-Sun bonus tiers (50% / 100% / 200% RAF-style) CLI added (5 flags, 614 documented total now): --gen-events / --gen-events-yearly / --gen-events-weekends --info-wsea / --validate-wsea Validator catches: eventId=0 + duplicates, empty name, unknown holidayKind / factionGroup, duration_seconds=0 (event never runs), duration > recurrence period (events would overlap themselves on next iteration), bonusXpPercent > 200 (very high — verify intentional).
2026-05-09 17:14:46 -07:00
std::printf(" --gen-events <wsea-base> [name]\n");
std::printf(" Emit .wsea starter: 3 events (Brawl Week / Fishing Extravaganza / Anniversary) covering kind categories\n");
std::printf(" --gen-events-yearly <wsea-base> [name]\n");
std::printf(" Emit .wsea 4 yearly holidays (Hallow's End / Brewfest / Lunar Festival / Winter's Veil) with WTKN reward refs\n");
std::printf(" --gen-events-weekends <wsea-base> [name]\n");
std::printf(" Emit .wsea 3 monthly XP-bonus weekends (50%% / 100%% / 200%% RAF tiers)\n");
std::printf(" --info-wsea <wsea-base> [--json]\n");
std::printf(" Print WSEA entries (id / kind / duration / recurrence / xp bonus / token reward / name)\n");
std::printf(" --validate-wsea <wsea-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name not empty, kind in 0..6, duration>0, no overlapping recurrence\n");
std::printf(" --export-wsea-json <wsea-base> [out.json]\n");
std::printf(" Export binary .wsea to a human-editable JSON sidecar (defaults to <base>.wsea.json)\n");
std::printf(" --import-wsea-json <json-path> [out-base]\n");
std::printf(" Import a .wsea.json sidecar back into binary .wsea (accepts kind/faction int OR name forms)\n");
feat(pipeline): add WMOU (Wowee Mount catalog) format Novel open replacement for Blizzard's Mount.dbc + MountCapability.dbc + MountType.dbc + the mount-related subsets of Spell.dbc / Item.dbc. The 32nd open format added to the editor. Defines all summonable steeds: ground mounts, flying mounts, swimming mounts, racial mounts (Tauren Plainsrunner for druids), and class mounts (Warlock dreadsteed, Paladin charger). Each mount has a summon spell, optional teach item, riding skill prerequisite, speed bonus, and faction / race availability mask. Cross-references with previously-added formats: WMOU.entry.summonSpellId -> WSPL.entry.spellId WMOU.entry.itemIdToLearn -> WIT.entry.itemId WMOU.entry.requiredSkillId -> WSKL.entry.skillId (Riding skill ID 762) WCHC.race.mountSpellId ~= WMOU.entry.summonSpellId (loose match by spellId) Format: • magic "WMOU", version 1, little-endian • per mount: mountId / name / description / icon / displayId / summonSpellId / itemIdToLearn / requiredSkillId+Rank / speedPercent / mountKind / factionId / categoryId / raceMask Enums: • Kind (5): Ground / Flying / Swimming / Hybrid / Aquatic • Faction (3): Both / Alliance / Horde • Category (8): Common / Epic / Racial / Event / Achievement / Pvp / Quest / ClassMount API: WoweeMountLoader::save / load / exists / findById. Three preset emitters showcase typical mount catalogs: • makeStarter — 3 mounts (ground horse + epic flying gryphon + aquatic riding turtle) • makeRacial — 6 racial mounts (4 Alliance: Pinto / Ram / Frostsaber / Mechanostrider; 2 Horde: Dire Wolf / Skeletal Horse) with raceMask gating per WCHC race bit positions • makeFlying — 4 flying mounts spanning Common (60%) -> Epic (100%) -> Achievement (280%) -> Pvp (310%) speed tiers CLI added (5 flags, 621 documented total now): --gen-mounts / --gen-mounts-racial / --gen-mounts-flying --info-wmou / --validate-wmou Validator catches: mountId=0 + duplicates, empty name, summonSpellId=0 (mount cannot be cast), unknown enum values, speedPercent=0 (no speed bonus), flying mount with requiredSkillRank<150 (player can't fly), Racial category without raceMask (any race could use — usually a typo).
2026-05-09 17:23:00 -07:00
std::printf(" --gen-mounts <wmou-base> [name]\n");
std::printf(" Emit .wmou starter: 3 mounts (Brown Horse / Swift Gryphon / Riding Turtle) covering ground/flying/aquatic\n");
std::printf(" --gen-mounts-racial <wmou-base> [name]\n");
std::printf(" Emit .wmou 6 racial mounts (4 Alliance + 2 Horde) with raceMask gating\n");
std::printf(" --gen-mounts-flying <wmou-base> [name]\n");
std::printf(" Emit .wmou 4 flying mounts spanning common/epic/achievement/pvp tiers (60%%/100%%/280%%/310%% speed)\n");
std::printf(" --info-wmou <wmou-base> [--json]\n");
std::printf(" Print WMOU entries (id / kind / speed / required riding rank / faction / category / name)\n");
std::printf(" --validate-wmou <wmou-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name not empty, summonSpellId>0, flying needs rank>=150, racial needs raceMask\n");
std::printf(" --export-wmou-json <wmou-base> [out.json]\n");
std::printf(" Export binary .wmou to a human-editable JSON sidecar (defaults to <base>.wmou.json)\n");
std::printf(" --import-wmou-json <json-path> [out-base]\n");
std::printf(" Import a .wmou.json sidecar back into binary .wmou (accepts kind/faction/category int OR name forms)\n");
feat(pipeline): add WBGD (Wowee Battleground Definition) format Novel open replacement for Blizzard's BattlemasterList.dbc + PvpDifficulty.dbc + the AzerothCore-style battleground_template SQL tables. The 33rd open format added to the editor. Defines per-BG gameplay rules: player count brackets, score-to-win, time limit, objective type (annihilation / capture flag / control nodes / king of hill / resource race / carry object), per-team start positions, respawn timer, and the WTKN currency token awarded on win. Cross-references with previously-added formats: WBGD.entry.mapId -> WMS.map.mapId (where mapType=Battleground) WBGD.entry.markTokenId -> WTKN.entry.tokenId (Mark of Honor for that BG) The classic preset's markTokenId fields (102/103/104) deliberately match WTKN.makePvp's "Mark of Honor: Warsong Gulch / Arathi Basin / Alterac Valley" token ids — so the demo content stack already wires together: WBGD.scoreToWin reached → grant WTKN mark → which a vendor in WTRN can charge in via item.extendedCost. Format: • magic "WBGD", version 1, little-endian • per BG: battlegroundId / mapId / name / description / objectiveKind / min+maxPlayersPerSide / minLevel..maxLevel / scoreToWin / timeLimitSeconds / bracketSize / allianceStart vec3 + facing / hordeStart vec3 + facing / respawnTimeSeconds / markTokenId Enums: • ObjectiveKind (6): Annihilation / CaptureFlag / ControlNodes / KingOfHill / ResourceRace / CarryObject API: WoweeBattlegroundLoader::save / load / exists / findById. Three preset emitters showcase typical BG shapes: • makeStarter — 1 king-of-hill BG (10v10, 3-cap to win) • makeClassic — 3 classic BGs with authentic player counts, level brackets, scoring, and WTKN cross-refs (Warsong Gulch CTF / Arathi Basin nodes / Alterac Valley resource race) • makeArena — 3 arena formats (Nagrand 2v2 / Blade's Edge 3v3 / Lordaeron 5v5) — annihilation objective + no respawn + 25 min cap CLI added (5 flags, 628 documented total now): --gen-bg / --gen-bg-classic / --gen-bg-arena --info-wbgd / --validate-wbgd Validator catches: bgId=0 + duplicates, empty name, unknown objectiveKind, player count=0, min>max counts/levels, scoreToWin=0 (no win condition), non-annihilation BG with respawnTimeSeconds=0 (losing side cannot recover).
2026-05-09 17:30:59 -07:00
std::printf(" --gen-bg <wbgd-base> [name]\n");
std::printf(" Emit .wbgd starter: 1 king-of-hill BG (10v10, 3-cap to win, 30 min limit)\n");
std::printf(" --gen-bg-classic <wbgd-base> [name]\n");
std::printf(" Emit .wbgd 3 classic BGs: Warsong Gulch (CTF) + Arathi Basin (nodes) + Alterac Valley (resource race)\n");
std::printf(" --gen-bg-arena <wbgd-base> [name]\n");
std::printf(" Emit .wbgd 3 arena formats: Nagrand 2v2 + Blade's Edge 3v3 + Lordaeron 5v5 (annihilation)\n");
std::printf(" --info-wbgd <wbgd-base> [--json]\n");
std::printf(" Print WBGD entries (id / map / objective / player counts / level range / score / token reward)\n");
std::printf(" --validate-wbgd <wbgd-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name not empty, player counts>0+min<=max, level range valid, scoreToWin>0\n");
std::printf(" --export-wbgd-json <wbgd-base> [out.json]\n");
std::printf(" Export binary .wbgd to a human-editable JSON sidecar (defaults to <base>.wbgd.json)\n");
std::printf(" --import-wbgd-json <json-path> [out-base]\n");
std::printf(" Import a .wbgd.json sidecar back into binary .wbgd (accepts objectiveKind int OR name string)\n");
feat(pipeline): add WMAL (Wowee Mail Template) format Novel open replacement for AzerothCore-style mail_loot_template SQL + the in-game mail subset of the inventory + currency systems. The 34th open format added to the editor. Defines templated mail messages with currency + item attachments. Triggered by quest reward delivery (overflow when bag is full), auction house bid wins / sales, achievement reward attachments, GM correspondence, holiday event mailings (Brewfest samples, Hallow's End candy), and returned-mail-on-rejection. Cross-references with previously-added formats: WMAL.entry.senderNpcId -> WCRT.entry.creatureId WMAL.entry.attachments.itemId -> WIT.entry.itemId Format: • magic "WMAL", version 1, little-endian • per template: templateId / senderNpcId / subject / body / senderName / moneyCopperAttached / categoryId / cod / returnable / expiryDays / attachments[] (each: itemId + quantity) Enums: • Category (8): QuestReward / Auction / GmCorrespondence / AchievementReward / EventMailing / Raffle / ScriptDelivery / ReturnedMail API: WoweeMailLoader::save / load / exists / findById. Three preset emitters showcase typical mail templates: • makeStarter — 3 templates (quest overflow / auction won / GM gift) covering the 3 most common categories • makeHoliday — 4 holiday samples that cross-reference the WTKN seasonal token IDs (200=Tricky Treats, 201=Brewfest, 202=Coin of Ancestry, 203=Stranger's Gift) so the demo content stack ships a full holiday onboarding experience • makeAuction — 5-template auction-house family (outbid / won / sold / expired / cancelled) — runtime fills in actual bid amounts / sold items at send time CLI added (5 flags, 635 documented total now): --gen-mail / --gen-mail-holiday / --gen-mail-auction --info-wmal / --validate-wmal Validator catches: templateId=0 + duplicates, empty subject, neither senderNpcId nor senderName set (no displayable sender), unknown category, expiryDays=0 (mail expires immediately), cod=1 with no money attached (free COD), empty mail in categories where the runtime doesn't fill in content (skips Auction / GmCorrespondence / ReturnedMail where empty templates are intentional). Two bugs caught + fixed during smoke-test on the auction preset: • print formatting glued the `0` from senderNpcId after the senderName when no NPC was set (rendered as "Postmaster0" instead of "Postmaster") — fixed with an explicit if/else split • validator's "no money + no items" warning was too aggressive for the Auction category, where templates are intentionally informational and the runtime fills in the real values — added Auction + ReturnedMail to the skip list
2026-05-09 17:41:03 -07:00
std::printf(" --gen-mail <wmal-base> [name]\n");
std::printf(" Emit .wmal starter: 3 templates (quest overflow / auction won / GM gift) covering main mail categories\n");
std::printf(" --gen-mail-holiday <wmal-base> [name]\n");
std::printf(" Emit .wmal 4 holiday samples (Tricky Treats / Brewfest / Lunar / Winter's Veil) with WTKN cross-refs\n");
std::printf(" --gen-mail-auction <wmal-base> [name]\n");
std::printf(" Emit .wmal 5-template auction-house family (outbid / won / sold / expired / cancelled)\n");
std::printf(" --info-wmal <wmal-base> [--json]\n");
std::printf(" Print WMAL templates (id / category / sender / subject + body / money + items / cod / expiry)\n");
std::printf(" --validate-wmal <wmal-base> [--json]\n");
std::printf(" Static checks: id>0+unique, subject not empty, sender set, attachments valid, no money+no items info-only\n");
std::printf(" --export-wmal-json <wmal-base> [out.json]\n");
std::printf(" Export binary .wmal to a human-editable JSON sidecar (defaults to <base>.wmal.json)\n");
std::printf(" --import-wmal-json <json-path> [out-base]\n");
std::printf(" Import a .wmal.json sidecar back into binary .wmal (accepts category int OR name string)\n");
feat(pipeline): add WGEM (Wowee Gem / Enchantment) format Novel open replacement for Blizzard's ItemEnchantment.dbc + GemProperties.dbc + SpellItemEnchantment.dbc. The 35th open format added to the editor. Defines two related kinds of item enhancement in one catalog: • Gems — socketable jewelry pieces with color (red / blue / yellow / meta) that fit into gear sockets, granting stats or triggering passive spells when socketed • Enchantments — persistent buffs applied to weapon / armor pieces, either by an enchanter spell or by an item proc (Mongoose, Crusader, Berserking) Cross-references with previously-added formats: WGEM.gem.itemIdToInsert -> WIT.entry.itemId WGEM.gem.spellId -> WSPL.entry.spellId WGEM.enchantment.spellId -> WSPL.entry.spellId Format: • magic "WGEM", version 1, little-endian • gems[]: gemId / itemIdToInsert / name / color / statType + statValue / requiredItemQuality / spellId • enchantments[]: enchantId / name / description / iconPath / enchantSlot / statType + statValue / spellId / durationSeconds / chargeCount Enums: • Color (8): Meta / Red / Yellow / Blue / Purple / Green / Orange / Prismatic • EnchantSlot (5): Permanent / Temporary / SocketColor / Ring / Cloak API: WoweeGemLoader::save / load / exists + WoweeGem::findGem / findEnchant. Three preset emitters showcase common shapes: • makeStarter — 3 gems (one per primary color) + 2 enchantments (proc + stat) • makeGemSet — 6-gem full color palette covering primary + secondary combinations • makeEnchants — 5 enchant variants spanning slots (Mongoose / Deadly Poison / stats ring / cloak / Berserking) CLI added (5 flags, 642 documented total now): --gen-gems / --gen-gems-set / --gen-gems-enchants --info-wgem / --validate-wgem Validator catches: ids=0 + duplicates, empty name, color / slot out of range, stat-only entries with statValue=0 (gem provides nothing), chargeCount > 0 on non-Temporary enchant slots (charges silently ignored at runtime). The validator caught a real preset issue on first run — the proc enchants (Mongoose / Deadly Poison / Berserking) had spellId=0 and statValue=0, providing nothing. Fixed by adding placeholder spellIds in the 28000-29000 range, with a comment noting they resolve to real WSPL proc spells when the spell catalog is extended.
2026-05-09 18:01:48 -07:00
std::printf(" --gen-gems <wgem-base> [name]\n");
std::printf(" Emit .wgem starter: 3 gems (red/yellow/blue) + 2 enchantments (Crusader proc + Greater Stats)\n");
std::printf(" --gen-gems-set <wgem-base> [name]\n");
std::printf(" Emit .wgem 6-gem full color set (3 primary + 3 secondary purple/green/orange)\n");
std::printf(" --gen-gems-enchants <wgem-base> [name]\n");
std::printf(" Emit .wgem 5 enchant variants (Mongoose / Deadly Poison / stats ring / cloak / Berserking proc)\n");
std::printf(" --info-wgem <wgem-base> [--json]\n");
std::printf(" Print WGEM gems (id / color / stat / item) + enchantments (id / slot / stat / duration / charges)\n");
std::printf(" --validate-wgem <wgem-base> [--json]\n");
std::printf(" Static checks: ids>0+unique, name not empty, color/slot in range, stat-only entries need non-zero value\n");
std::printf(" --export-wgem-json <wgem-base> [out.json]\n");
std::printf(" Export binary .wgem to a human-editable JSON sidecar (defaults to <base>.wgem.json)\n");
std::printf(" --import-wgem-json <json-path> [out-base]\n");
std::printf(" Import a .wgem.json sidecar back into binary .wgem (accepts color/slot int OR name strings)\n");
feat(pipeline): add WGLD (Wowee Guild) catalog format Novel open replacement for AzerothCore-style guild + guild_member + guild_rank + guild_bank_tab + guild_perk SQL tables. The 36th open format added to the editor. Each guild entry holds the complete social-organization state: header (name, leader, faction, MOTD, info, creation date, level + experience, bank money, packed emblem), rank ladder with permissions bitmask + daily withdraw caps, member roster with rank + join date + public/officer notes, bank tabs with per-tab and per-rank deposit / withdraw / view permission masks, and purchased guild perks referencing WSPL spell IDs. Cross-references with previously-added formats: WGLD.entry.factionId ~ WCHC.race.factionId (guilds are faction-locked) WGLD.entry.perks.spellId -> WSPL.entry.spellId Format: • magic "WGLD", version 1, little-endian • per guild: header (12 scalar fields + 4 strings) + ranks[] + members[] + bankTabs[] + perks[] • per rank: rankIndex / name / permissionsMask / moneyPerDayCopper • per member: characterName / rankIndex / joinedDate / publicNote / officerNote • per bankTab: tabIndex / name / iconPath / deposit+withdraw+view permission masks • per perk: perkId / name / spellId / requiredGuildLevel Enums: • Faction (2): Alliance / Horde • RankPermissionFlags (14): GuildChat / OfficerChat / Invite / Remove / Promote / Demote / SetMotd / EditPublicNote / EditOfficerNote / ViewBank / Deposit / Withdraw / Disband / RepairFromBank API: WoweeGuildLoader::save / load / exists / findById + shared addDefaultRanks helper used by both starter and faction-pair presets. Three preset emitters: • makeStarter — 1 small guild, default 5-rank ladder (GM/Officer/Veteran/Member/Initiate), 3 members borrowing names from WCRT merchants for cross-format consistency • makeFull — 1 fleshed-out guild: 6 ranks (with Recruit added) + 8 members + 4 bank tabs (officer-only withdraw on tabs 3+4) + 3 perks referencing WSPL spell IDs (Heroic Strike / Battle Shout / Thunder Clap as placeholder perk procs) • makeFactionPair — 2 parallel guilds, one Alliance + one Horde, with identical rank structures CLI added (5 flags, 649 documented total now): --gen-guilds / --gen-guilds-full / --gen-guilds-pair --info-wgld / --validate-wgld Validator catches: guildId=0 + duplicates, empty name / leaderName, factionId out of range, no ranks (members can't exist without a rank ladder), member.rankIndex exceeding the highest defined rank (intra-format cross-reference resolution), duplicate bank tabIndices, perks with spellId=0 (perk does nothing).
2026-05-09 18:10:45 -07:00
std::printf(" --gen-guilds <wgld-base> [name]\n");
std::printf(" Emit .wgld starter: 1 guild (Sentinels of Dawn) with default 5-rank ladder + 3 members\n");
std::printf(" --gen-guilds-full <wgld-base> [name]\n");
std::printf(" Emit .wgld fleshed-out guild: 6 ranks + 8 members + 4 bank tabs (officer-only on tabs 3+4) + 3 perks\n");
std::printf(" --gen-guilds-pair <wgld-base> [name]\n");
std::printf(" Emit .wgld 2 guilds: 1 Alliance (Light's Vanguard) + 1 Horde (Bloodfang Warband) with parallel ranks\n");
std::printf(" --info-wgld <wgld-base> [--json]\n");
std::printf(" Print WGLD entries (id / faction / level / leader + motd / rank+member+tab+perk counts)\n");
std::printf(" --validate-wgld <wgld-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name+leader not empty, faction 0..1, members reference valid ranks, unique tab indices\n");
std::printf(" --export-wgld-json <wgld-base> [out.json]\n");
std::printf(" Export binary .wgld to a human-editable JSON sidecar (defaults to <base>.wgld.json)\n");
std::printf(" --import-wgld-json <json-path> [out-base]\n");
std::printf(" Import a .wgld.json sidecar back into binary .wgld (accepts faction int OR name string)\n");
feat(pipeline): add WPCD (Wowee Player Condition) format Novel open replacement for Blizzard's PlayerCondition.dbc + the AzerothCore-style condition_template SQL tables. The 37th open format added to the editor. Defines reusable boolean conditions that other formats can reference for gating: "player has quest X completed", "player level >= N", "player class is mage", "player has item Y in inventory", "WSEA event Z is active". Conditions can be grouped and combined with AND/OR aggregators on a per-group basis: a quest-giver gossip option that says "show only to level 60 alliance mages who completed quest 1234" composes 4 conditions sharing the same groupId with AND aggregation. The runtime walks each group, applies the group's aggregator, and returns the boolean result to the caller. Cross-references with previously-added formats — the targetId field has a polymorphic interpretation by kind: WPCD.targetId (kind=QuestCompleted/Active) -> WQT.questId WPCD.targetId (kind=HasItem) -> WIT.itemId WPCD.targetId (kind=HasSpell) -> WSPL.spellId WPCD.targetId (kind=HasAchievement) -> WACH.achievementId WPCD.targetId (kind=AreaId) -> WMS.areaId WPCD.targetId (kind=EventActive) -> WSEA.eventId WPCD.targetId (kind=HasTitle) -> WTIT.titleId WPCD.targetId (kind=FactionRep) -> WFAC.factionId WPCD.targetId (kind=Class/Race) -> WCHC class/race id Future format extensions can reference WPCD.conditionId in their own gating fields — WTRG triggers gated by player state, WGSP options visible only when conditions are met, WMOU summon spells condition-gated by quest progress, etc. Format: • magic "WPCD", version 1, little-endian • per condition: conditionId / groupId (0 = standalone) / name / description / kind / aggregator / negated / targetId / minValue / maxValue Enums: • Kind (17): AlwaysTrue / AlwaysFalse / QuestCompleted / QuestActive / HasItem / HasSpell / MinLevel / MaxLevel / ClassMatch / RaceMatch / FactionRep / HasAchievement / TeamSize / GuildLevel / EventActive / AreaId / HasTitle • Aggregator (2): And / Or API: WoweeConditionLoader::save / load / exists / findById. Three preset emitters showcase typical usage: • makeStarter — 4 standalone conditions covering the most common kinds (quest-done / has-item / min-level / class) • makeGated — 5 conditions in 2 groups demonstrating AND-aggregation (alliance + mage + lvl 60) and OR-aggregation (did quest 1 OR quest 100) • makeEvent — 3 event-gated conditions cross-referencing WSEA event IDs (Hallow's End / Brewfest / Winter's Veil) CLI added (5 flags, 656 documented total now): --gen-conditions / --gen-conditions-gated / --gen-conditions-event --info-wpcd / --validate-wpcd Validator catches: conditionId=0 + duplicates, kind / aggregator out of range, kinds requiring targetId having target=0 (skips AlwaysTrue/False, MinLevel/MaxLevel, TeamSize, GuildLevel which use min/max instead), TeamSize with min > max.
2026-05-09 18:18:27 -07:00
std::printf(" --gen-conditions <wpcd-base> [name]\n");
std::printf(" Emit .wpcd starter: 4 conditions covering quest-done / has-item / min-level / class kinds\n");
std::printf(" --gen-conditions-gated <wpcd-base> [name]\n");
std::printf(" Emit .wpcd 5 conditions in 2 groups (group 100 AND alliance+mage+lvl60; group 200 OR did-quest-1/100)\n");
std::printf(" --gen-conditions-event <wpcd-base> [name]\n");
std::printf(" Emit .wpcd 3 event-gated conditions referencing WSEA event IDs (Hallow's End / Brewfest / Winter's Veil)\n");
std::printf(" --info-wpcd <wpcd-base> [--json]\n");
std::printf(" Print WPCD entries (id / group / kind / aggregator / negated / target / min..max value / name)\n");
std::printf(" --validate-wpcd <wpcd-base> [--json]\n");
std::printf(" Static checks: id>0+unique, kind in 0..16, aggregator 0..1, kinds that need targetId have non-zero target\n");
std::printf(" --export-wpcd-json <wpcd-base> [out.json]\n");
std::printf(" Export binary .wpcd to a human-editable JSON sidecar (defaults to <base>.wpcd.json)\n");
std::printf(" --import-wpcd-json <json-path> [out-base]\n");
std::printf(" Import a .wpcd.json sidecar back into binary .wpcd (accepts kind/aggregator int OR name forms)\n");
feat(pipeline): add WPET (Wowee Pet System) catalog Novel open replacement for AzerothCore-style pet_template + pet_levelstats SQL + the pet-related subsets of CreatureFamily.dbc + SpellFamilyName.dbc. The 38th open format added to the editor. Defines two related kinds of player-controlled NPCs in one catalog: • Pet families — hunter pet families (Wolf / Cat / Bear / Boar / Raptor / Spider / etc.) with per-family ability sets, base stat multipliers, and diet preferences • Warlock minions — Imp / Voidwalker / Succubus / Felhunter / Felguard, each with their own summon spell, creature template, and ability list Cross-references with previously-added formats: WPET.family.familyId -> WCRT.entry.familyId (matches creature family) WPET.family.abilities.spellId -> WSPL.entry.spellId WPET.minion.summonSpellId -> WSPL.entry.spellId WPET.minion.creatureId -> WCRT.entry.creatureId (used for stat scaling) WPET.minion.abilities.spellId -> WSPL.entry.spellId The starter preset's familyIds (1=Wolf, 2=Cat) match WCRT::FamilyId enum values, so a hunter taming a wolf via WCRT links straight through to WPET ability sets. Format: • magic "WPET", version 1, little-endian • families[]: familyId / name / description / icon / petType / baseAttackSpeed / damageMultiplier / armorMultiplier / dietMask / abilities[] • minions[]: minionId / name / summonSpellId / creatureId / abilities[] (each: spellId / rank / autocastDefault) Enums: • PetType (3): Cunning / Ferocity / Tenacity (WotLK+ talent tree categorization) • DietFlags: Meat / Fish / Bread / Cheese / Fruit / Fungus API: WoweePetLoader::save / load / exists + WoweePet::findFamily / findMinion + dietMaskName helper that decodes a dietMask into a "meat+fish" string. Three preset emitters showcase typical pet catalogs: • makeStarter — 2 hunter families (Wolf + Cat) with full 3-ability sets + 1 warlock Imp • makeHunter — 8 classic hunter families covering all 3 petType categories with appropriate diet masks • makeWarlock — 5 warlock minions each with summon spell ID and creatureId pointing into WCRT CLI added (5 flags, 663 documented total now): --gen-pets / --gen-pets-hunter / --gen-pets-warlock --info-wpet / --validate-wpet Validator catches: ids=0 + duplicates, empty name, petType out of range, baseAttackSpeed<=0 (would divide by zero in DPS calc), dietMask=0 (pet cannot be fed for happiness), minion missing summonSpellId / creatureId.
2026-05-09 18:27:02 -07:00
std::printf(" --gen-pets <wpet-base> [name]\n");
std::printf(" Emit .wpet starter: 2 hunter families (Wolf + Cat) + 1 warlock minion (Imp) with WSPL ability refs\n");
std::printf(" --gen-pets-hunter <wpet-base> [name]\n");
std::printf(" Emit .wpet 8 classic hunter families (Wolf/Cat/Bear/Boar/Raptor/Hyena/Spider/Crab) with diet+petType\n");
std::printf(" --gen-pets-warlock <wpet-base> [name]\n");
std::printf(" Emit .wpet 5 warlock minions (Imp/Voidwalker/Succubus/Felhunter/Felguard) with summon spell + WCRT ref\n");
std::printf(" --info-wpet <wpet-base> [--json]\n");
std::printf(" Print WPET families (id / petType / atkSpd / dmg+arm mult / diet) + minions (id / summon / creatureId)\n");
std::printf(" --validate-wpet <wpet-base> [--json]\n");
std::printf(" Static checks: ids>0+unique, name not empty, petType 0..2, atkSpeed>0, minion needs summon+creatureId\n");
std::printf(" --export-wpet-json <wpet-base> [out.json]\n");
std::printf(" Export binary .wpet to a human-editable JSON sidecar (defaults to <base>.wpet.json)\n");
std::printf(" --import-wpet-json <json-path> [out-base]\n");
std::printf(" Import a .wpet.json sidecar back into binary .wpet (accepts petType int OR name string)\n");
feat(pipeline): add WAUC (Wowee Auction House) catalog Novel open replacement for Blizzard's AuctionHouse.dbc + the AzerothCore-style auctionhouse / auctionhousebot SQL tables. The 39th open format added to the editor. Defines per-house rules for the auction system: faction access, deposit rate (basis points of buyout price), house cut on successful sale, three listing duration tiers with per-tier deposit multipliers, disallowed item-class bitmask, and the auctioneer NPC. Cross-references with previously-added formats: WAUC.entry.auctioneerNpcId -> WCRT.entry.creatureId (Auctioneer-flagged NPC) WAUC.entry.disallowedClassMask bitmask of WIT.Class values that may not be auctioned at this house The faction-pair preset captures the canonical asymmetry: faction houses charge 5% on a successful sale while neutral houses charge 15% — the cross-faction tax that makes neutral AHs profitable for goblins. Format: • magic "WAUC", version 1, little-endian • per house: houseId / auctioneerNpcId / name / factionAccess / baseDepositRateBp / houseCutRateBp / maxBidCopper / 3 duration hours + 3 deposit multipliers / disallowedClassMask Enums: • FactionAccess (4): Alliance / Horde / Neutral / Both • Rates use basis points: 10000 = 100% API: WoweeAuctionLoader::save / load / exists / findById. Three preset emitters showcase typical auction setups: • makeStarter — 1 neutral house with default 12h / 24h / 48h tiers • makeFactionPair — 3 houses (Stormwind / Orgrimmar / Booty Bay) with auctioneer NPC IDs + canonical faction-vs-neutral cut rates • makeRestricted — 1 house disallowing Containers (1) + Quest items (12) + Keys (13) with tighter durations + 1000g bid cap CLI added (5 flags, 670 documented total now): --gen-auction / --gen-auction-pair / --gen-auction-restricted --info-wauc / --validate-wauc Validator catches: houseId=0 + duplicates, empty name, factionAccess out of range, duration tier=0, durations not in short<=medium<=long order, houseCutRateBp >= 100% (seller loses money on a sale), warns on >50% cut.
2026-05-09 18:34:54 -07:00
std::printf(" --gen-auction <wauc-base> [name]\n");
std::printf(" Emit .wauc starter: 1 neutral house with stock 12h/24h/48h tiers and 5%% house cut\n");
std::printf(" --gen-auction-pair <wauc-base> [name]\n");
std::printf(" Emit .wauc 3 houses (Stormwind / Orgrimmar / Booty Bay) — faction 5%% cut vs neutral 15%% asymmetry\n");
std::printf(" --gen-auction-restricted <wauc-base> [name]\n");
std::printf(" Emit .wauc 1 house with disallowedClassMask (no Containers/Quest/Keys) + tighter durations + bid cap\n");
std::printf(" --info-wauc <wauc-base> [--json]\n");
std::printf(" Print WAUC houses (id / faction / deposit + cut percent / 3 duration tiers / disallow mask / npc)\n");
std::printf(" --validate-wauc <wauc-base> [--json]\n");
std::printf(" Static checks: id>0+unique, faction 0..3, durations short<=medium<=long, cut < 100%%\n");
std::printf(" --export-wauc-json <wauc-base> [out.json]\n");
std::printf(" Export binary .wauc to a human-editable JSON sidecar (defaults to <base>.wauc.json)\n");
std::printf(" --import-wauc-json <json-path> [out-base]\n");
std::printf(" Import a .wauc.json sidecar back into binary .wauc (accepts factionAccess int OR name string)\n");
feat(pipeline): add WCHN (Wowee Chat Channel) catalog Novel open replacement for Blizzard's ChatChannels.dbc + the AzerothCore-style chat_channel SQL tables. The 40th open format added to the editor. Defines the world chat channel system: General, Trade, LookingForGroup, GuildRecruitment, LocalDefense, plus per-zone area channels and custom user-created channels. Each channel has access rules (faction / level), join behavior (auto vs opt-in), broadcast policy (announce / moderated), and optional area / map gating that auto-joins or auto-leaves the channel as the player moves. Cross-references with previously-added formats: WCHN.entry.areaIdGate -> WMS.area.areaId (channel auto-attaches in this area) WCHN.entry.mapIdGate -> WMS.map.mapId (channel auto-attaches on this map) Format: • magic "WCHN", version 1, little-endian • per channel: channelId / name / description / channelType / factionAccess / autoJoin / announce / moderated / minLevel / areaIdGate / mapIdGate Enums: • ChannelType (10): AreaLocal / Zone / Continent / World / Trade / LookingForGroup / GuildRecruit / LocalDefense / Custom / Pvp • FactionAccess (3): Alliance / Horde / Both API: WoweeChannelLoader::save / load / exists / findById. Three preset emitters: • makeStarter — 4 stock channels (General Zone + Trade + LFG + GuildRecruit) with default autoJoin policies • makeCity — 5 city-specific channels (3 Stormwind + 2 Orgrimmar) with mapId / areaId gates so they auto-attach on entry • makeModerated — 3 moderated / restricted channels (LocalDefense level 10+, WorldDefense moderated, RaidCoordination level 60+) CLI added (5 flags, 677 documented total now): --gen-channels / --gen-channels-city / --gen-channels-moderated --info-wchn / --validate-wchn Validator catches: channelId=0 + duplicates, empty name, unknown channelType / factionAccess, world / continent channel with area or map gate (gate is silently ignored at runtime — usually a typo), minLevel=0 (no level gate at all).
2026-05-09 18:43:26 -07:00
std::printf(" --gen-channels <wchn-base> [name]\n");
std::printf(" Emit .wchn starter: 4 stock channels (General / Trade / LFG / GuildRecruit) with autoJoin defaults\n");
std::printf(" --gen-channels-city <wchn-base> [name]\n");
std::printf(" Emit .wchn 5 city channels (Stormwind General/Trade/LFG + Orgrimmar General/Trade) with map+area gates\n");
std::printf(" --gen-channels-moderated <wchn-base> [name]\n");
std::printf(" Emit .wchn 3 moderated channels (LocalDefense / WorldDefense moderated / RaidCoordination level 60+)\n");
std::printf(" --info-wchn <wchn-base> [--json]\n");
std::printf(" Print WCHN entries (id / type / faction / autoJoin/announce/moderated / level / map+area gates / name)\n");
std::printf(" --validate-wchn <wchn-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name not empty, type 0..9, faction 0..2, world+area-gate combo warning\n");
std::printf(" --export-wchn-json <wchn-base> [out.json]\n");
std::printf(" Export binary .wchn to a human-editable JSON sidecar (defaults to <base>.wchn.json)\n");
std::printf(" --import-wchn-json <json-path> [out-base]\n");
std::printf(" Import a .wchn.json sidecar back into binary .wchn (accepts channelType/factionAccess int OR name string)\n");
std::printf(" --gen-cinematics <wcms-base> [name]\n");
std::printf(" Emit .wcms starter: 3 cinematics (pre-rendered intro / quest cutscene / login splash)\n");
std::printf(" --gen-cinematics-intros <wcms-base> [name]\n");
std::printf(" Emit .wcms 4 class intros (Warrior / Hunter / Rogue / Mage) triggered on first ClassStart\n");
std::printf(" --gen-cinematics-quests <wcms-base> [name]\n");
std::printf(" Emit .wcms 3 quest-bound cinematics referencing WQT questIds 1 / 100 / 102 (start / start / end)\n");
std::printf(" --info-wcms <wcms-base> [--json]\n");
std::printf(" Print WCMS entries (id / kind / trigger / target / duration / skippable / soundtrackId / name)\n");
std::printf(" --validate-wcms <wcms-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name+mediaPath not empty, kind 0..4, trigger 0..8, target required for non-Manual triggers\n");
std::printf(" --export-wcms-json <wcms-base> [out.json]\n");
std::printf(" Export binary .wcms to a human-editable JSON sidecar (defaults to <base>.wcms.json)\n");
std::printf(" --import-wcms-json <json-path> [out-base]\n");
std::printf(" Import a .wcms.json sidecar back into binary .wcms (accepts kind/triggerKind int OR name string)\n");
std::printf(" --gen-glyphs <wgly-base> [name]\n");
std::printf(" Emit .wgly starter: 3 glyphs (1 warrior + 1 mage + 1 rogue) showing per-class Major slot picks\n");
std::printf(" --gen-glyphs-warrior <wgly-base> [name]\n");
std::printf(" Emit .wgly 6 warrior glyphs (3 major + 3 minor) demonstrating per-class allotment\n");
std::printf(" --gen-glyphs-universal <wgly-base> [name]\n");
std::printf(" Emit .wgly 4 universal glyphs (classMask=All) covering hearthstone/levitate/mounting/salvation\n");
std::printf(" --info-wgly <wgly-base> [--json]\n");
std::printf(" Print WGLY entries (id / type / spellId / itemId / classMask / requiredLevel / name)\n");
std::printf(" --validate-wgly <wgly-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name+spellId not empty, type 0..2, classMask>0, level<25 warning, missing-itemId warning\n");
std::printf(" --export-wgly-json <wgly-base> [out.json]\n");
std::printf(" Export binary .wgly to a human-editable JSON sidecar (defaults to <base>.wgly.json)\n");
std::printf(" --import-wgly-json <json-path> [out-base]\n");
std::printf(" Import a .wgly.json sidecar back into binary .wgly (accepts glyphType int OR name string)\n");
std::printf(" --gen-vehicles <wvhc-base> [name]\n");
std::printf(" Emit .wvhc starter: 3 vehicles (chopper / wind rider / salvaged tank) covering ground+air+siege roles\n");
std::printf(" --gen-vehicles-siege <wvhc-base> [name]\n");
std::printf(" Emit .wvhc 3 siege weapons (Demolisher 2-seat / Glaive Thrower / Salvaged Cannon) with control spellIds\n");
std::printf(" --gen-vehicles-flying <wvhc-base> [name]\n");
std::printf(" Emit .wvhc 3 flying mounts (Wind Rider / Storm Gryphon / Twilight Drake) cross-ref WMNT flightCapabilityIds\n");
std::printf(" --info-wvhc <wvhc-base> [--json]\n");
std::printf(" Print WVHC entries with seat layout (id / creature / kind / movement / power / seat count / name)\n");
std::printf(" --validate-wvhc <wvhc-base> [--json]\n");
std::printf(" Static checks: id>0+unique, creatureId>0, kind/movement/power in range, exactly 1 driver seat, no duplicate seatIndex\n");
std::printf(" --export-wvhc-json <wvhc-base> [out.json]\n");
std::printf(" Export binary .wvhc to a human-editable JSON sidecar with nested seat arrays (defaults to <base>.wvhc.json)\n");
std::printf(" --import-wvhc-json <json-path> [out-base]\n");
std::printf(" Import a .wvhc.json sidecar back into binary .wvhc (accepts vehicleKind/movementKind/powerType int OR name string)\n");
std::printf(" --gen-holidays <whol-base> [name]\n");
std::printf(" Emit .whol starter: 3 seasonal holidays (Hallow's End / Brewfest / Winter Veil) with boss+reward+quest cross-refs\n");
std::printf(" --gen-holidays-weekly <whol-base> [name]\n");
std::printf(" Emit .whol 3 weekly call-to-arms BG bonus windows (Warsong Gulch / Arathi Basin / Eye of the Storm)\n");
std::printf(" --gen-holidays-special <whol-base> [name]\n");
std::printf(" Emit .whol 3 special events (Wintergrasp world-PvP / Lunar Festival / Children's Week)\n");
std::printf(" --info-whol <whol-base> [--json]\n");
std::printf(" Print WHOL entries (id / kind / recurrence / start MM/DD / duration / quest+boss+reward IDs / name)\n");
std::printf(" --validate-whol <whol-base> [--json]\n");
std::printf(" Static checks: id>0+unique, kind 0..5, recurrence 0..3, duration>0, valid month/day for non-weekly, calendar-only warning\n");
std::printf(" --export-whol-json <whol-base> [out.json]\n");
std::printf(" Export binary .whol to a human-editable JSON sidecar (defaults to <base>.whol.json)\n");
std::printf(" --import-whol-json <json-path> [out-base]\n");
std::printf(" Import a .whol.json sidecar back into binary .whol (accepts holidayKind/recurrence int OR name string)\n");
std::printf(" --gen-liquids <wliq-base> [name]\n");
std::printf(" Emit .wliq starter: 3 stock liquids (Fresh Water / Lava / Sludge Slime) with shaders+sounds+DPS\n");
std::printf(" --gen-liquids-magical <wliq-base> [name]\n");
std::printf(" Emit .wliq 4 magical liquids (Fel Fire / Holy Light / Underworld / Cosmic) with damage spell IDs\n");
std::printf(" --gen-liquids-hazardous <wliq-base> [name]\n");
std::printf(" Emit .wliq 3 high-damage liquids (Naxx Slime / Acid Bog / Fel Lava) with WSPL cross-refs\n");
std::printf(" --info-wliq <wliq-base> [--json]\n");
std::printf(" Print WLIQ entries (id / kind / fog RGB / density / viscosity / DPS / spell+sound IDs / name)\n");
std::printf(" --validate-wliq <wliq-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name+shader+material not empty, kind 0..9, fog/visc 0..1, hazardous-no-damage warning\n");
std::printf(" --export-wliq-json <wliq-base> [out.json]\n");
std::printf(" Export binary .wliq to a human-editable JSON sidecar (defaults to <base>.wliq.json)\n");
std::printf(" --import-wliq-json <json-path> [out-base]\n");
std::printf(" Import a .wliq.json sidecar back into binary .wliq (accepts liquidKind int OR name string)\n");
std::printf(" --list-formats [--json]\n");
std::printf(" Print the catalog of all novel open formats (magic / extension / category / replaces / description)\n");
std::printf(" --info-magic <path> [--json]\n");
std::printf(" Auto-detect any .w* file by 4-byte magic; report format / version / catalog name / entry count + suggest --info-* flag\n");
std::printf(" --summary-dir <dir> [--json]\n");
std::printf(" Recursively walk a directory; report per-format file count, total entries, and bytes for every Wowee open format found\n");
std::printf(" --rename-by-magic <file> [--dry-run] [--force]\n");
std::printf(" Recover the correct .w* extension on a file by reading its 4-byte magic. --dry-run prints the planned move; --force overwrites\n");
std::printf(" --bulk-rename-by-magic <dir> [--dry-run] [--force]\n");
std::printf(" Apply --rename-by-magic recursively to every file in <dir>. Conflicts are skipped without --force; exits 1 if any rename failed\n");
std::printf(" --touch-tree <dir> [--json] [--quiet]\n");
std::printf(" CI integrity check: open every recognized .w* file in <dir>, parse standard header, report PASS/FAIL + extension mismatches. Exit 1 on any failure\n");
std::printf(" --tree-summary-md <dir> [out.md]\n");
std::printf(" Emit a Markdown report of a content tree (per-format counts + per-file detail with catalog name + entry count). Stdout if no out path\n");
std::printf(" --catalog-grep <pattern> <dir> [--case-sensitive] [--json]\n");
std::printf(" Recursively search catalog NAMES (the internal name field) across .w* files in <dir>. Case-insensitive by default. Exit 1 if no match\n");
std::printf(" --diff-headers <fileA> <fileB> [--json]\n");
std::printf(" Compare two .w* files at the standard catalog header level (magic / version / name / entry count / file size). Exit 1 if any field differs\n");
std::printf(" --audit-tree <dir> [--json]\n");
std::printf(" Walk directory recursively and flag corrupted/misnamed Wowee files: too-small, unknown-magic, ext/magic mismatch, magic-without-ext, truncated headers. Exit 1 on any issue\n");
std::printf(" --magic-fix <dir> [--apply] [--json]\n");
std::printf(" Auto-rename files whose extension doesn't match their magic to the canonical extension. Default is dry-run; pass --apply to commit. Skips collisions where the target already exists. Natural follow-up to --audit-tree\n");
std::printf(" --bulk-validate <dir> [--json]\n");
std::printf(" Recursively run each format's --validate-X validator across every recognized .w* file. Reports per-file pass/fail counts; lists failure paths. Asset formats with no validator are skipped. Exit 1 if any failure\n");
std::printf(" --bulk-export-json <dir> [--json]\n");
std::printf(" Recursively export every recognized .w* file to its JSON sidecar via the per-format --export-X-json flag. Useful for git-friendly diffs of binary catalogs. Exit 1 if any failure\n");
std::printf(" --bulk-import-json <dir> [--json]\n");
std::printf(" Recursively import every .wXXX.json sidecar back to its binary .w* form via the per-format --import-X-json flag. Inverse of --bulk-export-json. Exit 1 if any failure\n");
feat(editor): add --diff-tree to compare two directories of .w* catalogs Walks both trees in parallel, classifies each file by its 4-byte magic, and bucks differences into five categories: - only-in-A file present in A, missing from B (removed) - only-in-B file present in B, missing from A (added) - magic-changed same path but the format swapped (e.g. somebody renamed a .wsrg to .wmat) - size-changed same magic, different byte size (content was edited) - identical same magic, same size Exit 1 if any category but identical is non-zero, so it composes into shell pipelines and CI. JSON sidecar via --json. Useful for project-version comparison: did anything actually change between two snapshots? --diff-tree answers in one pass. Pairs naturally with the existing --diff-headers (which goes deeper on a single file pair) and the --bulk-* / --audit-tree family of cross-tree utilities. Files whose magic isn't recognized by the format table are silently skipped so unrelated junk in the tree (build artifacts, temp files) doesn't pollute the diff. Identity check is magic+size only — true byte-equality would need a hash and the heuristic is good enough for the typical use case. This is the 15th cross-format utility: --list-formats / --info-magic / --summary-dir / --rename-by-magic --bulk-rename-by-magic / --touch-tree / --tree-summary-md --catalog-grep / --diff-headers / --audit-tree / --magic-fix --bulk-validate / --bulk-export-json / --bulk-import-json --diff-tree CLI flag count 988 -> 989.
2026-05-09 22:46:52 -07:00
std::printf(" --diff-tree <dirA> <dirB> [--json]\n");
std::printf(" Compare two directory trees of .w* catalogs at the magic+size level. Reports only-in-A / only-in-B / magic-changed / size-changed / identical counts and lists changed paths. Exit 1 if any difference\n");
std::printf(" --orphan-jsons <dir> [--json]\n");
std::printf(" Find .wXXX.json sidecars whose binary .wXXX is missing. Useful after deleting/moving binaries — orphan JSONs accumulate noise and may shadow re-imports. Exit 1 if any orphans found\n");
std::printf(" --list-by-magic <dir> <magic> [--json]\n");
std::printf(" List every file in a directory tree matching a 4-char magic (e.g. WSPL). Reports per-file size + entry count + catalog name + relative path. Exit 1 if no matches\n");
std::printf(" --catalog-stats <file> [--json]\n");
std::printf(" Single-file deep stats — header bytes vs entry-section bytes, average entry size, sampled entry IDs. Useful for sizing analysis (which catalogs are biggest, where do the bytes go)\n");
std::printf(" --gen-animations <wani-base> [name]\n");
std::printf(" Emit .wani starter: 5 essential animations (Stand / Walk / Run / Death / AttackUnarmed) with fallback chains\n");
std::printf(" --gen-animations-combat <wani-base> [name]\n");
std::printf(" Emit .wani 8 combat anims (1H/2H/dual-wield/bow/rifle/thrown + parry + channel) with weapon-flag bitmasks\n");
std::printf(" --gen-animations-movement <wani-base> [name]\n");
std::printf(" Emit .wani 6 movement anims (Walk/Run/Sprint/Swim/Mount/Fly) with behavior-tier transitions (default/aerial/swim)\n");
std::printf(" --info-wani <wani-base> [--json]\n");
std::printf(" Print WANI entries (id / tier / flags / weapon mask / loop duration / fallback / name)\n");
std::printf(" --validate-wani <wani-base> [--json]\n");
std::printf(" Static checks: id-unique, name not empty, tier 0..4, fallback != self, loop+oneshot exclusivity, looped requires duration\n");
std::printf(" --export-wani-json <wani-base> [out.json]\n");
std::printf(" Export binary .wani to a human-editable JSON sidecar (defaults to <base>.wani.json)\n");
std::printf(" --import-wani-json <json-path> [out-base]\n");
std::printf(" Import a .wani.json sidecar back into binary .wani (accepts behaviorTier int OR name string)\n");
std::printf(" --gen-svk <wsvk-base> [name]\n");
std::printf(" Emit .wsvk starter: 3 visual kits (Frostbolt / Fireball / HealingTouch) with projectile + impact + hand effects\n");
std::printf(" --gen-svk-combat <wsvk-base> [name]\n");
std::printf(" Emit .wsvk 5 combat visuals (sword/arrow/groundpound/parry/deflect) with WANI animation cross-refs\n");
std::printf(" --gen-svk-utility <wsvk-base> [name]\n");
std::printf(" Emit .wsvk 4 utility visuals (portal/hearthstone/mount-summon/resurrect) with no projectile\n");
std::printf(" --info-wsvk <wsvk-base> [--json]\n");
std::printf(" Print WSVK entries (id / cast+impact anim IDs / projectile speed+gravity / cast duration / AoE radius / sounds / name)\n");
std::printf(" --validate-wsvk <wsvk-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name not empty, no negative speeds/radii, projectile-model + speed coherence, no-effect warning\n");
std::printf(" --export-wsvk-json <wsvk-base> [out.json]\n");
std::printf(" Export binary .wsvk to a human-editable JSON sidecar (defaults to <base>.wsvk.json)\n");
std::printf(" --import-wsvk-json <json-path> [out-base]\n");
std::printf(" Import a .wsvk.json sidecar back into binary .wsvk\n");
std::printf(" --gen-wsui <wwui-base> [name]\n");
std::printf(" Emit .wwui starter: 3 BG scoreboards (WSG flag captures, AB resources, EotS flag carrier)\n");
std::printf(" --gen-wsui-wintergrasp <wwui-base> [name]\n");
std::printf(" Emit .wwui 4 Wintergrasp UI (alliance+horde tank counts, time remaining, towers controlled)\n");
std::printf(" --gen-wsui-dungeon <wwui-base> [name]\n");
std::printf(" Emit .wwui 3 dungeon UI (boss progress bar, key fragments collected, treasure hunt counter)\n");
std::printf(" --info-wwui <wwui-base> [--json]\n");
std::printf(" Print WWUI entries (id / kind / panel position / always-visible / hide-when-zero / map+area / variableIndex / default / name)\n");
std::printf(" --validate-wwui <wwui-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name not empty, kind 0..5, position 0..4, alwaysVis+hideZero conflict, (mapId, varIdx) collision warning\n");
std::printf(" --export-wwui-json <wwui-base> [out.json]\n");
std::printf(" Export binary .wwui to a human-editable JSON sidecar (defaults to <base>.wwui.json)\n");
std::printf(" --import-wwui-json <json-path> [out-base]\n");
std::printf(" Import a .wwui.json sidecar back into binary .wwui (accepts displayKind/panelPosition int OR name string)\n");
std::printf(" --gen-pcn <wpcn-base> [name]\n");
std::printf(" Emit .wpcn starter: 3 single-check conditions (level>=60 / race=Human / class=Warrior)\n");
std::printf(" --gen-pcn-quest-gates <wpcn-base> [name]\n");
std::printf(" Emit .wpcn 4 quest-style gates (quest complete, reputation, achievement, zone presence) with cross-refs\n");
std::printf(" --gen-pcn-composite <wpcn-base> [name]\n");
std::printf(" Emit .wpcn 6 entries (3 leaves + 3 chained roots) exercising AND/OR/NOT chainOps for boolean trees\n");
std::printf(" --info-wpcn <wpcn-base> [--json]\n");
std::printf(" Print WPCN entries (id / kind / op / target IDs / int values / chainOp / chainNextId / name)\n");
std::printf(" --validate-wpcn <wpcn-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name not empty, kind 0..15, op 0..7, chainOp 0..3, chain self-loop, dangling chainNextId warning\n");
std::printf(" --export-wpcn-json <wpcn-base> [out.json]\n");
std::printf(" Export binary .wpcn to a human-editable JSON sidecar (defaults to <base>.wpcn.json)\n");
std::printf(" --import-wpcn-json <json-path> [out-base]\n");
std::printf(" Import a .wpcn.json sidecar back into binary .wpcn (accepts conditionKind/comparisonOp/chainOp int OR name string)\n");
feat(pipeline): add WTSK (Wowee Trade Skill / Recipe) catalog New open format — replaces SkillLineAbility.dbc plus the recipe portions of SkillLine.dbc plus the AzerothCore trade_skill SQL tables. Closes the crafting gap left by WSKL (which carries skill lines but not the recipes that bind to them). 14 professions (Blacksmithing, Tailoring, Engineering, Alchemy, Enchanting, Leatherworking, Jewelcrafting, Inscription, Mining, Skinning, Herbalism, Cooking, FirstAid, Fishing). Each recipe has 4 skill-up bracket thresholds (orange / yellow / green / gray) for skill-up probability, a craft spell cross-ref (WSPL), produced item cross-ref (WIT) with min/max quantity range, an optional tool item, and up to 4 reagent slots (itemId + count). Cross-references with prior formats — craftSpellId points at WSPL.spellId, producedItemId / toolItemId / reagent[].itemId all point at WIT.itemId, and skillId points at WSKL.skillId. CLI: --gen-tsk (3-recipe entry-tier starter), --gen-tsk- blacksmithing (5-recipe progression rough sharpening through truesilver champion), --gen-tsk-alchemy (5-recipe progression minor healing through flask of titans), --info-wtsk, --validate-wtsk with --json variants. Validator catches id=0/duplicates, profession out of range, missing craft spell or produced item, monotonic-bracket check (must be orange <= yellow <= green <= gray), reagent itemId-without-count mismatch, and free-recipe warning (no reagents and no tool). Format graph now exposes 49 distinct binary formats. CLI flag count: 747 → 752.
2026-05-09 19:41:49 -07:00
std::printf(" --gen-tsk <wtsk-base> [name]\n");
std::printf(" Emit .wtsk starter: 3 entry-tier recipes (Coarse Sharpening Stone / Linen Bandage / Minor Healing Potion)\n");
std::printf(" --gen-tsk-blacksmithing <wtsk-base> [name]\n");
std::printf(" Emit .wtsk 5-recipe Blacksmithing progression (rough sharpening → truesilver champion plate)\n");
std::printf(" --gen-tsk-alchemy <wtsk-base> [name]\n");
std::printf(" Emit .wtsk 5-recipe Alchemy progression (minor healing → flask of titans) with reagent slots\n");
std::printf(" --info-wtsk <wtsk-base> [--json]\n");
std::printf(" Print WTSK entries (id / profession / 4 skill brackets / craft spell / produced item / qty / tool / reagent count / name)\n");
std::printf(" --validate-wtsk <wtsk-base> [--json]\n");
std::printf(" Static checks: id>0+unique, name not empty, profession 0..13, craft spell + produced item required, monotonic skill brackets\n");
std::printf(" --export-wtsk-json <wtsk-base> [out.json]\n");
std::printf(" Export binary .wtsk to a human-editable JSON sidecar with nested reagents (defaults to <base>.wtsk.json)\n");
std::printf(" --import-wtsk-json <json-path> [out-base]\n");
std::printf(" Import a .wtsk.json sidecar back into binary .wtsk (accepts profession int OR name string, reagents array up to 4 slots)\n");
feat(pipeline): add WCEQ (Wowee Creature Equipment) — 50th open format Replaces the AzerothCore-style creature_equip_template SQL tables plus the visible-weapon / shield / ranged-slot data that was traditionally embedded in creature templates. Closes a long-standing gap in the creature subsystem: until now WCRT defined a creature's stats, WSPN placed it in the world, and WLOT defined what it drops — but nothing defined what items it visibly equips. Each entry binds a creatureId to up to three equipped items (main hand / off hand / ranged) plus the visual kit that fires when the main-hand weapon is brandished. equipFlags bits encode hidden / dual-wield / shield-offhand / thrown-ranged / 2H polearm to drive the renderer's attachment-point selection. Cross-references with prior formats — creatureId points at WCRT.creatureId, mainHandItemId / offHandItemId / rangedItemId all point at WIT.itemId, and mainHandVisualId points at WSVK.visualKitId so brandished weapons can play their signature glow / aura. CLI: --gen-ceq (3 generic guard/hunter/rogue starters), --gen-ceq-bosses (4 iconic loadouts incl. Frostmourne and Illidan's warglaives, with WSVK visual cross-refs), --gen-ceq-ranged (3 ranged-only rifle/bow/crossbow loadouts), --info-wceq, --validate-wceq with --json variants. Validator catches id=0/duplicates, missing creatureId, all-empty-slots warning, kFlagDualWield without both hand items, kFlagShield without offhand item, mutually-exclusive dual-wield + shield, and 2H polearm with offhand item filled. Format graph milestone: 50 distinct binary formats. CLI flag count: 754 → 760.
2026-05-09 19:48:13 -07:00
std::printf(" --gen-ceq <wceq-base> [name]\n");
std::printf(" Emit .wceq starter: 3 generic creature loadouts (guard 1H+shield, hunter bow, rogue dual-dagger)\n");
std::printf(" --gen-ceq-bosses <wceq-base> [name]\n");
std::printf(" Emit .wceq 4 iconic boss loadouts (Onyxia 2H, Lich King Frostmourne, Sylvanas bow, Illidan dual warglaives) with WSVK refs\n");
std::printf(" --gen-ceq-ranged <wceq-base> [name]\n");
std::printf(" Emit .wceq 3 ranged-only loadouts (rifle / bow / crossbow) — ranged slot only, no melee\n");
std::printf(" --info-wceq <wceq-base> [--json]\n");
std::printf(" Print WCEQ entries (id / creatureId / mainhand+offhand+ranged item IDs / equipFlags / visualKitId / name)\n");
std::printf(" --validate-wceq <wceq-base> [--json]\n");
std::printf(" Static checks: id+creatureId>0+unique, dual-wield/shield/2H polearm flag coherence, mutually-exclusive flag combos\n");
std::printf(" --export-wceq-json <wceq-base> [out.json]\n");
std::printf(" Export binary .wceq to a human-editable JSON sidecar (defaults to <base>.wceq.json)\n");
std::printf(" --import-wceq-json <json-path> [out-base]\n");
std::printf(" Import a .wceq.json sidecar back into binary .wceq (slot fields default to canonical 16/17/18 if omitted)\n");
feat(pipeline): add WSET (Wowee Item Set / Tier Bonus) catalog 51st open format — replaces ItemSet.dbc + ItemSetSpell.dbc plus the AzerothCore-style item_set_spell SQL data. Closes the tier-bonus gap left by WIT (which describes individual items but not the set bonuses they grant when worn together). Each entry binds up to 8 piece item IDs to up to 4 bonus thresholds — at N pieces worn, the matching bonus spell activates as an aura. Standard 2/4/6/8-piece tier set pattern is the canonical case; 5-piece PvP sets with 2/4 bonuses are also supported. Cross-references with prior formats — itemIds[] point at WIT.itemId, bonusSpellIds[] point at WSPL.spellId, and requiredSkillId points at WSKL.skillId. requiredClassMask is a 32-bit field (uint32_t) so bit positions match WCHC's classId enum directly — Druid (bit 11 = 0x800) and Mage (bit 8 = 0x100) wouldn't fit in a uint8_t. CLI: --gen-itset (2 raid sets — Battlegear of Wrath + Stormrage Raiment, real WoW item/spell IDs), --gen-itset-tier (4 tier-1 progression sets covering plate / cloth / leather / holy plate), --gen-itset-pvp (3 PvP gladiator 5-piece sets with honor-rank skill thresholds), --info-wset, --validate-wset with --json variants. Validator catches id+name+pieceCount required, pieceCount/bonusCount within array bounds, piece- slot drift (0 IDs within count or non-0 IDs past count), bonus thresholds strictly ascending, no bonus threshold exceeding pieceCount (would never trigger), and spellId=0 in any populated bonus slot. Format graph: 50 → 51 binary formats. CLI flag count: 762 → 767.
2026-05-09 19:54:36 -07:00
std::printf(" --gen-itset <wset-base> [name]\n");
std::printf(" Emit .wset starter: 2 raid sets (Battlegear of Wrath / Stormrage Raiment) — 8-piece tier-2 layouts\n");
std::printf(" --gen-itset-tier <wset-base> [name]\n");
std::printf(" Emit .wset 4 progression tier-1 sets (warrior plate / mage cloth / rogue leather / paladin holy plate) with 2/4/6 bonuses\n");
std::printf(" --gen-itset-pvp <wset-base> [name]\n");
std::printf(" Emit .wset 3 PvP gladiator sets (5-piece) with 2/4 bonuses and honor-rank skill thresholds\n");
std::printf(" --info-wset <wset-base> [--json]\n");
std::printf(" Print WSET entries (id / pieces / bonuses / classMask / skill+rank gates / first item ID / name)\n");
std::printf(" --validate-wset <wset-base> [--json]\n");
std::printf(" Static checks: id+name+pieceCount required, piece/bonus arrays match counts, monotonic bonus thresholds within pieceCount\n");
std::printf(" --export-wset-json <wset-base> [out.json]\n");
std::printf(" Export binary .wset to a human-editable JSON sidecar with nested itemIds[] + bonuses[] arrays\n");
std::printf(" --import-wset-json <json-path> [out-base]\n");
std::printf(" Import a .wset.json sidecar back into binary .wset (pieceCount/bonusCount derived from array sizes; missing slots cleared)\n");
feat(pipeline): add WGTP (Wowee Game Tips) catalog 52nd open format — replaces GameTips.dbc plus loading-screen tutorial hint tables. Defines the rotating tips shown during world loads, the contextual tutorial hints that fire on first gameplay events (first quest accept, first death, first dungeon entry), and the persistent tooltip-help strings that explain UI elements. 4 display kinds (LoadingScreen / Tutorial / TooltipHelp / Hint), 7 audience-filter bits (Alliance / Horde / NewPlayer / Hardcore / PvE / PvP / Roleplay) for pool selection, level range gating (minLevel + maxLevel), displayWeight for relative frequency within the pool, optional WPCN condition cross-ref for further gating, and class-mask restriction matching WCHC bit positions. Cross-references with prior formats — conditionId points at WPCN.conditionId for advanced gating; requiredClassMask uses the same WCHC.classId bit layout as WGLY/WSET. CLI: --gen-tips (3 generic loading-screen tips), --gen-tips- new-player (5 onboarding Tutorial-kind tips for level 1-15, weighted higher for new players), --gen-tips-advanced (4 endgame tips for level 70+ covering raid mechanics / arena / daily professions / dungeon finder), --info-wgtp, --validate-wgtp with --json variants. Validator catches id/name/text required, kind 0..3, audienceFilter=0 (tip never shown), invalid level range, displayWeight=0 (in pool but never picked) warning, and brevity check (>280 chars) on Tutorial / Hint kinds that need to fit on screen. Format graph: 51 → 52 binary formats. CLI flag count: 770 → 775.
2026-05-09 20:00:56 -07:00
std::printf(" --gen-tips <wgtp-base> [name]\n");
std::printf(" Emit .wgtp starter: 3 generic loading-screen tips (combat / movement / quest hints) for kAudienceAll\n");
std::printf(" --gen-tips-new-player <wgtp-base> [name]\n");
std::printf(" Emit .wgtp 5 onboarding tutorial tips (hearthstone / talents / mount / quest log / professions) for level 1-15\n");
std::printf(" --gen-tips-advanced <wgtp-base> [name]\n");
std::printf(" Emit .wgtp 4 endgame tips (raid mechanics / arena / daily professions / dungeon finder) for level 70+\n");
std::printf(" --info-wgtp <wgtp-base> [--json]\n");
std::printf(" Print WGTP entries (id / kind / audience mask / level range / weight / condition / classMask / name)\n");
std::printf(" --validate-wgtp <wgtp-base> [--json]\n");
std::printf(" Static checks: id+name+text required, kind 0..3, audienceFilter>0, valid level range, brevity check on tutorial/hint kinds\n");
std::printf(" --export-wgtp-json <wgtp-base> [out.json]\n");
std::printf(" Export binary .wgtp to a human-editable JSON sidecar (defaults to <base>.wgtp.json)\n");
std::printf(" --import-wgtp-json <json-path> [out-base]\n");
std::printf(" Import a .wgtp.json sidecar back into binary .wgtp (accepts displayKind int OR name string)\n");
2026-05-09 20:05:06 -07:00
std::printf(" --gen-cmp <wcmp-base> [name]\n");
std::printf(" Emit .wcmp starter: 3 common vendor companions (Mechanical Squirrel / Cat / Prairie Dog)\n");
std::printf(" --gen-cmp-rare <wcmp-base> [name]\n");
std::printf(" Emit .wcmp 4 promo / collector pets (Mini Diablo / Panda Cub / Zergling / Murky) at Epic rarity\n");
std::printf(" --gen-cmp-faction <wcmp-base> [name]\n");
std::printf(" Emit .wcmp 3 faction-restricted pets (Alliance Lion Cub / Horde Mottled Boar / Argent Squire any-faction)\n");
std::printf(" --info-wcmp <wcmp-base> [--json]\n");
std::printf(" Print WCMP entries (id / creature / kind / rarity / faction / learn spell+item / idle sound / name)\n");
std::printf(" --validate-wcmp <wcmp-base> [--json]\n");
std::printf(" Static checks: id+name+creatureId+learnSpellId required, kind 0..7, rarity 0..3, faction 0..2, Epic-no-itemId warning\n");
std::printf(" --export-wcmp-json <wcmp-base> [out.json]\n");
std::printf(" Export binary .wcmp to a human-editable JSON sidecar (defaults to <base>.wcmp.json)\n");
std::printf(" --import-wcmp-json <json-path> [out-base]\n");
std::printf(" Import a .wcmp.json sidecar back into binary .wcmp (accepts companionKind/rarity/factionRestriction int OR name string)\n");
feat(pipeline): add WSMC (Wowee Spell Mechanic) catalog 54th open format — replaces SpellMechanic.dbc plus the AzerothCore-style diminishing-returns (DR) tables. Defines crowd-control mechanic categories that spells reference: Stun, Silence, Polymorph, Sleep, Fear, Root, Snare, Slow, Knockback, etc. Each mechanic carries gameplay metadata (breaks-on-damage, can-be-dispelled, default duration, max stacks) plus DR category and dispel type. 8 DR categories (DRNone / DRStun / DRDisorient / DRSilence / DRRoot / DRPolymorph / DRControlled / DRMisc) — the runtime uses these to gate repeated CC on the same target. 7 dispel types (DispelNone / Magic / Curse / Disease / Poison / Enrage / Stealth) bind which dispel spells can remove the mechanic. conflictsMask is a bitmask of OTHER mechanic IDs — only one mechanic from a conflict-group can apply to a target simultaneously. Cross-references with prior formats — mechanicId is referenced by WSPL.spellId entries that apply this CC; this catalog is referenced from spell tags rather than referencing out. CLI: --gen-smc (3 baseline Stun/Silence/Snare), --gen-smc- hard (5 hard-CC: Stun/Polymorph/Sleep/Fear/Knockback with conflictsMask wiring), --gen-smc-roots (4 movement-impair: Root/Snare/Slow stacking 5x/GroundPin breaks-on-damage), --info-wsmc, --validate-wsmc with --json variants. Validator catches id+name required, DR category 0..7, dispel type 0..6, maxStacks=0 (mechanic could never apply), canBeDispelled+DispelNone inconsistency, and self-conflict bit set in conflictsMask (mechanic blocking itself). Format graph: 53 → 54 binary formats. CLI flag count: 784 → 789.
2026-05-09 20:09:44 -07:00
std::printf(" --gen-smc <wsmc-base> [name]\n");
std::printf(" Emit .wsmc starter: 3 baseline mechanics (Stun / Silence / Snare) covering the most common DR categories\n");
std::printf(" --gen-smc-hard <wsmc-base> [name]\n");
std::printf(" Emit .wsmc 5 hard-CC mechanics (Stun / Polymorph / Sleep / Fear / Knockback) with conflictsMask DR wiring\n");
std::printf(" --gen-smc-roots <wsmc-base> [name]\n");
std::printf(" Emit .wsmc 4 movement-impair mechanics (Root / Snare / Slow stacking / GroundPin breaks-on-damage)\n");
std::printf(" --info-wsmc <wsmc-base> [--json]\n");
std::printf(" Print WSMC entries (id / DR category / dispel type / breaks / dispellable / duration / max stacks / conflicts mask / name)\n");
std::printf(" --validate-wsmc <wsmc-base> [--json]\n");
std::printf(" Static checks: id+name required, DR 0..7 / dispel 0..6, maxStacks>0, dispellable+none-dispel inconsistency, self-conflict bit\n");
std::printf(" --export-wsmc-json <wsmc-base> [out.json]\n");
std::printf(" Export binary .wsmc to a human-editable JSON sidecar (defaults to <base>.wsmc.json)\n");
std::printf(" --import-wsmc-json <json-path> [out-base]\n");
std::printf(" Import a .wsmc.json sidecar back into binary .wsmc (accepts drCategory/dispelType int OR name string)\n");
std::printf(" --gen-kbd <wkbd-base> [name]\n");
std::printf(" Emit .wkbd starter: 3 essential bindings (MOVE_FORWARD / TARGET_NEAREST_ENEMY / TOGGLE_CHARACTER)\n");
std::printf(" --gen-kbd-movement <wkbd-base> [name]\n");
std::printf(" Emit .wkbd 8 movement bindings (WASD / arrow keys / jump / autorun) with primary+alternate keys\n");
std::printf(" --gen-kbd-ui <wkbd-base> [name]\n");
std::printf(" Emit .wkbd 10 UI panel bindings (Character C / Inventory I / Bags B / Spellbook P / Talents N / etc)\n");
std::printf(" --info-wkbd <wkbd-base> [--json]\n");
std::printf(" Print WKBD entries (id / category / user-overridable / sort / default key + alt / actionName)\n");
std::printf(" --validate-wkbd <wkbd-base> [--json]\n");
std::printf(" Static checks: id+actionName+defaultKey required, category 0..8, alt!=default, unique action names + primary keys\n");
std::printf(" --export-wkbd-json <wkbd-base> [out.json]\n");
std::printf(" Export binary .wkbd to a human-editable JSON sidecar (defaults to <base>.wkbd.json)\n");
std::printf(" --import-wkbd-json <json-path> [out-base]\n");
std::printf(" Import a .wkbd.json sidecar back into binary .wkbd (accepts category int OR name string; isUserOverridable defaults to 1)\n");
feat(pipeline): add WSCH (Wowee Spell School) catalog 56th open format — replaces SpellSchools.dbc plus the Resistances.dbc resistance-cap tables. Defines damage schools spells use: Physical, Holy, Fire, Nature, Frost, Shadow, Arcane, plus combined / hybrid schools that count as multiple types simultaneously (Spellfire, Spellshadow, Spellfrost — relevant for resistance-bypass mechanics). 7 canonical schools with single-bit IDs (1, 2, 4, 8, 16, 32, 64) so combinedSchoolMask values line up directly with the spell engine's school-bit enum. Hybrid schools use high-bit IDs (0x80000001+) and their combinedSchoolMask references the canonical bits they qualify as. Each school carries visual identity (color tint, icon), gameplay rules (canBeImmune / canBeAbsorbed / canBeReflected / canCrit), resistance cap at max level, and cast / impact sound IDs. Cross-references with prior formats — castSoundId and impactSoundId point at WSND.soundId; combinedSchoolMask is a bitmask of OTHER WSCH.schoolId values within the same catalog. CLI: --gen-sch (3 base — Physical / Fire / Holy showing non-resistable Holy + non-reflectable Physical), --gen-sch- magical (6 canonical magical schools with proper colors + 365 max-level resistance caps), --gen-sch-combined (3 hybrids — Spellfire / Spellshadow / Spellfrost with multi-bit combinedSchoolMask), --info-wsch, --validate-wsch with --json variants. Validator catches id+name required, reflected-without-absorbed warning (reflected damage should be absorbable), self-referential combinedSchoolMask (school qualifying as itself), and combined-mask references to bits not defined in the same catalog (resolved at runtime across catalogs). Format graph: 55 → 56 binary formats. CLI flag count: 798 → 804.
2026-05-09 20:19:34 -07:00
std::printf(" --gen-sch <wsch-base> [name]\n");
std::printf(" Emit .wsch starter: 3 base schools (Physical / Fire / Holy) with proper colors+resistance caps\n");
std::printf(" --gen-sch-magical <wsch-base> [name]\n");
std::printf(" Emit .wsch 6 magical schools (Holy / Fire / Nature / Frost / Shadow / Arcane) with canonical schoolId bits\n");
std::printf(" --gen-sch-combined <wsch-base> [name]\n");
std::printf(" Emit .wsch 3 hybrid schools (Spellfire / Spellshadow / Spellfrost) with combinedSchoolMask wiring\n");
std::printf(" --info-wsch <wsch-base> [--json]\n");
std::printf(" Print WSCH entries (schoolId / immune+absorb+reflect+crit flags / resistance cap / combined-mask / name)\n");
std::printf(" --validate-wsch <wsch-base> [--json]\n");
std::printf(" Static checks: id+name required, reflected-without-absorbed warning, combined-mask references defined schools, no self-reference\n");
std::printf(" --export-wsch-json <wsch-base> [out.json]\n");
std::printf(" Export binary .wsch to a human-editable JSON sidecar (defaults to <base>.wsch.json)\n");
std::printf(" --import-wsch-json <json-path> [out-base]\n");
std::printf(" Import a .wsch.json sidecar back into binary .wsch (canBeImmune/Absorbed/Crit default to 1, canBeReflected to 0)\n");
feat(pipeline): add WLFG (Wowee Looking-for-Group) catalog 57th open format — replaces LFGDungeons.dbc plus the AzerothCore-style dungeon-finder reward tables. Defines the dungeons / raids that the Dungeon Finder / Raid Browser presents to players, with their level brackets, group-size requirements, role requirements (tank / heal / DPS), and queue-completion rewards. 4 difficulty levels (Normal / Heroic / Mythic / Hardmode — the latter for Ulduar-style toggleable boss difficulty), 4 expansion gates (Classic / TBC / WotLK / TurtleWoW), and 3 role-requirement bits (Tank / Heal / DPS — typically all three for queue-formed groups). Cross-references with prior formats — mapId points at WMS.mapId (the instance map), queueRewardItemId points at WIT.itemId (the random reward bag), firstClearAchievement points at WACH.achievementId. CLI: --gen-lfg (3 classic 5-mans Ragefire/Wailing/Deadmines with real WoW mapIds + level brackets), --gen-lfg-heroic (5 WotLK 80-level heroic 5-mans with emblem rewards + real first-clear achievement IDs from Halls of Lightning through Old Kingdom), --gen-lfg-raid (3 raid entries — Naxx-25, Ulduar-25 Hardmode, ToC-25 Mythic), --info-wlfg, --validate-wlfg with --json variants. Validator catches id+name+mapId required, difficulty 0..3, expansion 0..3, minLevel<=maxLevel, recommended-level outside range warning, unusual groupSize warning (5/10/25/40 are canonical), and zero role mask (queue can't form a balanced group). Format graph: 56 → 57 binary formats. CLI flag count: 804 → 811.
2026-05-09 20:24:21 -07:00
std::printf(" --gen-lfg <wlfg-base> [name]\n");
std::printf(" Emit .wlfg starter: 3 classic 5-man dungeons (Ragefire Chasm / Wailing Caverns / Deadmines) with level brackets\n");
std::printf(" --gen-lfg-heroic <wlfg-base> [name]\n");
std::printf(" Emit .wlfg 5 WotLK 80-level heroic 5-mans with emblem rewards + first-clear achievement IDs\n");
std::printf(" --gen-lfg-raid <wlfg-base> [name]\n");
std::printf(" Emit .wlfg 3 raid entries (Naxxramas-25 / Ulduar-25 Hardmode / ToC-25 Mythic) with larger groupSize\n");
std::printf(" --info-wlfg <wlfg-base> [--json]\n");
std::printf(" Print WLFG entries (id / map / level range / minIlvl / difficulty / groupSize / role mask / expansion / rewards / name)\n");
std::printf(" --validate-wlfg <wlfg-base> [--json]\n");
std::printf(" Static checks: id+name+mapId required, difficulty 0..3 / expansion 0..3, minLevel<=maxLevel, recommended within range, role mask>0\n");
std::printf(" --export-wlfg-json <wlfg-base> [out.json]\n");
std::printf(" Export binary .wlfg to a human-editable JSON sidecar (defaults to <base>.wlfg.json)\n");
std::printf(" --import-wlfg-json <json-path> [out-base]\n");
std::printf(" Import a .wlfg.json sidecar back into binary .wlfg (accepts difficulty/expansion int OR name string; groupSize defaults to 5, role mask to kRoleAll)\n");
feat(pipeline): add WMAC (Wowee Macro / Slash Command) catalog 58th open format — novel format with no direct DBC equivalent. WoW historically stored player macros client-side in the user profile and system slash commands as hardcoded engine handlers; WMAC unifies both into a single structured catalog so default macros, system slash commands, and shipped player presets can be authored, validated, and shipped as content alongside the rest of the open-format graph. 5 macro kinds (SystemSlash for engine /sit /dance handlers, DefaultMacro for shipped presets, PlayerTemplate for user templates, GuildMacro for guild-shared, SharedMacro for account-wide). Multi-line macro bodies are stored verbatim with literal '\n' separators — the client parses /cast / /target / /run lines at runtime. Cross-references with prior formats — requiredClassMask uses WCHC.classId bit positions (Warrior=0x02, etc, same as WGLY/ WSET/WGTP). CLI: --gen-mac (3 system slash — /sit, /dance, /target with [@mouseover] modifier), --gen-mac-combat (4 warrior combat templates — heroic strike spam, charge/intercept stance dance, intercept stance switch, victory rush+bloodthirst fallback — each with default key bindings), --gen-mac-utility (3 universal utility — /follow target, mass /inv with %targetN tokens, /releasecorpse via RepopMe()), --info-wmac, --validate-wmac with --json variants. Validator catches id+name+body required, kind 0..4, body within maxLength cap, body starting with '/' or '#' (slash command or showtooltip annotation), and SystemSlash + classMask warning (slash commands are class- agnostic — restricting them to a class makes no sense). Format graph: 57 → 58 binary formats. CLI flag count: 814 → 819.
2026-05-09 20:30:17 -07:00
std::printf(" --gen-mac <wmac-base> [name]\n");
std::printf(" Emit .wmac starter: 3 system slash commands (/sit, /dance, /target with [@mouseover])\n");
std::printf(" --gen-mac-combat <wmac-base> [name]\n");
std::printf(" Emit .wmac 4 warrior combat macros (Heroic Strike spam, Charge/Intercept stance dance, Victory Rush) with bind keys + classMask\n");
std::printf(" --gen-mac-utility <wmac-base> [name]\n");
std::printf(" Emit .wmac 3 utility macros (/follow target, mass /inv with %%targetN, /releasecorpse via RepopMe)\n");
std::printf(" --info-wmac <wmac-base> [--json]\n");
std::printf(" Print WMAC entries (id / kind / classMask / bindKey / maxLen / body length / name)\n");
std::printf(" --validate-wmac <wmac-base> [--json]\n");
std::printf(" Static checks: id+name+body required, kind 0..4, body within maxLength, body starts with '/' or '#', SystemSlash + classMask warning\n");
std::printf(" --export-wmac-json <wmac-base> [out.json]\n");
std::printf(" Export binary .wmac to a human-editable JSON sidecar (defaults to <base>.wmac.json; multi-line bodies escape \\n)\n");
std::printf(" --import-wmac-json <json-path> [out-base]\n");
std::printf(" Import a .wmac.json sidecar back into binary .wmac (accepts macroKind int OR name string; maxLength defaults to 255)\n");
feat(pipeline): add WCHF (Wowee Character Customization Feature) catalog 59th open format — replaces CharHairGeosets.dbc + CharFacialHairStyles.dbc plus the variation portions of CharSections.dbc. Defines per-(race, sex) customization options the character creation screen exposes: skin colors, face variations, hair styles, hair colors, facial hair (beards / mustaches), and race-specific markings (Tauren horns, Draenei tendrils, Blood Elf ears). 9 feature kinds (SkinColor / FaceVariation / HairStyle / HairColor / FacialHair / FacialColor / EarStyle / Horns / Markings) cover the full canonical customization surface. Each entry is one selectable carousel choice for one (race, sex, kind) tuple — variationIndex disambiguates. expansionGate enum gates Blood Elf / Draenei (TBC) and DK features (WotLK) behind the right expansion unlock. Cross-references with prior formats — raceId points at WCHC.race.raceId. requiresExpansion bit positions match the WLFG expansion enum (Classic=0, TBC=1, WotLK=2, Turtle=3) for consistency. CLI: --gen-chf (5 Human Male starter — skin / face / 2 hair styles / facial hair), --gen-chf-bloodelf (8 Blood Elf Female hair styles, requiresExpansion=TBC — the iconic TBC race feature), --gen-chf-tauren (6 Tauren Male features using race-specific Horns kind + 3 facial hair variations), --info-wchf, --validate-wchf with --json variants. Validator catches id+name+raceId+texturePath required, kind 0..8 / sex 0..1 / expansion 0..3, and the critical (race, sex, kind, variation) tuple-uniqueness check — duplicates would shadow each other in the create- character carousel. Format graph: 58 → 59 binary formats. CLI flag count: 819 → 826.
2026-05-09 20:35:21 -07:00
std::printf(" --gen-chf <wchf-base> [name]\n");
std::printf(" Emit .wchf starter: 5 Human Male features (1 skin / 1 face / 2 hair styles / 1 facial hair)\n");
std::printf(" --gen-chf-bloodelf <wchf-base> [name]\n");
std::printf(" Emit .wchf 8 Blood Elf Female hair styles (TBC iconic feature, requiresExpansion=TBC)\n");
std::printf(" --gen-chf-tauren <wchf-base> [name]\n");
std::printf(" Emit .wchf 6 Tauren Male features (3 horn variants + 3 facial hair) using race-specific kinds\n");
std::printf(" --info-wchf <wchf-base> [--json]\n");
std::printf(" Print WCHF entries (id / race / sex / kind / variation / geoset bits / expansion / name)\n");
std::printf(" --validate-wchf <wchf-base> [--json]\n");
std::printf(" Static checks: id+name+raceId+texturePath required, kind 0..8 / sex 0..1 / expansion 0..3, unique (race,sex,kind,variation) tuples\n");
std::printf(" --export-wchf-json <wchf-base> [out.json]\n");
std::printf(" Export binary .wchf to a human-editable JSON sidecar (defaults to <base>.wchf.json)\n");
std::printf(" --import-wchf-json <json-path> [out-base]\n");
std::printf(" Import a .wchf.json sidecar back into binary .wchf (accepts featureKind/sexId/requiresExpansion int OR name string)\n");
feat(pipeline): add WPVP (Wowee PvP Honor / Rank) — 60th open format 60th open format milestone — replaces the AzerothCore-style PvP rank tables plus the vanilla honor-rank reward chains. Defines PvP progression rungs: vanilla honor ranks (Private through Grand Marshal / High Warlord), arena rating brackets (Combatant / Challenger / Rival / Duelist / Gladiator), and battleground rated tiers. 5 rank kinds (VanillaHonor / ArenaRating / BattlegroundRated / WorldPvP / ConquestPoint) cover the canonical PvP progression surface. Each entry carries an alliance-specific name and a horde-specific name (Grand Marshal = High Warlord; arena tiers share the same name on both factions), an honor or rating threshold, and optional gear cross-refs (chest, gloves, shoulders) into WIT. Cross-references with prior formats — titleId points at WTTL.titleId (the unlock title), chest/gloves/shoulders point at WIT.itemId for the matching PvP set, bracketBgId points at WBGD.bgId for battleground-bracket gating. CLI: --gen-pvp (3 vanilla entry tiers Rank2-4 with alliance vs horde alternate names), --gen-pvp-alliance (9 vanilla ranks 6-14 with WTTL+WIT cross-refs and ramping honor thresholds 50k → 260k), --gen-pvp-arena (5 arena rating brackets 1500/1750/2000/2200/2400 with 10/20/40/80/160 emblem rewards), --info-wpvp, --validate-wpvp with --json variants. Validator catches id+name required, kind 0..4, level range valid, faction-name pairing (one set without the other is a typo), threshold monotonicity within a rankKind, arena rating below 1500 floor, and VanillaHonor with minHonor=0 baseline warning. Format graph: 59 → 60 binary formats (milestone). CLI flag count: 826 → 833.
2026-05-09 20:41:09 -07:00
std::printf(" --gen-pvp <wpvp-base> [name]\n");
std::printf(" Emit .wpvp starter: 3 vanilla honor entry tiers (Rank2-4 — Private/Scout, Corporal/Grunt, Sergeant)\n");
std::printf(" --gen-pvp-alliance <wpvp-base> [name]\n");
std::printf(" Emit .wpvp 9 vanilla ranks 6-14 (Knight through Grand Marshal) with WTTL+WIT cross-refs for chest/gloves/shoulders\n");
std::printf(" --gen-pvp-arena <wpvp-base> [name]\n");
std::printf(" Emit .wpvp 5 arena rating brackets (Combatant 1500 / Challenger 1750 / Rival 2000 / Duelist 2200 / Gladiator 2400)\n");
std::printf(" --info-wpvp <wpvp-base> [--json]\n");
std::printf(" Print WPVP entries (id / kind / threshold / emblem reward / title+chest cross-refs / alliance / horde names)\n");
std::printf(" --validate-wpvp <wpvp-base> [--json]\n");
std::printf(" Static checks: id+name required, kind 0..4, level range valid, faction alt names paired, threshold monotonic within kind, arena>=1500\n");
std::printf(" --export-wpvp-json <wpvp-base> [out.json]\n");
std::printf(" Export binary .wpvp to a human-editable JSON sidecar (defaults to <base>.wpvp.json)\n");
std::printf(" --import-wpvp-json <json-path> [out-base]\n");
std::printf(" Import a .wpvp.json sidecar back into binary .wpvp (accepts rankKind int OR name string; bracket level defaults to 1..80)\n");
feat(pipeline): add WBNK (Wowee Bag / Bank Slot) catalog 61st open format — replaces ItemBag.dbc plus the bank-storage and special-purpose container tables. Defines every slot the player has access to: equipped bags, bank bags, keyring, soul shard bag, quiver, reagent bag, hunter pet stable. 8 bag kinds (Inventory / Bank / Keyring / Quiver / SoulShard / Stable / Reagent / Wallet) cover the canonical container surface. Each entry has a fixed capacity (or 0 = variable, size set by equipped bag), a display order in the inventory UI, an unlock state with optional gold cost (bank bags ramp through 10s / 1g / 10g / 25g / 50g / 100g matching canonical WoW prices), and an accepts-bag-subclass mask gating which container kinds may be equipped (generic / herb / enchanting / engineer / gem / mining / leather / inscription / quiver / ammo pouch). Cross-references with prior formats — fixedBagItemId points at WIT.itemId for the bag item that always occupies a fixed slot (0 = player-equipable variable slot). CLI: --gen-bnk (5 inventory slots — 16-slot fixed main backpack + 4 player-equippable bag slots accepting generic containers + herb + enchanting bags), --gen-bnk-bank (8 bank bag slots with the canonical WoW unlock cost ramp), --gen-bnk-special (4 special-purpose: 32-slot Keyring fixed, warlock SoulShardBag, hunter ArrowQuiver, hunter HuntersStable for 5 pets), --info-wbnk, --validate-wbnk with --json variants. Validator catches id+name required, kind 0..7, locked-with-zero-cost (slot can never be unlocked), fixed-slot-with-non-zero-mask (equippable bag would be ignored), variable slot with empty mask (no bag can fit), and ambiguous (bagKind, displayOrder) tuples (UI sort would flicker). Format graph: 60 → 61 binary formats. CLI flag count: 833 → 840.
2026-05-09 20:46:06 -07:00
std::printf(" --gen-bnk <wbnk-base> [name]\n");
std::printf(" Emit .wbnk starter: 5 inventory slots (16-slot main backpack + 4 player-equippable bag slots)\n");
std::printf(" --gen-bnk-bank <wbnk-base> [name]\n");
std::printf(" Emit .wbnk 8 bank bag slots — first 2 free, then 6 with ascending gold costs (10s/1g/10g/25g/50g/100g)\n");
std::printf(" --gen-bnk-special <wbnk-base> [name]\n");
std::printf(" Emit .wbnk 4 special slots (Keyring fixed, Soul Shard warlock-only, Quiver hunter-only, Hunters Stable for pets)\n");
std::printf(" --info-wbnk <wbnk-base> [--json]\n");
std::printf(" Print WBNK entries (id / kind / size / display order / unlock status + cost / accept-mask / fixed bag item / name)\n");
std::printf(" --validate-wbnk <wbnk-base> [--json]\n");
std::printf(" Static checks: id+name required, kind 0..7, locked-with-zero-cost warning, fixed-slot-with-mask conflict, ambiguous (kind,order)\n");
std::printf(" --export-wbnk-json <wbnk-base> [out.json]\n");
std::printf(" Export binary .wbnk to a human-editable JSON sidecar (defaults to <base>.wbnk.json)\n");
std::printf(" --import-wbnk-json <json-path> [out-base]\n");
std::printf(" Import a .wbnk.json sidecar back into binary .wbnk (accepts bagKind int OR name string; isUnlocked defaults to 1, mask to AnyContainer)\n");
feat(pipeline): add WRUN (Wowee DK Rune Cost) catalog 62nd open format — replaces RuneCost.dbc plus the DK-specific portions of ChrPowerType. Defines per-spell rune costs (Blood / Frost / Unholy) and runic-power generation / consumption for the Death Knight class. 4 spell tree branches (BloodTree / FrostTree / UnholyTree / Generic) classify which spec uses each rune cost. Each entry binds a spell to its rune cost (how many of each rune kind the spell consumes), an optional anyDeathConvertCost (extra Death-rune-acceptable cost for procced abilities), and a runicPowerCost (negative = generator, positive = spender). Cross-references with prior formats — spellId points at WSPL.spellId (the spell that uses this rune cost). CLI: --gen-rune (3 baseline DK abilities — Death Strike 1F+1U + 20RP gen, Frost Strike pure 40 RP spender, Heart Strike 1B + 10RP gen), --gen-rune-blood (4 blood-tree DK abilities — Heart Strike, Death and Decay AoE, Vampiric Blood tank cooldown, Rune Tap self-heal), --gen-rune-frost (4 frost-tree — Frost Strike, Howling Blast AoE, Obliterate finisher, Icy Touch ranged opener applying Frost Fever), --info-wrun, --validate-wrun with --json variants. Validator catches id+name+spellId required, branch 0..3, no rune cost > 2 (DK only has 2 of each rune type so a higher cost can never be paid), runicPowerCost > 100 (DK RP cap), no-cost warning (spell consumes nothing — verify it's a passive/stance/form), and high-RP-generator warning (> 25 RP per cast is unusual). Format graph: 61 → 62 binary formats. CLI flag count: 840 → 847.
2026-05-09 20:52:19 -07:00
std::printf(" --gen-rune <wrun-base> [name]\n");
std::printf(" Emit .wrun starter: 3 baseline DK rune costs (Death Strike 1F+1U, Frost Strike 40RP spender, Heart Strike 1B)\n");
std::printf(" --gen-rune-blood <wrun-base> [name]\n");
std::printf(" Emit .wrun 4 blood-tree DK rune costs (Heart Strike, Death and Decay AoE, Vampiric Blood, Rune Tap self-heal)\n");
std::printf(" --gen-rune-frost <wrun-base> [name]\n");
std::printf(" Emit .wrun 4 frost-tree DK rune costs (Frost Strike, Howling Blast AoE, Obliterate finisher, Icy Touch opener)\n");
std::printf(" --info-wrun <wrun-base> [--json]\n");
std::printf(" Print WRUN entries (id / spell / B/F/U costs / death-convertible / RP cost+arrow / branch / name)\n");
std::printf(" --validate-wrun <wrun-base> [--json]\n");
std::printf(" Static checks: id+name+spellId required, branch 0..3, no rune cost > 2 (DK has only 2 of each), runicPower > 100 cap, no-cost warning\n");
std::printf(" --export-wrun-json <wrun-base> [out.json]\n");
std::printf(" Export binary .wrun to a human-editable JSON sidecar (defaults to <base>.wrun.json)\n");
std::printf(" --import-wrun-json <json-path> [out-base]\n");
std::printf(" Import a .wrun.json sidecar back into binary .wrun (accepts spellTreeBranch int OR name string)\n");
feat(pipeline): add WLDS (Wowee Loading Screen) catalog 63rd open format — replaces LoadingScreens.dbc plus the per-zone background-image tables. Defines the loading-screen images shown when the client crosses into a new map / instance, with optional level-bracket gating and expansion gating (TBC art only shown if expansion installed). When multiple screens match the player's current map + level + expansion, displayWeight selects randomly between them — a zone with 3 weighted variants gets a different image roughly proportional to weight. 4 expansion gates (Classic / TBC / WotLK / TurtleWoW), isAnimated flag for screens with subtle animation, isWideAspect flag for 16:9 raid intro art (vs 4:3 standard). Cross-references with prior formats — mapId points at WMS.map.mapId (which map triggers this loading screen); mapId=0 is the catch-all sentinel for screens shown when no map-specific screen matches. CLI: --gen-lds (3 base screens — Elwynn level 1-30, Orgrimmar, GenericFallback level 31-80 catch-all), --gen-lds-instances (5 WotLK dungeon screens with mapId+expansion cross-refs: Halls of Lightning/Stone, Utgarde Pinnacle, Violet Hold, Old Kingdom), --gen-lds-raid (3 raid intro screens — Naxxramas/Ulduar/ToC at isWideAspect=1 with weight=3), --info-wlds, --validate-wlds with --json variants. Validator catches id+name+texture required, expansion 0..3, level range valid, weight=0 (in pool but never picked), and the practical catch-all overlap warning when multiple mapId=0 screens share overlapping level brackets (random pick becomes non-deterministic). Format graph: 62 → 63 binary formats. CLI flag count: 847 → 854.
2026-05-09 20:59:25 -07:00
std::printf(" --gen-lds <wlds-base> [name]\n");
std::printf(" Emit .wlds starter: 3 base loading screens (Elwynn / Orgrimmar / GenericFallback catch-all)\n");
std::printf(" --gen-lds-instances <wlds-base> [name]\n");
std::printf(" Emit .wlds 5 WotLK dungeon loading screens (Halls of Lightning / Stone / UP / Violet Hold / Old Kingdom)\n");
std::printf(" --gen-lds-raid <wlds-base> [name]\n");
std::printf(" Emit .wlds 3 raid intro screens (Naxxramas / Ulduar / Trial of the Crusader) with isWideAspect=1\n");
std::printf(" --info-wlds <wlds-base> [--json]\n");
std::printf(" Print WLDS entries (id / mapId / level range / display weight / expansion / animated+wide flags / name)\n");
std::printf(" --validate-wlds <wlds-base> [--json]\n");
std::printf(" Static checks: id+name+texture required, expansion 0..3, level range valid, weight=0 warning, catch-all overlap\n");
std::printf(" --export-wlds-json <wlds-base> [out.json]\n");
std::printf(" Export binary .wlds to a human-editable JSON sidecar (defaults to <base>.wlds.json)\n");
std::printf(" --import-wlds-json <json-path> [out-base]\n");
std::printf(" Import a .wlds.json sidecar back into binary .wlds (accepts expansionRequired int OR name string)\n");
feat(pipeline): add WSUF (Wowee Item Random Suffix) catalog 64th open format — replaces ItemRandomProperties.dbc + ItemRandomSuffix.dbc plus the AzerothCore-style suffix-roll tables. Defines random "of the X" suffixes that roll on green and blue items at world drop ("Sturdy Cloth Cap of the Bear" = base item + STR + STA suffix). 5 suffix categories (Generic / Elemental / Defensive / PvPSuffix / Crafted), per-suffix item-quality bracket gating (only blue+ items can roll PvPSuffix), restricted-slot mask that limits which equipment slots a suffix can apply to (15 slot bits matching WCEQ slot enum), and up to 5 stat bonus slots per suffix matching WoW canonical max. statValuePoints isn't an absolute number — it's a scaling base that the runtime multiplies by an item-level coefficient to compute the final per-item bonus, so "of the Bear" gives proportionally more strength on a level-60 item than on a level-20 item. Cross-references with prior formats — statKind values match WIT.statType enum (STR=4, AGI=3, INT=5, SPI=6, STA=7) so item generators roll consistent stats with base items. CLI: --gen-suf (3 generic stat triads — Bear STR+STA, Eagle INT+SPI, Tiger STR+AGI), --gen-suf-magical (4 elemental spell-power suffixes restricted to caster-eligible slots), --gen-suf-pvp (3 PvPSuffix entries with resilience + offensive stats, blue+ quality only), --info-wsuf, --validate-wsuf with --json variants. Validator catches id+name required, category 0..4, quality range valid (0..7 covers heirloom), itemQuality floor<=ceiling, stat-kind/value pairing (kind=0 with value!=0 is a typo, vice versa), and no-stats warning (suffix renames item without changing it). Format graph: 63 → 64 binary formats. CLI flag count: 854 → 861.
2026-05-09 21:05:49 -07:00
std::printf(" --gen-suf <wsuf-base> [name]\n");
std::printf(" Emit .wsuf starter: 3 stat-suffix entries (of the Bear STR+STA / of the Eagle INT+SPI / of the Tiger STR+AGI)\n");
std::printf(" --gen-suf-magical <wsuf-base> [name]\n");
std::printf(" Emit .wsuf 4 elemental suffixes (of Fire / of Frost / of Shadow / of Arcane) — flat per-school spell power\n");
std::printf(" --gen-suf-pvp <wsuf-base> [name]\n");
std::printf(" Emit .wsuf 3 PvPSuffix-category entries (of the Champion / of the Gladiator / of Resilience) blue+ only\n");
std::printf(" --info-wsuf <wsuf-base> [--json]\n");
std::printf(" Print WSUF entries (id / category / quality range / restricted slot mask / # populated stat slots / name)\n");
std::printf(" --validate-wsuf <wsuf-base> [--json]\n");
std::printf(" Static checks: id+name required, category 0..4, quality range valid (0..7), stat-kind/value pairing, no-stats warning\n");
std::printf(" --export-wsuf-json <wsuf-base> [out.json]\n");
std::printf(" Export binary .wsuf to a human-editable JSON sidecar with nested stats[] arrays (defaults to <base>.wsuf.json)\n");
std::printf(" --import-wsuf-json <json-path> [out-base]\n");
std::printf(" Import a .wsuf.json sidecar back into binary .wsuf (accepts suffixCategory int OR name string; missing stat slots cleared)\n");
std::printf(" --gen-crr <wcrr-base> [name]\n");
std::printf(" Emit .wcrr starter: 3 essential combat ratings (Hit / Crit / Haste) at canonical L80 conversion values\n");
std::printf(" --gen-crr-defensive <wcrr-base> [name]\n");
std::printf(" Emit .wcrr 4 defensive ratings (Defense / Dodge / Parry / Block) for tank stat scaling with diminishing returns caps\n");
std::printf(" --gen-crr-spell <wcrr-base> [name]\n");
std::printf(" Emit .wcrr 3 spell ratings (SpellPower direct / SpellPenetration flat / MP5 mana regen) for caster stat scaling\n");
std::printf(" --info-wcrr <wcrr-base> [--json]\n");
std::printf(" Print WCRR entries (id / kind / pointsAtL1/L60/L70/L80 conversion floors / max benefit pct / name)\n");
std::printf(" --validate-wcrr <wcrr-base> [--json]\n");
std::printf(" Static checks: id+name required, kind 0..4, all pointsAtLN > 0 (div-by-zero risk), maxPct > 0, monotonic ascending curve\n");
std::printf(" --export-wcrr-json <wcrr-base> [out.json]\n");
std::printf(" Export binary .wcrr to a human-editable JSON sidecar (defaults to <base>.wcrr.json)\n");
std::printf(" --import-wcrr-json <json-path> [out-base]\n");
std::printf(" Import a .wcrr.json sidecar back into binary .wcrr (accepts ratingKind int OR name string; pointsAtLN default to canonical WoW values)\n");
feat(pipeline): add WUMV (Wowee Unit Movement Type) catalog 66th open format — replaces UnitMovement.dbc plus the movement-modifier portions of CreatureModelData.dbc. Defines movement speed types (walk / run / swim / flight / fly / pitch) with their canonical baseline speeds in yards-per- second, plus the temp speed buffs that stack on top (Sprint, Aspect of the Cheetah, Travel Form). 12 movement categories cover the canonical surface (Walk / Run / Backward / Swim / SwimBack / Turn / Flight / FlightBack / Pitch / Fly / FlyBack / TempBuff). baseSpeed is yards/second for baseline categories and ignored for TempBuff entries (which use baseMultiplier instead). maxMultiplier caps stacking — Sprint capped at 1.4 means Sprint + Aspect of Cheetah doesn't exceed 1.4× run speed. stackingPriority resolves conflicts when multiple buffs of equal multiplier compete (higher wins). CLI: --gen-umv (4 baseline at canonical WoW vanilla speeds: Walk 2.5y/s, Run 7.0y/s, Swim 4.7y/s, Turn π rad/s), --gen-umv-flight (5 flight entries — ground-rail Flight 7y/s, free Fly 14y/s, Pitch 1.5 rad/s, backward variants at slower 4.5y/s), --gen-umv-buffs (5 temp speed buffs matching real WoW spell auras with proper durations and stacking priorities), --info-wumv, --validate-wumv with --json variants. Validator catches id+name required, category 0..11, baseMultiplier > 0 (otherwise unit freezes in place), maxMultiplier >= baseMultiplier (cap below floor would clamp the base down), baseline categories need baseSpeed > 0, and Run < 3.0y/s warning (canonical is 7.0y/s). Format graph: 65 → 66 binary formats. CLI flag count: 870 → 875.
2026-05-09 21:18:03 -07:00
std::printf(" --gen-umv <wumv-base> [name]\n");
std::printf(" Emit .wumv starter: 4 baseline movement types (Walk 2.5y/s / Run 7.0y/s / Swim 4.7y/s / Turn π rad/s)\n");
std::printf(" --gen-umv-flight <wumv-base> [name]\n");
std::printf(" Emit .wumv 5 flight entries (Flight ground-rail / Fly free / FlyBack / FlightBack / Pitch rate) requiring flight skill\n");
std::printf(" --gen-umv-buffs <wumv-base> [name]\n");
std::printf(" Emit .wumv 5 temp speed buffs (Sprint 1.4× / Aspect Cheetah 1.3× / Travel 1.4× / Crusader 1.2× / Wind Walk 1.5×)\n");
std::printf(" --info-wumv <wumv-base> [--json]\n");
std::printf(" Print WUMV entries (id / category / baseSpeed / multipliers / duration / priority / flight+stack flags / name)\n");
std::printf(" --validate-wumv <wumv-base> [--json]\n");
std::printf(" Static checks: id+name required, category 0..11, baseMultiplier > 0, max >= base, baseline categories need baseSpeed > 0\n");
std::printf(" --export-wumv-json <wumv-base> [out.json]\n");
std::printf(" Export binary .wumv to a human-editable JSON sidecar (defaults to <base>.wumv.json)\n");
std::printf(" --import-wumv-json <json-path> [out-base]\n");
std::printf(" Import a .wumv.json sidecar back into binary .wumv (accepts movementCategory int OR name string; multipliers default to 1.0/1.4)\n");
feat(pipeline): add WQSO (Wowee Quest Sort) catalog 67th open format — replaces QuestSort.dbc plus the quest-log categorization fields in QuestInfo.dbc. Defines the categories that quests fall into for the quest-log UI: class quests (Warrior trial, etc), profession quests, daily quests, holiday events, reputation grinds, dungeon / heroic / raid quests, repeatables, PvP, tournament. 12 sort kinds (General / ClassQuest / Profession / Daily / Holiday / Reputation / Dungeon / Raid / Heroic / Repeatable / PvP / Tournament). Each WQT (quest) entry can reference a sortId here to be grouped under the right header in the quest log. Sorts can be class-restricted (Warrior quests only show for warriors), profession-restricted, or faction-reputation-gated. Cross-references with prior formats — targetClassMask uses WCHC.classId bit positions (matches WGLY/WSET/WGTP convention), targetProfessionId points at WTSK.profession enum, targetFactionId points at WFAC.factionId. CLI: --gen-qso (3 generic sorts — General catch-all, Daily reset, Repeatable non-daily), --gen-qso-class (10 class- specific sorts with proper bit masks for Warrior 0x02 through Druid 0x800), --gen-qso-profession (8 profession sorts with WTSK profession enum cross-refs), --info-wqso, --validate-wqso with --json variants. Validator catches id+name+displayName required, kind 0..11, ClassQuest with classMask=0 (not actually class-restricted), Profession with profId=0 + non-Blacksmithing-name (likely typo since 0=Blacksmithing in WTSK), and Reputation with factionId=0 (no faction to grind). Format graph: 66 → 67 binary formats. CLI flag count: 877 → 882.
2026-05-09 21:23:38 -07:00
std::printf(" --gen-qso <wqso-base> [name]\n");
std::printf(" Emit .wqso starter: 3 generic sorts (General catch-all / Daily reset / Repeatable non-daily)\n");
std::printf(" --gen-qso-class <wqso-base> [name]\n");
std::printf(" Emit .wqso 10 class-specific sorts (Warrior / Paladin / Hunter / Rogue / Priest / DK / Shaman / Mage / Warlock / Druid)\n");
std::printf(" --gen-qso-profession <wqso-base> [name]\n");
std::printf(" Emit .wqso 8 profession sorts (Blacksmithing / Tailoring / Engineering / Alchemy / Enchanting / Leatherworking / Jewelcrafting / Inscription)\n");
std::printf(" --info-wqso <wqso-base> [--json]\n");
std::printf(" Print WQSO entries (id / kind / display priority / classMask / profession / faction / displayName)\n");
std::printf(" --validate-wqso <wqso-base> [--json]\n");
std::printf(" Static checks: id+name+displayName required, kind 0..11, ClassQuest needs classMask>0, Reputation needs factionId>0\n");
std::printf(" --export-wqso-json <wqso-base> [out.json]\n");
std::printf(" Export binary .wqso to a human-editable JSON sidecar (defaults to <base>.wqso.json)\n");
std::printf(" --import-wqso-json <json-path> [out-base]\n");
std::printf(" Import a .wqso.json sidecar back into binary .wqso (accepts sortKind int OR name string)\n");
std::printf(" --gen-srg <wsrg-base> [name]\n");
std::printf(" Emit .wsrg starter: 3 baseline range buckets (Self 0-0 / Melee 0-5 / Spell 0-30)\n");
std::printf(" --gen-srg-ranged <wsrg-base> [name]\n");
std::printf(" Emit .wsrg 5 ranged spell buckets (Short 20y / Medium 30y / Long 40y / VeryLong 100y / Unlimited)\n");
std::printf(" --gen-srg-friendly <wsrg-base> [name]\n");
std::printf(" Emit .wsrg 3 friendly-only buckets (Heal 40y / Cleanse 30y / Buff 30y) where hostile range = 0\n");
std::printf(" --info-wsrg <wsrg-base> [--json]\n");
std::printf(" Print WSRG entries (id / kind / hostile + friendly min-max yards / icon color / name)\n");
std::printf(" --validate-wsrg <wsrg-base> [--json]\n");
std::printf(" Static checks: id+name required, rangeKind 0..6, min<=max, no negatives, no duplicate ids; warns on Self+nonzero range and Melee>8y\n");
std::printf(" --export-wsrg-json <wsrg-base> [out.json]\n");
std::printf(" Export binary .wsrg to a human-editable JSON sidecar (defaults to <base>.wsrg.json)\n");
std::printf(" --import-wsrg-json <json-path> [out-base]\n");
std::printf(" Import a .wsrg.json sidecar back into binary .wsrg (accepts rangeKind int OR rangeKindName string)\n");
std::printf(" --gen-sct <wsct-base> [name]\n");
std::printf(" Emit .wsct starter: 4 baseline cast time buckets (Instant 0ms / FastCast 1s / MediumCast 1.5s / LongCast 3s)\n");
std::printf(" --gen-sct-channel <wsct-base> [name]\n");
std::printf(" Emit .wsct 3 channeled-spell buckets (TickEvery1s 3s total / TickEvery2s 6s / TickEvery3s 9s)\n");
std::printf(" --gen-sct-ramp <wsct-base> [name]\n");
std::printf(" Emit .wsct 4 level-scaled buckets (perLevelMs > 0; baseMs grows with level, clamped to min/max)\n");
std::printf(" --info-wsct <wsct-base> [--json]\n");
std::printf(" Print WSCT entries (id / kind / baseMs / perLevelMs / minMs / maxMs / iconColor / name)\n");
std::printf(" --validate-wsct <wsct-base> [--json]\n");
std::printf(" Static checks: id+name required, castKind 0..4, baseMs>=0, min<=max, no duplicate ids; warns on Instant+nonzero base, errors on Channel+0 base\n");
std::printf(" --export-wsct-json <wsct-base> [out.json]\n");
std::printf(" Export binary .wsct to a human-editable JSON sidecar (defaults to <base>.wsct.json)\n");
std::printf(" --import-wsct-json <json-path> [out-base]\n");
std::printf(" Import a .wsct.json sidecar back into binary .wsct (accepts castKind int OR castKindName string)\n");
feat(editor): add WSDR (Spell Duration Index) — completes WSRG/WSCT/WSDR triplet Open replacement for SpellDuration.dbc plus per-spell duration fields in Spell.dbc. Defines the categorical duration buckets that auras / DoTs / HoTs / buffs reference (5s / 30s / 5min / 1hr / UntilCancelled / UntilDeath). Together with WSRG (range) and WSCT (cast time), this completes a small triplet of spell-metadata catalogs: instead of every Frostbolt rank embedding its own range, cast time, and chill-debuff duration as duplicate fields, each spell holds three small integer ids that resolve through these three tables. The engine retunes thousands of spells at once by editing one bucket. Duration scales with caster level via perLevelMs (a rank-1 Renew at 9s grows to 12s at lvl 60), then is clamped to maxDurationMs. Negative baseDurationMs is the canonical sentinel for "no timer" (UntilCancelled / UntilDeath); resolveAtLevel returns -1 for those so HUD code can render the indefinite-duration glyph. Three preset emitters: --gen-sdr (5 baseline tiers from instant to one-hour), --gen-sdr-buffs (4 long-duration buffs including UntilDeath), --gen-sdr-dot (4 tick-based DoT/HoT buckets at 3s ticks). Validation enforces base>0 for Timed/TickBased, base<0 for permanent kinds, max>=base, durationKind 0..4, no duplicate ids, and warns on Instant+nonzero base. Wired through the cross-format table; WSDR appears automatically in all 9 cross-format utilities. Format count 69 -> 70; CLI flag count 899 -> 904.
2026-05-09 21:41:55 -07:00
std::printf(" --gen-sdr <wsdr-base> [name]\n");
std::printf(" Emit .wsdr starter: 5 baseline duration buckets (Instant 0 / Short 5s / Medium 30s / Long 5min / OneHour 60min)\n");
std::printf(" --gen-sdr-buffs <wsdr-base> [name]\n");
std::printf(" Emit .wsdr 4 long-duration buffs (PartyBuff 30m / RaidBuff 60m / WorldBuff 4hr / UntilDeath -1)\n");
std::printf(" --gen-sdr-dot <wsdr-base> [name]\n");
std::printf(" Emit .wsdr 4 DoT/HoT buckets (4-tick 12s / 5-tick 15s / 6-tick 18s / 8-tick 24s)\n");
std::printf(" --info-wsdr <wsdr-base> [--json]\n");
std::printf(" Print WSDR entries (id / kind / baseMs / perLevelMs / maxMs / iconColor / name)\n");
std::printf(" --validate-wsdr <wsdr-base> [--json]\n");
std::printf(" Static checks: id+name required, durationKind 0..4, base>0 for Timed/TickBased, base<0 for permanent kinds, max>=base, no duplicate ids\n");
std::printf(" --export-wsdr-json <wsdr-base> [out.json]\n");
std::printf(" Export binary .wsdr to a human-editable JSON sidecar (defaults to <base>.wsdr.json)\n");
std::printf(" --import-wsdr-json <json-path> [out-base]\n");
std::printf(" Import a .wsdr.json sidecar back into binary .wsdr (accepts durationKind int OR durationKindName string)\n");
feat(editor): add WSCD (Spell Cooldown Category) open catalog format Open replacement for SpellCooldown.dbc plus the per-spell category-cooldown fields in Spell.dbc. Defines the shared-cooldown buckets that related spells reference: casting one spell triggers a cooldown on every other spell in the same bucket. Mage Polymorph variants (Sheep / Pig / Turtle / Cat) all share one bucket so morphing a target locks all variants at once. Healing potions and mana potions share the SharedWithItems bucket so consuming one locks the other. Distinct from WSDR (which times how long an aura stays on a target) — WSCD times how long before a spell can be cast again. The global cooldown (GCD) is itself just one bucket of this kind, flagged with OnGCDStart so the engine triggers it at cast start rather than cast finish. Three preset emitters: --gen-cdb (4 baseline buckets including GCD), --gen-cdb-class (5 mage-specific class cooldowns including the Polymorph family), --gen-cdb-items (5 item cooldowns including the heal/mana potion shared bucket and the 60min Hearthstone family). Validation enforces id+name presence, bucketKind 0..4, no duplicate ids, and warns on Global without OnGCDStart (engine wouldn't trigger on cast start) and Spell kind with SharedWithItems (contradictory). categoryFlags is a bitfield (AffectedByHaste / SharedWithItems / OnGCDStart / IgnoresCooldownReduction); --info-wscd decodes the bits to label list. Wired through the cross-format table; WSCD appears automatically in all 9 cross-format utilities. Format count 70 -> 71; CLI flag count 907 -> 912.
2026-05-09 21:49:13 -07:00
std::printf(" --gen-cdb <wscd-base> [name]\n");
std::printf(" Emit .wscd starter: 4 baseline cooldown buckets (GlobalCooldown 1.5s / ShortItem 5s / MediumItem 30s / LongItem 60s shared)\n");
std::printf(" --gen-cdb-class <wscd-base> [name]\n");
std::printf(" Emit .wscd 5 mage class buckets (Polymorph family / AlterTime 90s / Counterspell 24s / Blink 15s / IceBlock 5min)\n");
std::printf(" --gen-cdb-items <wscd-base> [name]\n");
std::printf(" Emit .wscd 5 item buckets (HealingPot+ManaPot 60s shared / ManaJade GCD-only / EngineerTrinket 60s / Hearthstone 60min)\n");
std::printf(" --info-wscd <wscd-base> [--json]\n");
std::printf(" Print WSCD entries (id / kind / cooldownMs / category flags / name) — flags decoded as label list\n");
std::printf(" --validate-wscd <wscd-base> [--json]\n");
std::printf(" Static checks: id+name required, bucketKind 0..4, no duplicate ids; warns on Global without OnGCDStart and Spell with SharedWithItems\n");
std::printf(" --export-wscd-json <wscd-base> [out.json]\n");
std::printf(" Export binary .wscd to a human-editable JSON sidecar (defaults to <base>.wscd.json)\n");
std::printf(" --import-wscd-json <json-path> [out-base]\n");
std::printf(" Import a .wscd.json sidecar back into binary .wscd (accepts bucketKind int OR name; categoryFlags as int OR pipe-separated label string)\n");
feat(editor): add WCEF (Creature Family) open catalog format Open replacement for CreatureFamily.dbc plus the per-creature family fields in Creature.dbc. Defines the family categorization that pet-able beasts share (Bear / Cat / Wolf / Boar / Crab / Raptor / Devilsaur / etc), each with its own pet talent tree (Ferocity / Tenacity / Cunning), food preferences as a bitmask (Meat / Fish / Bread / Cheese / Fruit / Fungus / Raw), the skill line that family-specific abilities reference, and the minimum hunter level required to tame it. Used by the hunter pet system to decide which talent tree a tamed pet uses, validate that a hunter can tame a creature, match feeding-table food items to pet preferences, and gate exotic-beast families behind the Beast Master 51-point talent. Cross-references back to WCRT (creature.familyId points here) and WSPL (family-specific abilities reference WSPL spellId via the skillLine field). Three preset emitters: --gen-cef (5 baseline families covering both major talent trees), --gen-cef-ferocity (4 DPS-tree pets with bleed/howl/armor-shred mechanics), --gen-cef-exotic (4 exotic Beast Master families requiring 51-point talent). Validation enforces id+name presence, familyKind 0..5, talent tree 0..3, no duplicate ids, and warns on: - NotPet families with a non-None talent tree (irrelevant) - Exotic families with minLevelForTame > 80 (level-cap unreachable) - Beast/Exotic families with no food types set (pet would starve) Wired through the cross-format table; WCEF appears automatically in all 10 cross-format utilities. Format count 71 -> 72; CLI flag count 914 -> 919.
2026-05-09 21:54:00 -07:00
std::printf(" --gen-cef <wcef-base> [name]\n");
std::printf(" Emit .wcef starter: 5 baseline beast families (Bear/Cat/Wolf/Boar/Crab) covering Tenacity + Ferocity trees\n");
std::printf(" --gen-cef-ferocity <wcef-base> [name]\n");
std::printf(" Emit .wcef 4 Ferocity-tree DPS pets (Cat / Wolf / Raptor / Devilsaur) with bleed/howl/armor mechanics\n");
std::printf(" --gen-cef-exotic <wcef-base> [name]\n");
std::printf(" Emit .wcef 4 exotic Beast Master families (Worm / Devilsaur / Chimaera / Core Hound) — Exotic kind, requires 51-pt BM\n");
std::printf(" --info-wcef <wcef-base> [--json]\n");
std::printf(" Print WCEF entries (id / kind / talent tree / tame level / skill line / food types / name)\n");
std::printf(" --validate-wcef <wcef-base> [--json]\n");
std::printf(" Static checks: id+name required, familyKind 0..5, talent tree 0..3, no duplicate ids; warns on NotPet+talent, Exotic+lvl>80, Beast/Exotic with no foods (would starve)\n");
std::printf(" --export-wcef-json <wcef-base> [out.json]\n");
std::printf(" Export binary .wcef to a human-editable JSON sidecar (defaults to <base>.wcef.json)\n");
std::printf(" --import-wcef-json <json-path> [out-base]\n");
std::printf(" Import a .wcef.json sidecar back into binary .wcef (accepts familyKind/petTalentTree int OR name; petFoodTypes int OR pipe-separated label string)\n");
feat(editor): add WSPC (Spell Power Cost) — completes spell-bucket five-pack Open replacement for the per-spell power-cost fields in Spell.dbc plus SpellPowerCost-related side tables. Defines categorical power-cost buckets that spells reference (LowMana 5% / MediumMana 15% / HighMana 30% of caster max mana; fixed Rage-30 / Energy-40 / Runic-30 / etc), so spells share cost metadata across ranks instead of embedding per-rank cost numbers. Completes the small lookup-bucket five-pack: WSRG — range bucket WSCT — cast time bucket WSDR — duration bucket WSCD — cooldown bucket WSPC — power cost bucket (this catalog) Five small integer ids per spell (range / cast / dur / cd / cost) replace the dozens of duplicate per-rank fields that Blizzard's Spell.dbc carries. Editing one bucket here retunes every spell that references it — change LowMana from 5% to 4% and every rank-1 bolt across every caster class becomes cheaper. Cost can be flat (baseCost), per-level scaled (perLevelCost), or percentage-of-max-power (percentOfBase) — the engine sums whichever fields are non-zero. resolveCost(id, level, maxPower) does the math. Twelve power types covering every WoW resource (Mana / Rage / Focus / Energy / Happiness / Runic Power / Runes / Soul Shards / Holy Power / Eclipse / Health / NoCost). Three preset emitters: --gen-spc (4 baseline mana tiers), --gen-spc-rage (4 fixed warrior rage costs including stance-locked Whirlwind), --gen-spc-mixed (5 cross-class costs covering every non-mana power type with refund-on-miss flag for energy). Validation enforces id+name presence, powerType 0..11, no duplicate ids; warns on percentOfBase outside [0,1] (would overflow), NoCost type with non-zero cost fields, and non-NoCost types with no cost set (would cast for free — easy bug to ship). Wired through the cross-format table; WSPC appears automatically in all 11 cross-format utilities. Format count 72 -> 73; CLI flag count 922 -> 927.
2026-05-09 22:00:55 -07:00
std::printf(" --gen-spc <wspc-base> [name]\n");
std::printf(" Emit .wspc starter: 4 baseline mana cost tiers (NoCost / LowMana 5%% / MediumMana 15%% / HighMana 30%%) — %% of caster max mana\n");
std::printf(" --gen-spc-rage <wspc-base> [name]\n");
std::printf(" Emit .wspc 4 fixed warrior rage costs (HeroicStrike 15 / Slam 20 / Whirlwind 25 stance-locked / MortalStrike 30)\n");
std::printf(" --gen-spc-mixed <wspc-base> [name]\n");
std::printf(" Emit .wspc 5 cross-class costs covering every non-mana power type (Hunter Focus 30 / Rogue Energy 40 refund / DK Runic 30 / Paladin Holy 1 / Warlock Shard 1)\n");
std::printf(" --info-wspc <wspc-base> [--json]\n");
std::printf(" Print WSPC entries (id / powerType / baseCost / perLevelCost / percentOfBase / cost flags / name) — flags decoded as label list\n");
std::printf(" --validate-wspc <wspc-base> [--json]\n");
std::printf(" Static checks: id+name required, powerType 0..11, no duplicate ids; warns on percent outside [0,1], NoCost+nonzero, and non-NoCost types with no cost set (would cast for free)\n");
std::printf(" --export-wspc-json <wspc-base> [out.json]\n");
std::printf(" Export binary .wspc to a human-editable JSON sidecar (defaults to <base>.wspc.json)\n");
std::printf(" --import-wspc-json <json-path> [out-base]\n");
std::printf(" Import a .wspc.json sidecar back into binary .wspc (accepts powerType int OR name; costFlags int OR pipe-separated label string)\n");
feat(editor): add WGFS (Glyph Slot) open catalog format Open replacement for Blizzard's GlyphSlot.dbc. Defines the per-class glyph slot layout: which slots a class has (Major / Minor / Prime), in which display order they appear in the spellbook UI, and at which character level each slot becomes available for use. Distinct from WGLY (GlyphProperties) which defines the individual glyphs themselves. WGLY says "Glyph of Polymorph exists, costs 1 inscription dust, modifies Polymorph"; WGFS says "the slot that holds Glyph of Polymorph is the second Major Glyph Slot, unlocks at level 25, and only Mages have it". Layout grew across expansions, captured by the three presets: - --gen-gfs — 6 slots: 3 Major + 3 Minor all-class baseline (25/50/75 each) - --gen-gfs-wotlk — 6 slots: 3 Major (15/30/50) + 3 Minor (15/50/70) matching WotLK 3.3.5a - --gen-gfs-cata — 9 slots: 3 Prime + 3 Major + 3 Minor matching Cataclysm Cross-references back to WGLY (glyphs reference slotKind to constrain which glyph fits which slot) and WCHC (requiredClassMask uses the same bit layout as WCHC class IDs). Validation enforces id+name+classMask presence (classMask=0 means no class can use the slot — usually a config bug), slotKind 0..2, no duplicate ids; warns on minLevelToUnlock>80 (would never unlock at WotLK cap), displayOrder>4 (UI typically shows 3-4), and (kind+order) collisions for overlapping classMask (two slots claiming the same UI position would render on top of each other). isUnlockedFor(id, classBit, level) is the engine helper. Wired through the cross-format table; WGFS appears automatically in all 11 cross-format utilities. Format count 73 -> 74; CLI flag count 929 -> 934.
2026-05-09 22:05:05 -07:00
std::printf(" --gen-gfs <wgfs-base> [name]\n");
std::printf(" Emit .wgfs starter: 6 baseline glyph slots (3 Major + 3 Minor) all-class, unlocking at 25/50/75 each\n");
std::printf(" --gen-gfs-wotlk <wgfs-base> [name]\n");
std::printf(" Emit .wgfs WotLK 3.3.5a layout: 3 Major (15/30/50) + 3 Minor (15/50/70), all-class\n");
std::printf(" --gen-gfs-cata <wgfs-base> [name]\n");
std::printf(" Emit .wgfs Cataclysm layout: 3 Prime + 3 Major + 3 Minor (9 slots), all unlocking at 25/50/75\n");
std::printf(" --info-wgfs <wgfs-base> [--json]\n");
std::printf(" Print WGFS entries (id / kind / displayOrder / minLevelToUnlock / classMask / name)\n");
std::printf(" --validate-wgfs <wgfs-base> [--json]\n");
std::printf(" Static checks: id+name+classMask required, slotKind 0..2, no duplicate ids; warns on lvl>80, displayOrder>4, and (kind+order) collisions for overlapping classMask\n");
std::printf(" --export-wgfs-json <wgfs-base> [out.json]\n");
std::printf(" Export binary .wgfs to a human-editable JSON sidecar (defaults to <base>.wgfs.json)\n");
std::printf(" --import-wgfs-json <json-path> [out-base]\n");
std::printf(" Import a .wgfs.json sidecar back into binary .wgfs (accepts slotKind int OR slotKindName string)\n");
feat(editor): add WCDF (Creature Difficulty) open catalog format Open replacement for Blizzard's CreatureDifficulty.dbc. Maps a base creature entry to its difficulty variants: Normal-10 / Normal-25 / Heroic-10 / Heroic-25 in WotLK raid format. Each variant is itself a separate WCRT creature entry with its own stats, abilities, and loot. When a 25-man party engages an instance, the engine looks up the encounter base creature's difficultyId, reads the normal25Id field, and spawns that variant instead. This is how Lord Marrowgar in 25-Heroic ICC has 30M HP and hits for 80k while the same encounter in 10-Normal has 5M HP and hits for 25k — same spawn point, different WCRT entries. 5-man dungeons typically use only normal10Id + heroic10Id (the 25-man fields stay 0 — engine falls through to the 10-man variant when 25-man is queried). World bosses don't scale at all (all 4 variant fields stay 0, engine falls back to the base entry). Cross-references back to WCRT — every non-zero variant id field points at a WCRT.creatureId entry; the base creature itself lives in WCRT too. Three preset emitters: --gen-cdf (4 example bosses with full 4-variant routing), --gen-cdf-wotlk-raid (4 ICC-style raid bosses Marrowgar/Deathwhisper/Saurfang/LK with all 4 difficulty variants), --gen-cdf-fiveman (4 5-man dungeon bosses with only Normal+Heroic 10-man set). resolveVariant(id, mode) is the engine helper. Validation enforces id+name+baseCreatureId presence, spawnGroupKind 0..5, no duplicate ids; warns on: - WorldBoss kind with non-zero variant ids (world bosses don't scale) - duplicate baseCreatureId across routes (only first honored) - all-self-reference non-WorldBoss (creature doesn't actually scale) - Boss with n25 but not n10 (raid sequencing typo — n10 always comes with n25) Wired through the cross-format table; WCDF appears automatically in all 11 cross-format utilities. Format count 74 -> 75; CLI flag count 936 -> 941.
2026-05-09 22:10:09 -07:00
std::printf(" --gen-cdf <wcdf-base> [name]\n");
std::printf(" Emit .wcdf starter: 4 example boss/mini/add routes with full 4-variant 10/25/H10/H25 mapping\n");
std::printf(" --gen-cdf-wotlk-raid <wcdf-base> [name]\n");
std::printf(" Emit .wcdf 4 ICC-style raid bosses (Marrowgar / Deathwhisper / Saurfang / Lich King) with all 4 difficulty variants\n");
std::printf(" --gen-cdf-fiveman <wcdf-base> [name]\n");
std::printf(" Emit .wcdf 4 5-man dungeon bosses with only Normal+Heroic 10-man variants set (25-man fields stay 0)\n");
std::printf(" --info-wcdf <wcdf-base> [--json]\n");
std::printf(" Print WCDF entries (id / kind / baseCreatureId / 4 variant ids / name)\n");
std::printf(" --validate-wcdf <wcdf-base> [--json]\n");
std::printf(" Static checks: id+name+baseCreatureId required, spawnGroupKind 0..5, no duplicate ids; warns on WorldBoss with variants, Boss with only n10, duplicate baseIds, all-self-ref non-WorldBoss\n");
std::printf(" --export-wcdf-json <wcdf-base> [out.json]\n");
std::printf(" Export binary .wcdf to a human-editable JSON sidecar (defaults to <base>.wcdf.json)\n");
std::printf(" --import-wcdf-json <json-path> [out-base]\n");
std::printf(" Import a .wcdf.json sidecar back into binary .wcdf (accepts spawnGroupKind int OR spawnGroupKindName string)\n");
feat(editor): add WMAT (Item Material) open catalog format Open replacement for Blizzard's Material.dbc plus the Material and SheatheType fields in ItemDisplayInfo.dbc. Defines the material categorization that items reference (Cloth / Leather / Mail / Plate / Wood / Steel / Crystal / Ethereal / etc), each with its own foley sound (played on item use), impact sound (played on drop / hit), weight category, and material-property flags (IsBreakable / IsMagical / IsFlammable / IsConductive / IsHolyCharged / IsCursed). The engine plays a sword's metallic clang from impactSoundId when it hits a stone wall, but a cloth tabard makes no such sound — the difference is exactly the material assigned by this catalog. Every armor and weapon item in WIT references a materialId here. Twelve materialKind values cover the standard armor classes (Cloth/Leather/Mail/Plate/Hide), structural materials (Wood / Stone / Metal), and special categories (Liquid / Organic / Crystal / Ethereal). Three weight tiers (Light / Medium / Heavy) control encumbrance UI hints. Cross-references back to WSND (foleySoundId / impactSoundId reference WSND sound entries) and forward to WIT (item entries reference materialId here). Three preset emitters: --gen-mat (5 armor materials matching WoW's armor classes), --gen-mat-weapon (5 weapon materials from breakable+flammable Wood through enchanted endgame steel), --gen-mat-magical (4 magical materials with special flags including the IsHolyCharged anti-undead property). Validation enforces id+name presence, materialKind 0..11, weightCategory 0..2, no duplicate ids; warns on: - IsHolyCharged + IsCursed both set (engine picks one, typically IsCursed wins) - Plate kind that's not Heavy weight (canonical violation) - Cloth kind that's not Light weight (canonical violation) Wired through the cross-format table; WMAT appears automatically in all 11 cross-format utilities. Format count 75 -> 76; CLI flag count 943 -> 948.
2026-05-09 22:14:44 -07:00
std::printf(" --gen-mat <wmat-base> [name]\n");
std::printf(" Emit .wmat 5 armor materials (Cloth/Leather/Mail/Plate/Hide) with weight categories matching WoW's armor classes\n");
std::printf(" --gen-mat-weapon <wmat-base> [name]\n");
std::printf(" Emit .wmat 5 weapon materials (Wood breakable+flammable / Steel / Mithril / Adamantite / EnchantedSteel magical)\n");
std::printf(" --gen-mat-magical <wmat-base> [name]\n");
std::printf(" Emit .wmat 4 magical materials (Crystal magical+breakable / Ethereal / CursedBone / HolyForged charged)\n");
std::printf(" --info-wmat <wmat-base> [--json]\n");
std::printf(" Print WMAT entries (id / kind / weight / foley/impact sound ids / material flags / name) — flags decoded as label list\n");
std::printf(" --validate-wmat <wmat-base> [--json]\n");
std::printf(" Static checks: id+name required, materialKind 0..11, weightCategory 0..2, no duplicate ids; warns on Holy+Cursed combo, Plate non-heavy, Cloth non-light\n");
std::printf(" --export-wmat-json <wmat-base> [out.json]\n");
std::printf(" Export binary .wmat to a human-editable JSON sidecar (defaults to <base>.wmat.json)\n");
std::printf(" --import-wmat-json <json-path> [out-base]\n");
std::printf(" Import a .wmat.json sidecar back into binary .wmat (accepts materialKind/weightCategory int OR name; materialFlags int OR pipe-separated label string)\n");
feat(editor): add WPSP (Player Spawn Profile) open catalog format Open replacement for AzerothCore's playercreateinfo SQL table plus the per-class/race starting fields in CharStartOutfit.dbc. Defines the initial state for a newly created character: starting map / zone / position / facing, bind point (Hearthstone destination), up to 4 starting items with counts, and up to 4 starting spells. One entry per (race, class) combination — a Human Warrior spawns at Northshire Abbey with a Worn Shortsword and Heroic Strike already learned, while an Orc Hunter spawns in Valley of Trials with Aimed Shot and a starter rifle. Death Knights have their own preset spawning at lvl 55 in Acherus, the Ebon Hold. The race+class fields are bitmasks (mirroring WCHC layout) so one profile entry can cover multiple class/race combinations that share starting state. findByRaceClass(raceBit, classBit) is the engine helper used by character creation. Cross-references back to WCHC (race/class bit layouts), WMS (map ids), WIT (starting item ids), and WSPL (starting spell ids). Three preset emitters: --gen-psp (5 Alliance combos covering each starting zone from Northshire to Ammen Vale), --gen-psp-horde (5 Horde combos from Valley of Trials to Sunstrider Isle), --gen-psp-dk (2 DK combos at lvl 55 in Acherus with Death Coil / Plague Strike / Death Grip starter loadout). Validation enforces id+name+race+class+startingLevel presence, no duplicate ids; warns on (0,0,0) spawn (uninitialized entry), item id/count mismatch (granted item without count or vice versa), startingLevel > 80 (above WotLK cap), and Death Knight class with startingLevel < 55 (DKs canonically start at 55). Wired through the cross-format table; WPSP appears automatically in all 12 cross-format utilities. Format count 76 -> 77; CLI flag count 951 -> 956.
2026-05-09 22:22:57 -07:00
std::printf(" --gen-psp <wpsp-base> [name]\n");
std::printf(" Emit .wpsp 5 Alliance starting profiles (Human Warrior / Dwarf Hunter / NightElf Druid / Gnome Mage / Draenei Shaman) with race+class spawn coords\n");
std::printf(" --gen-psp-horde <wpsp-base> [name]\n");
std::printf(" Emit .wpsp 5 Horde starting profiles (Orc Warrior / Tauren Druid / Undead Mage / Troll Hunter / BloodElf Priest)\n");
std::printf(" --gen-psp-dk <wpsp-base> [name]\n");
std::printf(" Emit .wpsp 2 Death Knight profiles (Alliance Human / Horde Orc) starting at lvl 55 in Acherus with DK starter spell loadout\n");
std::printf(" --info-wpsp <wpsp-base> [--json]\n");
std::printf(" Print WPSP entries (id / raceMask / classMask / map / zone / startingLevel / spawn coords / name)\n");
std::printf(" --validate-wpsp <wpsp-base> [--json]\n");
std::printf(" Static checks: id+name+race+class+startingLevel required, no duplicate ids; warns on (0,0,0) spawn, item id/count mismatch, lvl>80, DK not at 55\n");
std::printf(" --export-wpsp-json <wpsp-base> [out.json]\n");
std::printf(" Export binary .wpsp to a human-editable JSON sidecar (defaults to <base>.wpsp.json)\n");
std::printf(" --import-wpsp-json <json-path> [out-base]\n");
std::printf(" Import a .wpsp.json sidecar back into binary .wpsp (all per-entry fields preserved verbatim including spawn/bind coords and item/spell ids)\n");
feat(editor): add WTLE (Talent Tab) open catalog format Open replacement for Blizzard's TalentTab.dbc plus the per-tab fields in Spell.dbc / Talent.dbc. Defines the three talent trees that each class has — Warrior: Arms / Fury / Protection; Mage: Arcane / Fire / Frost; Paladin: Holy / Protection / Retribution; etc. Each tab carries its own name, role hint (DPS / Tank / Healer / Hybrid / PetClass), display order in the talent UI, background artwork path (e.g. "WarriorArms" for the parchment background), icon path, and the class bitmask it belongs to. Distinct from WTAL (which defines individual talent points) — WTLE says "the Arms tree exists for Warriors, displays in tab 1, is a DPS spec"; WTAL says "Mortal Strike is a 1-point talent in the Arms tree, row 7, requires Improved Charge as a prerequisite". Cross-references back to WCHC (classMask uses the same bit layout) and forward to WTAL (talent entries reference tabId here). findByClass(classBit) returns all tabs for a class sorted by displayOrder — the talent UI uses this directly to populate its tab buttons. Three preset emitters: --gen-tle (Warrior 3 tabs with two DPS + one Tank), --gen-tle-mage (Mage 3 DPS tabs), --gen-tle-paladin (Paladin 3 tabs covering all three roles in one preset). Validation enforces id+name+classMask presence (classMask=0 means no class can use the tab — usually a config bug), roleHint 0..4, no duplicate ids; warns on empty iconPath (missing-texture render), empty backgroundFile (no panel art), displayOrder>3 (UI shows at most 4 tabs), and (classMask + displayOrder) collisions for overlapping classes (two tabs claiming the same UI slot for the same class). Wired through the cross-format table; WTLE appears automatically in all 12 cross-format utilities. Format count 77 -> 78; CLI flag count 958 -> 963.
2026-05-09 22:27:18 -07:00
std::printf(" --gen-tle <wtle-base> [name]\n");
std::printf(" Emit .wtle Warrior tabs: 3 trees (Arms DPS / Fury DPS / Protection Tank) with canonical icon and background art paths\n");
std::printf(" --gen-tle-mage <wtle-base> [name]\n");
std::printf(" Emit .wtle Mage tabs: 3 trees (Arcane / Fire / Frost), all DPS, with mage-blue icon color\n");
std::printf(" --gen-tle-paladin <wtle-base> [name]\n");
std::printf(" Emit .wtle Paladin tabs: 3 trees covering all 3 roles (Holy Healer / Protection Tank / Retribution DPS)\n");
std::printf(" --info-wtle <wtle-base> [--json]\n");
std::printf(" Print WTLE entries (id / classMask / displayOrder / role / name / backgroundFile)\n");
std::printf(" --validate-wtle <wtle-base> [--json]\n");
std::printf(" Static checks: id+name+classMask required, roleHint 0..4, no duplicate ids; warns on empty icon/background, displayOrder>3, and (classMask+order) UI position collisions\n");
std::printf(" --export-wtle-json <wtle-base> [out.json]\n");
std::printf(" Export binary .wtle to a human-editable JSON sidecar (defaults to <base>.wtle.json)\n");
std::printf(" --import-wtle-json <json-path> [out-base]\n");
std::printf(" Import a .wtle.json sidecar back into binary .wtle (accepts roleHint int OR roleHintName string)\n");
feat(editor): add WCTR (Currency Type) open catalog format Open replacement for Blizzard's CurrencyTypes.dbc plus the per-currency cap tables in CurrencyCategory.dbc. Defines the in-game currencies that are NOT regular item stacks: Honor Points, Arena Points, Justice Points, Valor Points, Conquest Points, plus the various faction tokens (Champion's Seal, Wintergrasp Mark of Honor, Emblem of Frost). Distinct from regular items in WIT — currencies are tracked per-character as scalar quantities with weekly + absolute caps, not as stackable inventory slots. Some currencies are still backed by a WIT item entry for the icon and tooltip text (itemId field), while others (Honor, Arena) live entirely in the currency system. The cap model captures both shapes: - maxQuantity = absolute lifetime cap (Honor Points 75k) - maxQuantityWeekly = weekly earn cap, no absolute cap (Conquest Points 1650/wk) - both 0 = uncapped (faction tokens, Emblem of Frost) earnableNow(id, current, weekly) is the engine helper that returns the smaller of (remaining absolute, remaining weekly). Cross-references back to WIT (itemId for tooltip art) and WFAC (categoryId references factionId for FactionToken kind — the rep gate that lets you spend the token). Three preset emitters: --gen-ctr (4 PvP currencies covering absolute, weekly-only, and uncapped tiers), --gen-ctr-pve (4 PvE raid currencies with same cap variety), --gen-ctr-faction (4 faction tokens with their categoryId pointing at WFAC faction ids). Validation enforces id+name presence, currencyKind 0..5, no duplicate ids; warns on: - maxQuantityWeekly > maxQuantity (weekly cap will never be reached, absolute cap blocks first) - FactionToken kind with categoryId=0 (rep gate breaks) - no caps + no itemId + no iconPath (currency has no display data and unbounded earn rate) Wired through the cross-format table; WCTR appears automatically in all 12 cross-format utilities. Format count 78 -> 79; CLI flag count 965 -> 970.
2026-05-09 22:31:42 -07:00
std::printf(" --gen-ctr <wctr-base> [name]\n");
std::printf(" Emit .wctr 4 PvP currencies (Honor 75k cap / Arena 5k weekly / Conquest 1650 weekly / Champion's Seal no cap)\n");
std::printf(" --gen-ctr-pve <wctr-base> [name]\n");
std::printf(" Emit .wctr 4 PvE raid currencies (Justice 4k cap / Valor 1k weekly / Emblem of Frost no cap / Trophy of Crusade no cap)\n");
std::printf(" --gen-ctr-faction <wctr-base> [name]\n");
std::printf(" Emit .wctr 4 faction reputation tokens (Hodir / Cenarion / Argent Dawn / Wintergrasp) — categoryId references WFAC factionId\n");
std::printf(" --info-wctr <wctr-base> [--json]\n");
std::printf(" Print WCTR entries (id / kind / itemId / max+weekly caps / categoryId / accountWide / name)\n");
std::printf(" --validate-wctr <wctr-base> [--json]\n");
std::printf(" Static checks: id+name required, currencyKind 0..5, no duplicate ids; warns on weekly>absolute, FactionToken+cat=0, no caps+no item+no icon\n");
std::printf(" --export-wctr-json <wctr-base> [out.json]\n");
std::printf(" Export binary .wctr to a human-editable JSON sidecar (defaults to <base>.wctr.json)\n");
std::printf(" --import-wctr-json <json-path> [out-base]\n");
std::printf(" Import a .wctr.json sidecar back into binary .wctr (accepts currencyKind int OR currencyKindName string; isAccountWide bool OR int)\n");
feat(editor): add WSPR (Spell Reagent) — 80th open format milestone Open replacement for the per-spell reagent fields in Spell.dbc (Reagent[8] + ReagentCount[8]). Defines the item reagents that a spell consumes from the caster's inventory each time it's cast — Mage Portal needs a Rune of Portals, Resurrection needs a Holy Candle (focused, not consumed), Warlock summons consume Soul Shards. One entry per reagent-using spell — most spells have no reagents and are absent from this catalog. Each entry can list up to 8 (itemId, count) pairs which all must be present for the spell to cast. Five reagentKind values capture the variety of reagent semantics: - Standard — ordinary consumed reagent - SoulShard — warlock-specific shard tracking - FocusedItem — required to cast but NOT consumed (Symbol of Divinity for Resurrection) - Catalyst — enables a stronger version of the spell - Tradeable — crafting reagent for trade-skill recipes Cross-references back to WSPL (every entry references a spellId) and WIT (every reagent itemId references an item entry). findBySpell(spellId) is the primary engine lookup. Three preset emitters: --gen-spr (4 mage portal/teleport reagents using Rune of Teleportation 17031), --gen-spr-warlock (4 demon summons each consuming 1 Soul Shard 6265), --gen-spr-rez (3 resurrection variants demonstrating each ReagentKind including a no-reagent Druid Rebirth and a focused-item Priest Resurrection). Validation enforces id+name+spellId presence, reagentKind 0..4, no duplicate ids; warns on: - slot itemId/count mismatch (id without count or vice versa) - SoulShard kind with non-canonical reagent (not item 6265) - FocusedItem kind with no reagent slots set (focused-item gating has nothing to gate) - duplicate spellId across entries (engine honors only first) This is the 80th open format milestone. Wired through the cross-format table; WSPR appears automatically in all 14 cross-format utilities. Format count 79 -> 80; CLI flag count 974 -> 979.
2026-05-09 22:38:36 -07:00
std::printf(" --gen-spr <wspr-base> [name]\n");
std::printf(" Emit .wspr 4 mage portal/teleport reagents (Stormwind/Ironforge/Darnassus/Theramore) consuming Rune of Teleportation\n");
std::printf(" --gen-spr-warlock <wspr-base> [name]\n");
std::printf(" Emit .wspr 4 warlock summons (Imp / Voidwalker / Succubus / Felhunter) each consuming 1 Soul Shard\n");
std::printf(" --gen-spr-rez <wspr-base> [name]\n");
std::printf(" Emit .wspr 3 resurrection variants (Shaman Reincarnation / Priest Resurrection focused / Druid Rebirth no-cost) demonstrating each ReagentKind\n");
std::printf(" --info-wspr <wspr-base> [--json]\n");
std::printf(" Print WSPR entries (id / spellId / kind / used slot count / item x count list / name)\n");
std::printf(" --validate-wspr <wspr-base> [--json]\n");
std::printf(" Static checks: id+name+spellId required, reagentKind 0..4, no duplicate ids; warns on slot id/count mismatch, SoulShard kind with non-canonical reagent, FocusedItem kind with no slots, duplicate spellId\n");
std::printf(" --export-wspr-json <wspr-base> [out.json]\n");
std::printf(" Export binary .wspr to a human-editable JSON sidecar (defaults to <base>.wspr.json). Reagent slot arrays exported as JSON arrays\n");
std::printf(" --import-wspr-json <json-path> [out-base]\n");
std::printf(" Import a .wspr.json sidecar back into binary .wspr (accepts reagentKind int OR reagentKindName string; reagent arrays pad with zeros if shorter than 8)\n");
feat(editor): add WACR (Achievement Criteria) open catalog format Open replacement for Blizzard's Achievement_Criteria.dbc. Defines the individual progression criteria that a character must complete to earn an achievement. Each WACH achievement has a tree of WACR criteria — "Kill 100 boars" is one criteria entry with criteriaType=KillCreature, targetId=boarCreatureId, requiredCount=100. Multi-criteria achievements (e.g. "Visit all 3 capital cities") have one entry per sub-objective, all referencing the same achievementId, with progressOrder determining their display sequence in the achievement UI. Thirteen criteriaType values cover the full progression variety: KillCreature / ReachLevel / CompleteQuest / EarnGold / GainHonor / EarnReputation / ExploreZone / LootItem / UseItem / CastSpell / PvPKill / DungeonRun / Misc The targetId field is type-polymorphic — for KillCreature it references WCRT.creatureId, for CompleteQuest it references WQT.questId, for ExploreZone it's a WMS.zoneId, etc. The engine interprets it based on criteriaType. Cross-references back to WACH (achievementId), WCRT (KillCreature.targetId), WQT (CompleteQuest.targetId), WIT (LootItem/UseItem.targetId), WMS (ExploreZone.targetId), WSPL (CastSpell.targetId). findByAchievement(achId) returns all criteria for an achievement sorted by progressOrder — used directly by the achievement UI to render the progress checklist. Three preset emitters: --gen-acr (5 kill criteria under one composite achievement showing different creature targets), --gen-acr-quest (4-step quest progression), --gen-acr-mixed (5 cross-type criteria demonstrating the full CriteriaType variety). Validation enforces id+name+achievementId presence, criteriaType 0..12, no duplicate ids; warns on: - missing targetId for type-specific kinds (KillCreature, CompleteQuest, etc. — engine cannot track without it) - ReachLevel with requiredCount > 80 (above WotLK cap) - timeLimitMs set on non-time-sensitive types (engine ignores it for ReachLevel / EarnGold) - requiredCount=0 (criteria completes instantly on first progress event — usually a misconfig) Wired through the cross-format table; WACR appears automatically in all 14 cross-format utilities. Format count 80 -> 81; CLI flag count 981 -> 986.
2026-05-09 22:43:44 -07:00
std::printf(" --gen-acr <wacr-base> [name]\n");
std::printf(" Emit .wacr 5 kill-counting criteria (Defias 50 / Murloc 25 / Naga 100 / Dragon 1 / RareElite 1) under one composite achievement\n");
std::printf(" --gen-acr-quest <wacr-base> [name]\n");
std::printf(" Emit .wacr 4 quest-completion criteria (tutorial / starting zone / daily / escort) progression chain\n");
std::printf(" --gen-acr-mixed <wacr-base> [name]\n");
std::printf(" Emit .wacr 5 cross-type criteria (ReachLevel 80 / EarnGold 10k / GainHonor 5k / PvPKill 100 / ExploreZone Stormwind) showing CriteriaType variety\n");
std::printf(" --info-wacr <wacr-base> [--json]\n");
std::printf(" Print WACR entries (id / achievementId / type / targetId / requiredCount / timeLimitMs / progressOrder / name)\n");
std::printf(" --validate-wacr <wacr-base> [--json]\n");
std::printf(" Static checks: id+name+achievementId required, criteriaType 0..12, no duplicate ids; warns on missing targetId for type-specific kinds, ReachLevel>80, timeLimit on non-timed types, requiredCount=0\n");
std::printf(" --export-wacr-json <wacr-base> [out.json]\n");
std::printf(" Export binary .wacr to a human-editable JSON sidecar (defaults to <base>.wacr.json)\n");
std::printf(" --import-wacr-json <json-path> [out-base]\n");
std::printf(" Import a .wacr.json sidecar back into binary .wacr (accepts criteriaType int 0..12 OR criteriaTypeName string)\n");
feat(editor): add WSEF (Spell Effect Type) open catalog format Open replacement for the SpellEffect.Effect field meanings in Spell.dbc plus the engine's hard-coded effect dispatch table. Defines what each spell-effect integer value actually does — SCHOOL_DAMAGE=2 deals magical damage, DUMMY=3 is a script hook, HEAL=10 restores health, ENERGIZE=30 restores power, APPLY_AURA=6 attaches a buff/debuff, etc. WotLK's Spell.dbc has 192+ effect type integers, each with its own resolver in the spell engine. This catalog lets the engine look up "given effect=10, what resolution behavior do I run?" via a single table lookup instead of a hard-coded switch statement, and lets server-custom spells reference new effect IDs without touching engine code. Ten effectKind values capture the major behavior families (Damage / Heal / Aura / Energize / Trigger / Movement / Summon / Dispel / Dummy / Misc), and a 6-bit behaviorFlags field captures targeting/gating semantics: - RequiresTarget — must have a target - RequiresLineOfSight — LoS check on target - IsHostileEffect — hostile only (PvP gating) - IsBeneficialEffect — friendly only - IgnoresImmunities — bypasses Bubble / IBF / etc - TriggersGCD — counts toward GCD Distinct from WAUR (Spell Aura Type, future format) which is the secondary classification used when effectType is APPLY_AURA. The two together cover the full spell-effect classification space. Three preset emitters: --gen-sef (5 damage effects covering typical Spell.dbc damage IDs), --gen-sef-healing (4 heal effects all flagged IsBeneficialEffect), --gen-sef-aura (5 aura-application effects covering single-target / pet / party-wide / area variants). Validation enforces name presence, effectKind 0..9, no duplicate ids; warns on: - both Hostile and Beneficial flags set (engine picks Hostile, contradiction suggests config bug) - Damage kind without TriggersGCD (most damage should be on GCD — env damage is the canonical exception) - Heal kind without IsBeneficialEffect (engine treats heal as ungated, may damage enemies) Wired through the cross-format table; WSEF appears automatically in all 15 cross-format utilities. Format count 81 -> 82; CLI flag count 989 -> 994.
2026-05-09 22:50:13 -07:00
std::printf(" --gen-sef <wsef-base> [name]\n");
std::printf(" Emit .wsef 5 damage effect entries (SchoolDamage / EnvironmentalDamage / WeaponDamageNoSchool / NormalizedWeaponDmg / PowerBurn)\n");
std::printf(" --gen-sef-healing <wsef-base> [name]\n");
std::printf(" Emit .wsef 4 healing effects (Heal / HealMaxHealth / HealPct / ScriptedHeal) all flagged IsBeneficialEffect\n");
std::printf(" --gen-sef-aura <wsef-base> [name]\n");
std::printf(" Emit .wsef 5 aura-application effects (ApplyAura / ApplyAuraOnPet / AreaAuraParty / AreaAuraOwner / PersistentAreaAura)\n");
std::printf(" --info-wsef <wsef-base> [--json]\n");
std::printf(" Print WSEF entries (id / kind / baseAmount / behavior flags / name) — flags decoded as label list\n");
std::printf(" --validate-wsef <wsef-base> [--json]\n");
std::printf(" Static checks: name required, effectKind 0..9, no duplicate ids; warns on Hostile+Beneficial conflict, Damage without TriggersGCD, Heal without IsBeneficialEffect\n");
std::printf(" --export-wsef-json <wsef-base> [out.json]\n");
std::printf(" Export binary .wsef to a human-editable JSON sidecar (defaults to <base>.wsef.json)\n");
std::printf(" --import-wsef-json <json-path> [out-base]\n");
std::printf(" Import a .wsef.json sidecar back into binary .wsef (accepts effectKind int OR effectKindName string; behaviorFlags int OR pipe-separated label string)\n");
feat(editor): add WAUR (Spell Aura Type) — companion to WSEF, CLI flag count breaks 1000 Open replacement for the SpellEffect.EffectAuraType field meanings used when SpellEffect.Effect=APPLY_AURA. Defines what each aura-type integer value actually does once an aura is attached to a unit — PERIODIC_DAMAGE ticks damage every N seconds, MOD_STAT adds a stat bonus, MOD_INCREASE_SPEED scales movement, MOD_DAMAGE_PERCENT_DONE scales spell power, etc. Companion to WSEF — together they cover the full spell-effect classification space: WSEF: outer effect ID — what does the effect DO? (APPLY_AURA, SCHOOL_DAMAGE, HEAL, etc) WAUR: inner aura type — when WSEF=APPLY_AURA, what KIND of aura is applied? (PERIODIC_DAMAGE, MOD_STAT, STUN, ROOT, etc) Nine auraKind values (Periodic / StatMod / DamageMod / Movement / Visual / Trigger / Resource / Control / Misc) classify the major behavior families. Periodic auras carry an updateFrequencyMs (canonical 3s for DoT/HoT, 2s for energize, 1s for fast triggers). Stackable auras carry a maxStackCount. Cross-references back to WSEF (this catalog is the secondary classification that WSEF entry id 6 (APPLY_AURA) dispatches into) and forward to WSPL (spell entries with effect=APPLY_AURA reference an auraTypeId here). Three preset emitters: --gen-aur (5 periodic auras with canonical tick intervals), --gen-aur-stats (5 stat-modifier auras instantly applied on attach), --gen-aur-movement (4 movement-impairing CC auras typical of crowd-control spells). Validation enforces name presence, auraKind 0..8, targetingHint 0..3, no duplicate ids; errors on Periodic kind without updateFrequencyMs (would never tick); warns on: - non-Periodic/Trigger kinds with updateFrequencyMs > 0 (engine ignores tick interval) - maxStackCount > 0 with isStackable=false (cap unreachable) Wired through the cross-format table; WAUR appears automatically in all 15 cross-format utilities. Format count 82 -> 83; CLI flag count 996 -> 1001 — broke the 1000-flag mark.
2026-05-09 22:54:53 -07:00
std::printf(" --gen-aur <waur-base> [name]\n");
std::printf(" Emit .waur 5 periodic auras (PeriodicDamage / PeriodicHeal / PeriodicEnergize / PeriodicLeech / PeriodicTriggerSpell) at canonical 3s tick\n");
std::printf(" --gen-aur-stats <waur-base> [name]\n");
std::printf(" Emit .waur 5 stat-modifier auras (ModStat / ModResistance / ModDamageDone / ModHaste / ModCritPercent) — non-periodic, instantly applied\n");
std::printf(" --gen-aur-movement <waur-base> [name]\n");
std::printf(" Emit .waur 4 movement-impairing auras (Stun / ModDecreaseSpeed / ModConfuse / Root) typically applied by CC spells\n");
std::printf(" --info-waur <waur-base> [--json]\n");
std::printf(" Print WAUR entries (id / kind / targeting / stack flag / max stack / tick interval / name)\n");
std::printf(" --validate-waur <waur-base> [--json]\n");
std::printf(" Static checks: name required, auraKind 0..8, targetingHint 0..3, no duplicate ids; errors on Periodic without tick interval; warns on non-periodic with tick interval, maxStackCount without isStackable\n");
std::printf(" --export-waur-json <waur-base> [out.json]\n");
std::printf(" Export binary .waur to a human-editable JSON sidecar (defaults to <base>.waur.json)\n");
std::printf(" --import-waur-json <json-path> [out-base]\n");
std::printf(" Import a .waur.json sidecar back into binary .waur (accepts auraKind/targetingHint int OR name; isStackable bool OR int)\n");
feat(editor): add WIQR (Item Quality) open catalog format Open replacement for the hardcoded item quality tiers in the WoW client (Poor / Common / Uncommon / Rare / Epic / Legendary / Artifact / Heirloom). Defines each tier's tooltip text color, inventory slot border color, vendor price multiplier, drop-level gating, and disenchant eligibility. The hardcoded client uses a fixed color table (gray/white/green/ blue/purple/orange/red/gold). This catalog lets server admins: - retune the colors (rename "Epic" to "Tier 1" with custom hex) - add server-custom tiers above Heirloom - change vendor markup per tier (legendary 50x base price) - gate quality drops by character level (Heirlooms unlock 80) The standard preset reproduces the canonical 8-tier scale with exact hex values from the live client (#9d9d9d through #00ccff) and standard disenchant rules (Common+ disenchantable, Legendary and Artifact aren't). The server-custom preset shows 4 tiers above the standard range with non-standard pricing (Junk 0.1x, QuestLocked 0.0x unsellable). The raid preset gates 4 progression tiers behind minLevelToDrop=60 with escalating vendor multipliers up to 50x for Legendary. Cross-references back to WIT — item entries reference qualityId here for tooltip color and sort order. canDropAtLevel(id, lvl) is the engine helper used by loot generation. Validation enforces name presence, no duplicate ids, vendorPriceMultiplier >= 0, minLevelToDrop <= maxLevelToDrop; warns on: - minLevelToDrop > 80 (unreachable at WotLK cap) - vendorPriceMultiplier > 100x (sanity check the economy) - nameColorRGBA with alpha=0 (text would be invisible in tooltips — common bug when copy-pasting RGB hex without alpha byte) Wired through the cross-format table; WIQR appears automatically in all 15 cross-format utilities. Format count 83 -> 84; CLI flag count 1003 -> 1008.
2026-05-09 22:59:27 -07:00
std::printf(" --gen-iqr <wiqr-base> [name]\n");
std::printf(" Emit .wiqr 8 standard quality tiers (Poor through Heirloom) with canonical hex colors and disenchant rules\n");
std::printf(" --gen-iqr-server <wiqr-base> [name]\n");
std::printf(" Emit .wiqr 4 server-custom tiers (Junk / Weekly / QuestLocked / Donator) with custom colors and non-standard markups\n");
std::printf(" --gen-iqr-raid <wiqr-base> [name]\n");
std::printf(" Emit .wiqr 4 raid progression tiers (T1/T2/T3/Legendary lvl 60+) gated by minLevelToDrop and priced for server economy\n");
std::printf(" --info-wiqr <wiqr-base> [--json]\n");
std::printf(" Print WIQR entries (id / name / nameColor RGBA / vendor multiplier / minLevel / maxLevel / disenchant flag / border texture)\n");
std::printf(" --validate-wiqr <wiqr-base> [--json]\n");
std::printf(" Static checks: name required, no duplicate ids, vendor>=0, min<=max; warns on lvl>80 (unreachable), vendor>100x (sanity), alpha=0 nameColor (invisible)\n");
std::printf(" --export-wiqr-json <wiqr-base> [out.json]\n");
std::printf(" Export binary .wiqr to a human-editable JSON sidecar (defaults to <base>.wiqr.json). Colors as RGBA uint32, easy to paste hex values\n");
std::printf(" --import-wiqr-json <json-path> [out-base]\n");
std::printf(" Import a .wiqr.json sidecar back into binary .wiqr (canBeDisenchanted accepts bool OR int)\n");
feat(editor): add WSCS (Skill Cost) open catalog format Open replacement for Blizzard's SkillCostsData.dbc plus the per-rank training cost tables. Defines the tiered progression of trainable skills: each rank unlocks a skill range, requires a minimum character level, and costs a fixed amount of gold to learn. The canonical 6-tier profession progression captured by the default preset: Apprentice skill 0-75 lvl 5 1s Journeyman skill 50-150 lvl 10 5s Expert skill 125-225 lvl 20 1g Artisan skill 200-300 lvl 35 5g Master skill 275-375 lvl 50 10g Grand Master skill 350-450 lvl 65 25g Same shape applies to weapon skills (free, level-gated, capped at 5x char level) and riding skills (canonical Vanilla / TBC / WotLK gold costs from 90g Apprentice through 5000g Artisan flying down to 1000g Cold Weather Flying). Five costKind values cover the full training-skill space (Profession / WeaponSkill / RidingSkill / ClassSkill / Misc). Each entry's copperCost stores the cost in copper (1g = 10000c) which the info renderer pretty-prints as "25g 0s 0c". Cross-references back to WSKL — skill entries reference costId here for the tiered training schedule. nextTrainable(currentSkill, characterLevel) is the engine helper that returns the lowest-rank tier a character qualifies for and hasn't capped yet — used by trainer NPCs to populate their offered-skill list. Three preset emitters: --gen-scs (6 profession tiers), --gen-scs- weapon (5 weapon skill tiers), --gen-scs-riding (5 riding tiers with canonical gold costs). Validation enforces id+name presence, costKind 0..4, no duplicate ids, min<max range; warns on: - requiredLevel > 80 (unreachable at WotLK cap) - RidingSkill with requiredLevel < 20 (Apprentice canonically unlocks at 20) - Profession kind with copperCost=0 (every standard tier costs at least a copper — usually a config bug) Wired through the cross-format table; WSCS appears automatically in all 15 cross-format utilities. Format count 84 -> 85; CLI flag count 1010 -> 1015.
2026-05-09 23:04:02 -07:00
std::printf(" --gen-scs <wscs-base> [name]\n");
std::printf(" Emit .wscs 6 canonical profession tiers (Apprentice/Journeyman/Expert/Artisan/Master/GrandMaster) with standard skill ranges and gold costs\n");
std::printf(" --gen-scs-weapon <wscs-base> [name]\n");
std::printf(" Emit .wscs 5 weapon skill tiers (Beginner/Trained/Skilled/Expert/Master) — free to train, level-gated, capped at 5x char level\n");
std::printf(" --gen-scs-riding <wscs-base> [name]\n");
std::printf(" Emit .wscs 5 riding skill tiers (Apprentice 60%% / Journeyman 100%% / Expert 150%% / Artisan 280%% / Cold Weather Flying) with canonical Vanilla/TBC/WotLK gold costs\n");
std::printf(" --info-wscs <wscs-base> [--json]\n");
std::printf(" Print WSCS entries (id / rank / kind / minSkill / maxSkill / required level / gold cost / name)\n");
std::printf(" --validate-wscs <wscs-base> [--json]\n");
std::printf(" Static checks: id+name required, costKind 0..4, no duplicate ids, min<max; warns on lvl>80, RidingSkill below lvl 20, Profession with cost=0\n");
std::printf(" --export-wscs-json <wscs-base> [out.json]\n");
std::printf(" Export binary .wscs to a human-editable JSON sidecar (defaults to <base>.wscs.json)\n");
std::printf(" --import-wscs-json <json-path> [out-base]\n");
std::printf(" Import a .wscs.json sidecar back into binary .wscs (accepts costKind int OR costKindName string)\n");
feat(editor): add WIFS (Item Flag Set) open catalog format Open replacement for the bit-flag meanings used in Item.dbc / item_template.Flags. Documents every individual bit of the 32-bit item flags field with a human-readable name, description, kind classification, and is-positive hint. WoW's Item.dbc Flags field packs ~25 bits of metadata like Heroic, Lootable, NoLoot, Conjured, BindOnPickup, BindOnEquip — each controlling a specific gameplay behavior. The hardcoded client knows what each bit means via a switch statement; this catalog exposes that table to data-driven editors so: - server admins can document custom flag bits - tooltip generators can decode "why is this item soulbound?" via flag-name lookup (decode(0x40240000) returns ["Heroic", "BindOnPickup", "Unique"]) - validators can warn about contradictory flag combinations Seven flagKind values classify the bit families (Quality / Drop / Trade / Magic / Account / Server / Misc), and an isPositive hint tells UIs whether the flag enhances the item (green) or restricts it (red). Cross-references back to WIT (decodes WIT.flags into the matching named flag list) and WIQR (validators can pair Heroic flag with WIQR Epic+ quality requirement). Three preset emitters: --gen-ifs (8 canonical Item.dbc bits matching the standard 3.3.5a constants), --gen-ifs-binding (5 binding-related flags BindOnPickup / BindOnEquip / etc — all restrictive so isPositive=0), --gen-ifs-server (5 server-custom bits in the upper range demonstrating how to overlay extra metadata without colliding with Blizzard's bits). Validation enforces id+name+bitMask presence, flagKind 0..6, no duplicate ids, no duplicate bitMasks (collision means engine would only honor first matching name when decoding); warns on multi-bit masks (unusual — usually want individual bits). decode(flagsValue) is the engine helper that expands a raw flags integer into its named flag list — used directly by the tooltip generator and item info renderers. Wired through the cross-format table; WIFS appears automatically in all 16 cross-format utilities. Format count 85 -> 86; CLI flag count 1018 -> 1023.
2026-05-09 23:10:35 -07:00
std::printf(" --gen-ifs <wifs-base> [name]\n");
std::printf(" Emit .wifs 8 canonical Item.dbc flag bits (NoLoot / Conjured / Lootable / Wrapped / Heroic / Deprecated / NoUserDestroy / NoEquipCooldown)\n");
std::printf(" --gen-ifs-binding <wifs-base> [name]\n");
std::printf(" Emit .wifs 5 binding-related flags (BindOnPickup / BindOnEquip / BindOnUse / BindToAccount / Soulbound) — all isPositive=0 (restrict trading)\n");
std::printf(" --gen-ifs-server <wifs-base> [name]\n");
std::printf(" Emit .wifs 5 server-custom flag bits in upper range (Donator / EventReward / Anniversary / Honored / Heroic25man)\n");
std::printf(" --info-wifs <wifs-base> [--json]\n");
std::printf(" Print WIFS entries (id / bitMask hex / kind / +/- positivity / name) — handy for decoding raw item.flags integers\n");
std::printf(" --validate-wifs <wifs-base> [--json]\n");
std::printf(" Static checks: id+name+bitMask required, flagKind 0..6, no duplicate ids, no duplicate bitMasks (collision); warns on multi-bit masks (unusual, usually want a single bit)\n");
std::printf(" --export-wifs-json <wifs-base> [out.json]\n");
std::printf(" Export binary .wifs to a human-editable JSON sidecar (defaults to <base>.wifs.json)\n");
std::printf(" --import-wifs-json <json-path> [out-base]\n");
std::printf(" Import a .wifs.json sidecar back into binary .wifs (accepts flagKind int OR name; isPositive bool OR int)\n");
feat(editor): add WBKD (NPC Service Definition) open catalog format Open replacement for AzerothCore's npc_vendor / npc_trainer / npc_gossip / npc_options SQL tables plus the engine's hard-coded service-type dispatch. Defines the kinds of services NPCs can offer (Banker / Mailbox / Auctioneer / StableMaster / FlightMaster / Trainer / Innkeeper / Battlemaster / GuildBanker / ReagentVendor / TabardVendor / Misc) and the per-service metadata (gold cost, faction gating, gossip text id). When a player right-clicks an NPC, the engine looks at the NPC's serviceId list (from WCRT.npcFlags or equivalent) and dispatches to the appropriate service-frame handler — Banker opens the inventory expansion frame, Auctioneer opens the auction house, StableMaster opens the pet stable. This catalog defines what each service actually does and what preconditions it requires. Cross-references back to WCRT (creature.npcFlags decodes into a list of service ids defined here), WFAC (factionRequiredId references factionId for rep-gated services like Argent Tournament), and WGSP (gossipTextId references menuId for the "How can I help you?" dialogue line). Three preset emitters: --gen-bkd (5 city services typical of a capital city), --gen-bkd-battle (3 battlemaster services for each Vanilla BG queue), --gen-bkd-profession (4 profession services). findByKind(kind) is the engine helper used by NPC spawning to find e.g. all FlightMaster services configured for a server. Validation enforces id+name presence, serviceKind 0..11, no duplicate ids; warns on: - Mailbox kind with non-zero gossipTextId (mailboxes are gameobject services with no NPC dialogue; gossip won't show) - Innkeeper kind with gossipTextId=0 (no welcome/bind dialog; will silently bind the hearthstone) - Battlemaster kind with non-zero requiresGold (battle queue services are typically free) Wired through the cross-format table; WBKD appears automatically in all 16 cross-format utilities. Format count 86 -> 87; CLI flag count 1025 -> 1030.
2026-05-09 23:15:20 -07:00
std::printf(" --gen-bkd <wbkd-base> [name]\n");
std::printf(" Emit .wbkd 5 city services (Banker / Mailbox / Innkeeper / Auctioneer / FlightMaster) typical of capital city offerings\n");
std::printf(" --gen-bkd-battle <wbkd-base> [name]\n");
std::printf(" Emit .wbkd 3 battlemaster services (Alterac Valley / Warsong Gulch / Arathi Basin) for queueing into Vanilla BGs\n");
std::printf(" --gen-bkd-profession <wbkd-base> [name]\n");
std::printf(" Emit .wbkd 4 profession services (Blacksmith Trainer / Tailoring Trainer / Reagent Vendor / Stable Master)\n");
std::printf(" --info-wbkd <wbkd-base> [--json]\n");
std::printf(" Print WBKD entries (id / kind / gold cost / faction gate / gossip text id / name)\n");
std::printf(" --validate-wbkd <wbkd-base> [--json]\n");
std::printf(" Static checks: id+name required, serviceKind 0..11, no duplicate ids; warns on Mailbox+gossip (no NPC dialog), Innkeeper+no-gossip (silent bind), Battlemaster+gold (queues are free)\n");
std::printf(" --export-wbkd-json <wbkd-base> [out.json]\n");
std::printf(" Export binary .wbkd to a human-editable JSON sidecar (defaults to <base>.wbkd.json)\n");
std::printf(" --import-wbkd-json <json-path> [out-base]\n");
std::printf(" Import a .wbkd.json sidecar back into binary .wbkd (accepts serviceKind int OR serviceKindName string)\n");
feat(editor): add WTBR (Token Reward) open catalog format Open replacement for AzerothCore's currency_token_reward SQL table plus the per-vendor token redemption rows in npc_vendor. Each entry says "spend N copies of token X to receive reward Y", with reward type polymorphism: Y can be an item, a spell (taught to the character), a title, a mount, a companion pet, a currency conversion, an heirloom unlock, or a cosmetic (tabard / pennant / fluff). The rewardId field's interpretation depends on the rewardKind enum. Distinct from WTKN (Token catalog) which defines the token currency items themselves. WTKN says "the Champion's Seal exists as item 44990"; WTBR says "spend 25 Champion's Seals at Argent Tournament for the Squire's Belt (item 45517)". Eight rewardKind values cover the full reward space (Item / Spell / Title / Mount / Pet / Currency / Heirloom / Cosmetic), and an 8-tier requiredFactionStanding gates by reputation (Hated / Hostile / Unfriendly / Neutral / Friendly / Honored / Revered / Exalted) when paired with a non-zero requiredFactionId. Cross-references back to WTKN (spentTokenItemId), WIT (Item rewards), WSPL (Spell rewards), WTIT (Title rewards), WMOU (Mount rewards), WCMP (Pet rewards), WCTR (Currency conversion rewards), and WFAC (faction-rep gating). findByToken(itemId) is the engine helper used by vendor frames to populate the "what can I buy with these?" list. Three preset emitters: --gen-tbr (5 raid tier-token redemptions consuming Trophy of the Crusade and Emblem of Frost), --gen-tbr-pvp (5 PvP rewards spanning honor / arena / conquest plus title and tabard kinds), --gen-tbr-faction (5 faction- gated rewards demonstrating each standing tier from Honored through Exalted). Validation enforces id+name+spentTokenItemId+spentTokenCount presence, rewardKind 0..7, requiredFactionStanding 0..7, no duplicate ids; warns on: - rewardId=0 (no actual reward, vendor offers entry but grants nothing) - requiredFactionStanding > Neutral with requiredFactionId=0 (rep gate has no faction to check) - Currency conversion item -> itself (typo / config bug) Wired through the cross-format table; WTBR appears automatically in all 18 cross-format utilities. Format count 87 -> 88; CLI flag count 1034 -> 1039.
2026-05-09 23:26:13 -07:00
std::printf(" --gen-tbr <wtbr-base> [name]\n");
std::printf(" Emit .wtbr 5 raid tier-token redemptions (T9 Conqueror's helm / Vanquisher's chest / T10 Protector's legs / Trophy Gloves) consuming Trophy of the Crusade and Emblem of Frost\n");
std::printf(" --gen-tbr-pvp <wtbr-base> [name]\n");
std::printf(" Emit .wtbr 5 PvP token redemptions (BG mount / Arena weapon / Wrathful PvP helm / Combatant title / PvP tabard)\n");
std::printf(" --gen-tbr-faction <wtbr-base> [name]\n");
std::printf(" Emit .wtbr 5 faction-rep-gated rewards (Argent tabard @ Honored / Hodir mammoth @ Exalted / Cenarion ring @ Revered / Argent title @ Exalted / Wintergrasp pet @ Honored)\n");
std::printf(" --info-wtbr <wtbr-base> [--json]\n");
std::printf(" Print WTBR entries (id / spent token x count / reward kind / rewardId / faction@standing gate / name)\n");
std::printf(" --validate-wtbr <wtbr-base> [--json]\n");
std::printf(" Static checks: id+name+spentTokenItemId+count required, rewardKind 0..7, requiredFactionStanding 0..7, no duplicate ids; warns on rewardId=0 (no actual reward), standing>Neutral with factionId=0, Currency conversion item->itself\n");
std::printf(" --export-wtbr-json <wtbr-base> [out.json]\n");
std::printf(" Export binary .wtbr to a human-editable JSON sidecar (defaults to <base>.wtbr.json)\n");
std::printf(" --import-wtbr-json <json-path> [out-base]\n");
std::printf(" Import a .wtbr.json sidecar back into binary .wtbr (accepts rewardKind/requiredFactionStanding int OR name)\n");
feat(editor): add WSPS (Spell Proc Trigger) open catalog format Open replacement for AzerothCore's spell_proc_event SQL table plus the per-spell proc fields embedded in Spell.dbc. Defines when a "trigger" spell fires in response to other spell/combat events: Windfury Weapon procs on melee attack, Clearcasting on damaging cast, Judgement of Wisdom on melee hit, etc. Each entry says "when an event matching procFlags fires from a spell matching procFromSpellId (0 = any), at procChance probability with at most one trigger per internalCooldownMs window, fire triggerSpellId". The procPpm field provides an alternative procs-per-minute formula (when non-zero, supersedes procChance and scales with weapon speed for melee procs — the canonical WoW behavior for weapon imbues). 13-bit procFlags bitfield covers the standard event taxonomy: DealtMeleeAutoAttack / DealtMeleeSpell / TakenMeleeAutoAttack / TakenMeleeSpell / DealtRangedAutoAttack / DealtRangedSpell / DealtSpell / DealtSpellHeal / TakenSpell / OnKill / OnDeath / OnCastFinished / Critical (the last is a modifier — fires only on crit-tagged events). Cross-references back to WSPL (triggerSpellId references the spell that fires; procFromSpellId optionally restricts to a specific source spell). Three preset emitters: --gen-sps (4 weapon-imbue procs with canonical PPM rates and ICDs), --gen-sps-aura (4 aura-tied procs across multiple proc-flag combinations), --gen-sps-talent (4 talent procs including charge-consuming Clearcasting and Nightfall variants). Validation enforces id+name+triggerSpellId+procFlags presence, no duplicate ids; warns on: - procChance outside [0..1] (engine clamps) - procPpm < 0 (invalid PPM rate) - both procChance > 0 AND procPpm > 0 set (engine prefers PPM so procChance is silently ignored) - both procChance=0 AND procPpm=0 (proc never fires) Wired through the cross-format table; WSPS appears automatically in all 18 cross-format utilities. Format count 88 -> 89; CLI flag count 1041 -> 1046.
2026-05-09 23:32:25 -07:00
std::printf(" --gen-sps <wsps-base> [name]\n");
std::printf(" Emit .wsps 4 weapon-imbue procs (Windfury 20PPM+3sICD / Frostbrand 9PPM / Flametongue 15PPM / Mana Oil 4PPM+5sICD) on DealtMeleeAutoAttack\n");
std::printf(" --gen-sps-aura <wsps-base> [name]\n");
std::printf(" Emit .wsps 4 aura-tied procs (Blessing of Wisdom mana / Molten Armor reflect / Earth Shield heal / Judgement of Wisdom)\n");
std::printf(" --gen-sps-talent <wsps-base> [name]\n");
std::printf(" Emit .wsps 4 talent procs (Clearcasting / Omen of Clarity / Seal of Righteousness / Nightfall) with charge-consuming variants\n");
std::printf(" --info-wsps <wsps-base> [--json]\n");
std::printf(" Print WSPS entries (id / triggerSpellId / fromSpellId / chance%% / PPM / ICD / charges / proc flags)\n");
std::printf(" --validate-wsps <wsps-base> [--json]\n");
std::printf(" Static checks: id+name+triggerSpellId+procFlags required, no duplicate ids; warns on chance outside [0,1], procPpm<0, both chance+ppm set (PPM wins), neither set (never fires)\n");
std::printf(" --export-wsps-json <wsps-base> [out.json]\n");
std::printf(" Export binary .wsps to a human-editable JSON sidecar (defaults to <base>.wsps.json)\n");
std::printf(" --import-wsps-json <json-path> [out-base]\n");
std::printf(" Import a .wsps.json sidecar back into binary .wsps (procFlags accepts int OR pipe-separated label string)\n");
feat(editor): add WCMR (Creature Patrol Path) — 90th open format milestone Open replacement for AzerothCore's creature_movement / waypoints SQL tables plus the per-spawn waypoint arrays. Defines named waypoint paths that creatures patrol along: Stormwind guards walking the city perimeter, AQ40 trash rotating through the chamber, ICC patrols circling the spire. Each entry binds a creatureGuid to a sequence of (x, y, z, delayMs) waypoints. The pathKind controls cycling behavior (Loop / OneShot / Reverse / Random) and moveType controls the locomotion kind (Walk / Run / Fly / Swim) — a flying patrol ignores ground geometry, a swimming patrol stays underwater. This is the first open format with truly variable-length per-entry payload. Earlier formats with multi-slot fields (WSPR's 8-reagent slots, WPSP's 4-item arrays) used fixed-size caps padded with zeros. WCMR instead uses an inline length-prefixed waypoint array — entries can be 4 waypoints or 4000, with the loader advancing through the file by reading the count first then count*16 bytes of waypoint data. Cap of 64K waypoints per path keeps a corrupted file from allocating gigabytes. pathLengthYards(pathId) is the engine helper that sums segment distances between consecutive waypoints (closing the loop for Loop kind). Tested across 12-point and 16-point circular paths that geometrically resolve to the expected ~25y radius and ~60y radius totals. Cross-references back to WCRT — creatureGuid points at the spawned creature instance whose behavior mode follows this patrol. Three preset emitters: --gen-cmr (3 small paths showing each pathKind variant), --gen-cmr-city (4 capital-city guard 6-point loops with 2.0-2.5s waypoint dwell), --gen-cmr-boss (3 long raid-zone patrols up to 16 waypoints, demonstrating that variable-length payloads scale). Validation enforces id+name+creatureGuid+waypoints presence, pathKind 0..3, moveType 0..3, no duplicate ids; warns on 1-waypoint paths (creature would idle in place) and Loop with fewer than 3 waypoints (degenerate — indistinguishable from Reverse). This is the 90th open format milestone. Wired through the cross-format table; WCMR appears in all 18 cross-format utilities. Format count 89 -> 90; CLI flag count 1048 -> 1053.
2026-05-09 23:38:59 -07:00
std::printf(" --gen-cmr <wcmr-base> [name]\n");
std::printf(" Emit .wcmr 3 small patrols showing each pathKind (4-pt Loop guard / 6-pt OneShot run / 8-pt Random tiger)\n");
std::printf(" --gen-cmr-city <wcmr-base> [name]\n");
std::printf(" Emit .wcmr 4 capital-city guard routes (Stormwind / Orgrimmar / Ironforge / Thunder Bluff) with 6-point loops\n");
std::printf(" --gen-cmr-boss <wcmr-base> [name]\n");
std::printf(" Emit .wcmr 3 raid-zone patrols (AQ40 12-pt Loop / Naxx 8-pt OneShot / ICC 16-pt Random) demonstrating long-path support\n");
std::printf(" --info-wcmr <wcmr-base> [--json]\n");
std::printf(" Print WCMR entries (id / creatureGuid / pathKind / moveType / waypoint count / total path length yards / name)\n");
std::printf(" --validate-wcmr <wcmr-base> [--json]\n");
std::printf(" Static checks: id+name+creatureGuid+waypoints required, pathKind 0..3, moveType 0..3, no duplicate ids; warns on 1-waypoint paths (idle), Loop with <3 waypoints (degenerate)\n");
std::printf(" --export-wcmr-json <wcmr-base> [out.json]\n");
std::printf(" Export binary .wcmr to a human-editable JSON sidecar (defaults to <base>.wcmr.json). Waypoints exported as JSON arrays for variable-length editing\n");
std::printf(" --import-wcmr-json <json-path> [out-base]\n");
std::printf(" Import a .wcmr.json sidecar back into binary .wcmr (accepts pathKind/moveType int OR name; waypoint arrays length-preserving)\n");
feat(editor): add WBOS (Boss Encounter Definition) open catalog format Open replacement for AzerothCore's instance_encounter SQL table plus the per-boss script bindings. Defines raid boss encounter metadata: which creature is the boss, which map and difficulty variant it lives in, how many phases the encounter has, the soft-enrage timer and berserk spell, recommended group size, and item level. One entry per (boss × difficulty) combination. Lord Marrowgar in 10-Normal ICC is one entry; Lord Marrowgar in 25-Heroic ICC is a separate entry with a higher recommendedItemLevel and a different difficultyId pointing into WCDF. This format ties together five other catalogs into a coherent encounter description: - WCRT for the boss creature template - WMS for the instance map - WCDF for difficulty routing (10/25/H10/H25 variants) - WSPL for the berserk spell that fires at enrage - WACR for achievement criteria like "kill The Lich King in 25-Heroic" that point back via KillCreature targetId findByMap(mapId) returns all encounters in one raid instance, sorted by their catalog order — used by the Encounter Journal UI and instance lockout logic. findByBossCreature(bossId) returns all difficulty variants of one boss. Three preset emitters: --gen-bos (3 5-man dungeon bosses with no soft-enrage), --gen-bos-raid10 (4 ICC 10-Normal bosses including 5-phase Lich King with 15min hard enrage via Fury of Frostmourne 72546), --gen-bos-world (2 outdoor world bosses with 25-player size + no difficulty). Validation enforces id+name+boss+map+phases+size presence, no duplicate ids; warns on: - non-standard requiredPartySize (canonical sizes are 5/10/25/40) - berserkSpellId set without enrageTimerMs (spell never fires) - enrageTimerMs > 30 minutes (sanity check) Wired through the cross-format table; WBOS appears in all 18 cross-format utilities. Format count 90 -> 91; CLI flag count 1055 -> 1060.
2026-05-09 23:45:26 -07:00
std::printf(" --gen-bos <wbos-base> [name]\n");
std::printf(" Emit .wbos 3 5-man dungeon bosses (TrollChieftain 2-phase / ShamanWraith / DrakTharonFinal 3-phase) at recommended ilvl 200-210\n");
std::printf(" --gen-bos-raid10 <wbos-base> [name]\n");
std::printf(" Emit .wbos 4 ICC 10-Normal raid bosses (Marrowgar 2-phase / Deathwhisper 2-phase / Saurfang 1-phase / Lich King 5-phase) with soft-enrage timers\n");
std::printf(" --gen-bos-world <wbos-base> [name]\n");
std::printf(" Emit .wbos 2 outdoor world bosses (Doom Lord Kazzak / Doomwalker) — 25-player, no enrage, no difficultyId\n");
std::printf(" --info-wbos <wbos-base> [--json]\n");
std::printf(" Print WBOS entries (id / boss creature / map / difficulty / phases / size / ilvl / soft-enrage minutes / berserk spell / name)\n");
std::printf(" --validate-wbos <wbos-base> [--json]\n");
std::printf(" Static checks: id+name+boss+map+phases+size required, no duplicate ids; warns on non-standard party size, berserkSpellId set without enrageTimerMs, enrage > 30 min\n");
std::printf(" --export-wbos-json <wbos-base> [out.json]\n");
std::printf(" Export binary .wbos to a human-editable JSON sidecar (defaults to <base>.wbos.json)\n");
std::printf(" --import-wbos-json <json-path> [out-base]\n");
std::printf(" Import a .wbos.json sidecar back into binary .wbos (all per-entry fields preserved verbatim)\n");
feat(editor): add WHLD (Instance Lockout Schedule) open catalog format Open replacement for the engine-side instance reset timer logic plus the per-map InstanceTemplate.dbc reset fields. Defines how often each (map × difficulty) combination resets its lockout, how many boss kills each character can claim per lockout window, and the number of bonus rolls available (Cataclysm+ stub for forward compatibility). One entry per (map × difficulty × group size). Icecrown Citadel 10-Normal weekly, ICC 25-Normal weekly, ICC 10-Heroic weekly, and ICC 25-Heroic weekly are four separate entries with the same mapId but different difficultyId and resetIntervalMs. Cross-references back to WMS (mapId), WCDF (difficultyId), and forward to WBOS — the encounters bound to one lockout are the WBOS entries whose (mapId, difficultyId) pair matches. Four lockout kinds capture the canonical reset cadences: - Daily (24h, 86400000ms) — heroic dungeons, daily quests - Weekly (7d, 604800000ms) — raid lockouts - SemiWeekly (3.5d, 302400000ms) — Cata+ split lockouts - Custom (arbitrary intervalMs) — Wintergrasp 2.5h, holiday events with non-standard cadence nextResetMs(lockoutId, currentMs) is the engine helper that returns the next reset wall-clock millis after a given current time, rounded up to the nearest interval boundary. The engine overrides the epoch with its configured server reset time (typically Tuesday 8:00am server-local), but the catalog provides the interval shape. The info renderer pretty-prints intervals: 86400000ms reads as "1d", 9000000ms as "150m", which matches how server admins think about reset cadences. Three preset emitters: --gen-hld (4 ICC raid weekly lockouts), --gen-hld-dungeon (4 5-man heroic daily lockouts), --gen-hld-event (3 world-event lockouts including Wintergrasp's canonical Custom 2.5h interval). Validation enforces id+name+kind+resetIntervalMs presence, no duplicate ids; warns on non-standard raidGroupSize, kind/interval mismatches (Daily kind without 24h interval, Weekly kind without 7d interval), and 0 boss kill cap (instance grants no lockout-bound progress, every visit is fresh). Wired through the cross-format table; WHLD appears in all 18 cross-format utilities. Format count 91 -> 92; CLI flag count 1062 -> 1067.
2026-05-09 23:51:49 -07:00
std::printf(" --gen-hld <whld-base> [name]\n");
std::printf(" Emit .whld 4 ICC raid weekly lockouts (10N / 25N / 10H / 25H) — 12 boss kill cap, 7-day reset\n");
std::printf(" --gen-hld-dungeon <whld-base> [name]\n");
std::printf(" Emit .whld 4 5-man heroic dungeon daily lockouts (HoR / FoS / PoS / TotC) — 24h reset, 5-player size\n");
std::printf(" --gen-hld-event <whld-base> [name]\n");
std::printf(" Emit .whld 3 world-event lockouts (Brewfest daily / Hallow's End daily / Wintergrasp 2.5h Custom interval)\n");
std::printf(" --info-whld <whld-base> [--json]\n");
std::printf(" Print WHLD entries (id / map / diff / kind / interval (formatted) / boss kills / size / bonus rolls / name)\n");
std::printf(" --validate-whld <whld-base> [--json]\n");
std::printf(" Static checks: id+name+kind+interval required, no duplicate ids; warns on non-standard group size, kind/interval mismatches (Daily not 24h, Weekly not 7d), 0 boss kill cap\n");
std::printf(" --export-whld-json <whld-base> [out.json]\n");
std::printf(" Export binary .whld to a human-editable JSON sidecar (defaults to <base>.whld.json)\n");
std::printf(" --import-whld-json <json-path> [out-base]\n");
std::printf(" Import a .whld.json sidecar back into binary .whld (accepts raidLockoutKind int OR raidLockoutKindName string)\n");
feat(editor): add WSTC (Hunter Stable Slot) open catalog format Open replacement for the hardcoded hunter pet stable slot progression. Defines each stable slot's display order in the stable UI, the character level at which the slot becomes available, the gold cost to unlock, and whether it's a premium / donator-only slot. In WoW 3.3.5a hunters get 5 stable slots total: the active pet plus 4 stabled (slots 1-4 unlocking at hunter levels 10/20/30/40 with escalating gold costs 0/10s/50s/2g/10g). Cataclysm raised the cap to 5 stabled slots, and server-custom expansions go higher with donator-only "premium" slots that bypass the level gate. This catalog parameterizes the entire progression instead of editing engine source. Consumed directly by the StableMaster service in WBKD entries. unlockedSlotCount(characterLevel) is the engine helper used by the stable master frame to decide how many slot tabs to render. Three preset emitters: --gen-stc (5 canonical slots matching WoW 3.3.5a), --gen-stc-cata (6 Cata-style slots with slot 5 unlocking at lvl 60 for 25g), --gen-stc-premium (4 server-custom donator slots with no level/gold gate). The info renderer pretty-prints copperCost as "free" / "10s 0c" / "2g 0s 0c" — matches how server admins think about pricing. Validation enforces id+name presence, no duplicate ids; warns on: - minLevelToUnlock > 80 (unreachable at WotLK cap) - Premium slot with non-zero copperCost (donor slots are typically free; the gate is donor status, not gold) - duplicate displayOrder (stable UI position collision — only the first slot would render) Wired through the cross-format table; WSTC appears in all 18 cross-format utilities. Format count 92 -> 93; CLI flag count 1069 -> 1074.
2026-05-09 23:58:49 -07:00
std::printf(" --gen-stc <wstc-base> [name]\n");
std::printf(" Emit .wstc 5 canonical hunter stable slots (Active + 4 stabled, unlocking lvl 10/20/30/40/50 with 0/10s/50s/2g/10g costs)\n");
std::printf(" --gen-stc-cata <wstc-base> [name]\n");
std::printf(" Emit .wstc 6 Cata-style slots (Active + 5 stabled), with slot 5 unlocking at lvl 60 for 25g\n");
std::printf(" --gen-stc-premium <wstc-base> [name]\n");
std::printf(" Emit .wstc 4 server-custom donator-only slots (premium=1, no level/gold gate)\n");
std::printf(" --info-wstc <wstc-base> [--json]\n");
std::printf(" Print WSTC entries (id / displayOrder / minLevelToUnlock / cost (formatted) / premium flag / name)\n");
std::printf(" --validate-wstc <wstc-base> [--json]\n");
std::printf(" Static checks: id+name required, no duplicate ids; warns on lvl>80 (unreachable), Premium+nonzero cost (donor slots are free), duplicate displayOrder (UI collision)\n");
std::printf(" --export-wstc-json <wstc-base> [out.json]\n");
std::printf(" Export binary .wstc to a human-editable JSON sidecar (defaults to <base>.wstc.json)\n");
std::printf(" --import-wstc-json <json-path> [out-base]\n");
std::printf(" Import a .wstc.json sidecar back into binary .wstc (isPremium accepts bool OR int)\n");
feat(editor): add WSTM (Stat Modifier Curve) open catalog format Open replacement for the gtChanceTo*.dbc / gtRegen*.dbc / gtCombatRatings.dbc family of "1D level-keyed curve" tables. Each entry defines a single linear curve mapping character level to a stat value: melee crit chance per level, mana regen per spirit per level, base armor per level, etc. Curves are linear: value(level) = baseValue + perLevelDelta * (level - 1), with the result optionally scaled by a global multiplier and clamped to a level range. Most stock WoW curves fit this shape — the few that don't (cubic Combat Ratings) live in the dedicated WCRR catalog with spline support. Distinct from WCRR (Combat Rating conversion, integer ratings -> percentages) and WSPC (Spell Power Cost buckets, per-spell costs). WSTM is for the generic engine-side stat curves that aren't per-spell or per-rating. Seven curveKind values classify the major stat families (Crit / Hit / Power / Regen / Resist / Mitigation / Misc), and each curve carries its own [minLevel, maxLevel] applicability range plus a multiplier for global scaling without retuning each curve's slope. Three preset emitters: --gen-stm (5 crit-related curves with canonical 3.3.5a base+per-level scaling — MeleeCrit 5%+0.05/lvl resolves to 8.95% at lvl 80), --gen-stm-regen (4 regen curves including ManaPerSpirit and the Vanilla-era 3 rage/sec OOC decay), --gen-stm-armor (3 armor/mitigation/resistance curves). The info renderer demos resolveAtLevel(curveId, 80) inline as the @lvl80 column — server admins can sanity-check what each curve resolves to at character cap without writing test code. Validation enforces id+name presence, curveKind 0..6, minLevel<=maxLevel, no duplicate ids; warns on: - maxLevel > 80 (unreachable at WotLK cap) - multiplier=0 (curve always evaluates to 0) - multiplier<0 (inverts the curve — possibly intentional) - perLevelDelta<0 (curve shrinks with level — unusual) Wired through the cross-format table; WSTM appears in all 18 cross-format utilities. Format count 93 -> 94; CLI flag count 1076 -> 1081.
2026-05-10 00:05:07 -07:00
std::printf(" --gen-stm <wstm-base> [name]\n");
std::printf(" Emit .wstm 5 crit curves (MeleeCrit / RangedCrit / SpellCrit / Parry / Dodge) with canonical 3.3.5a base+per-level scaling\n");
std::printf(" --gen-stm-regen <wstm-base> [name]\n");
std::printf(" Emit .wstm 4 regen curves (ManaPerSpirit / HpPerSpirit / EnergyPerSec / RageDecay) with stock formulas\n");
std::printf(" --gen-stm-armor <wstm-base> [name]\n");
std::printf(" Emit .wstm 3 armor/mitigation curves (BaseArmorPerLevel / ArmorMitigationPct / ResistancePerLevel)\n");
std::printf(" --info-wstm <wstm-base> [--json]\n");
std::printf(" Print WSTM entries (id / kind / minLvl / maxLvl / baseValue / perLevelDelta / multiplier / value@lvl80 sample / name)\n");
std::printf(" --validate-wstm <wstm-base> [--json]\n");
std::printf(" Static checks: id+name required, curveKind 0..6, minLevel<=maxLevel, no duplicate ids; warns on maxLevel>80, multiplier=0 (always 0), multiplier<0 (inverts), perLevelDelta<0 (shrinks)\n");
std::printf(" --export-wstm-json <wstm-base> [out.json]\n");
std::printf(" Export binary .wstm to a human-editable JSON sidecar (defaults to <base>.wstm.json)\n");
std::printf(" --import-wstm-json <json-path> [out-base]\n");
std::printf(" Import a .wstm.json sidecar back into binary .wstm (accepts curveKind int OR curveKindName string)\n");
feat(editor): add WACT (Action Bar Layout) open catalog format Open replacement for the hardcoded per-class default action bar bindings. Defines which abilities auto-populate which action button slots when a new character is created or a class is reset. A Warrior's button 1 binds Heroic Strike, button 2 Charge, button 3 Rend, etc. — new characters of that class get those buttons pre-populated so the action bar isn't empty on first login. Distinct from WKBD (Keybindings) which maps physical keys to action button slots — WACT maps action button slots to abilities. The two together complete the default-control configuration: Key 1 -> Action Slot 1 (WKBD) -> Heroic Strike (WACT). Seven barMode values cover the major action bar contexts: - Main (slots 0-11, standard 12-button bar) - Pet (hunter/warlock pet action bar) - Vehicle (mounted/vehicle action bar) - Stance1/2/3 (warrior battle/defensive/berserker; druid bear/cat/tree) - Custom (server-custom bar overlay) Cross-references back to WCHC (classMask layout), WSPL (spellId for the bound ability), and WIT (itemId for item-macro bindings like Hearthstone in slot 12). findByClass(classBit, barMode) returns the bindings sorted by buttonSlot — used directly by character creation to populate action bars. Three preset emitters: --gen-act (10 Warrior starter bindings on Main bar with canonical 3.3.5a abilities), --gen-act-mage (10 Mage starter bindings including Counterspell + Polymorph), --gen-act-pet (10 Hunter pet-bar bindings using barMode=Pet for Attack/Stance/Bite/Claw/Dismiss). Validation enforces id+name+classMask presence, barMode 0..6, no duplicate ids; warns on: - buttonSlot > 143 (max is 12 bars × 12 slots = 144) - both spellId and itemId set (engine prefers spellId, item is silently ignored) - both spellId=0 AND itemId=0 (button will render empty) - (classMask + barMode + buttonSlot) collisions for overlapping classes — multiple bindings fighting for the same physical slot Wired through the cross-format table; WACT appears in all 18 cross-format utilities. Format count 94 -> 95; CLI flag count 1083 -> 1088.
2026-05-10 00:11:53 -07:00
std::printf(" --gen-act <wact-base> [name]\n");
std::printf(" Emit .wact 10 Warrior starter bindings on Main bar (Heroic Strike / Charge / Rend / Thunder Clap / Battle Shout / Sunder Armor / Mocking Blow / Hamstring / Overpower / Victory Rush)\n");
std::printf(" --gen-act-mage <wact-base> [name]\n");
std::printf(" Emit .wact 10 Mage starter bindings on Main bar (Fireball / Frostbolt / Frost Nova / Polymorph / Mage Armor / Arcane Intellect / Counterspell / Blink / Fire Blast / Conjure Water)\n");
std::printf(" --gen-act-pet <wact-base> [name]\n");
std::printf(" Emit .wact 10 Hunter pet-bar bindings on Pet bar mode (Attack / Follow / Stay / Aggressive / Defensive / Passive / Bite / Claw / Growl / Dismiss)\n");
std::printf(" --info-wact <wact-base> [--json]\n");
std::printf(" Print WACT entries (id / classMask / barMode / buttonSlot / spellId / itemId / name)\n");
std::printf(" --validate-wact <wact-base> [--json]\n");
std::printf(" Static checks: id+name+classMask required, barMode 0..6, no duplicate ids; warns on slot>143, both spellId+itemId set, both 0 (empty button), and (classMask+barMode+slot) collisions for overlapping classes\n");
std::printf(" --gen-weather-temperate <wow-base> [zoneName]\n");
std::printf(" Emit .wow weather schedule: clear-dominant + occasional rain + fog (forest / grassland)\n");
std::printf(" --gen-weather-arctic <wow-base> [zoneName]\n");
std::printf(" Emit .wow weather schedule: snow-dominant + blizzard + fog (tundra / glacier)\n");
std::printf(" --gen-weather-desert <wow-base> [zoneName]\n");
std::printf(" Emit .wow weather schedule: clear-dominant + sandstorm (dunes / wasteland)\n");
std::printf(" --gen-weather-stormy <wow-base> [zoneName]\n");
std::printf(" Emit .wow weather schedule: heavy rain + storm + occasional clear (coastal / monsoon)\n");
std::printf(" --gen-zone-atmosphere <zoneDir> [--name <zoneName>] [--preset default|arctic|desert|stormy|cave]\n");
std::printf(" Convenience: drop both atmosphere.wol + atmosphere.wow into <zoneDir> using a paired light/weather preset\n");
std::printf(" --info-wot <wot-base> [--json]\n");
std::printf(" Print WOT/WHM terrain metadata (tile, chunks, height range) and exit\n");
std::printf(" --info-extract <dir> [--json]\n");
std::printf(" Walk extracted asset tree and report open-format coverage and exit\n");
std::printf(" --info-extract-tree <dir>\n");
std::printf(" Hierarchical view of an extracted asset tree grouped by top-level dir + format\n");
std::printf(" --info-extract-budget <dir> [--json]\n");
std::printf(" Per-extension byte breakdown of an extract dir (sized largest-first)\n");
std::printf(" --info-png <path> [--json]\n");
std::printf(" Print PNG header (width, height, channels, bit depth) and exit\n");
std::printf(" --info-blp <path> [--json]\n");
std::printf(" Print BLP texture header (format, compression, mips, dimensions) and exit\n");
std::printf(" --info-m2 <path> [--json]\n");
std::printf(" Print proprietary M2 model metadata (verts, bones, anims, particles)\n");
std::printf(" --info-wmo <path> [--json]\n");
std::printf(" Print proprietary WMO building metadata (groups, portals, doodads)\n");
std::printf(" --info-adt <path> [--json]\n");
std::printf(" Print proprietary ADT terrain metadata (chunks, placements, textures)\n");
std::printf(" --info-jsondbc <path> [--json]\n");
std::printf(" Print JSON DBC sidecar metadata (records, fields, source) and exit\n");
std::printf(" --list-missing-sidecars <dir> [--json]\n");
std::printf(" List proprietary files lacking open-format sidecars (one per line)\n");
std::printf(" --info-zone <dir|json> [--json]\n");
std::printf(" Print zone.json fields (manifest, tiles, audio, flags) and exit\n");
std::printf(" --info-zone-overview <zoneDir> [--json]\n");
std::printf(" One-line compact zone summary (tiles, biome, counts, audio status)\n");
std::printf(" --info-project-overview <projectDir> [--json]\n");
std::printf(" One-line summary per zone in a project (single-page health check)\n");
std::printf(" --copy-project <fromDir> <toDir>\n");
std::printf(" Recursively copy a project tree (every zone subdir + manifests)\n");
std::printf(" --info-creatures <p> [--json]\n");
std::printf(" Print creatures.json summary (counts, behaviors) and exit\n");
std::printf(" --info-creatures-by-faction <p> [--json]\n");
std::printf(" Histogram of creature counts grouped by faction id\n");
std::printf(" --info-creatures-by-level <p> [--json]\n");
std::printf(" Distribution of creature levels (min/max/avg + per-level counts)\n");
std::printf(" --info-objects-by-path <p> [--json]\n");
std::printf(" Histogram of object placements grouped by model path (most-used first)\n");
std::printf(" --info-objects-by-type <p> [--json]\n");
std::printf(" M2 vs WMO breakdown plus scale distribution (min/max/avg)\n");
std::printf(" --info-objects <p> [--json]\n");
std::printf(" Print objects.json summary (counts, types, scale range) and exit\n");
std::printf(" --info-quests <p> [--json]\n");
std::printf(" Print quests.json summary (counts, rewards, chain errors) and exit\n");
std::printf(" --info-quests-by-level <p> [--json]\n");
std::printf(" Distribution of required levels across quests (min/max/avg + bar chart)\n");
std::printf(" --info-quests-by-xp <p> [--json]\n");
std::printf(" Distribution of XP rewards (min/max/avg + per-bucket histogram)\n");
std::printf(" --list-creatures <p> [--json]\n");
std::printf(" List every creature with index, name, position, level (for --remove-creature)\n");
std::printf(" --list-objects <p> [--json]\n");
std::printf(" List every object with index, type, path, position\n");
std::printf(" --list-quests <p> [--json]\n");
std::printf(" List every quest with index, title, giver, XP\n");
std::printf(" --list-quest-objectives <p> <questIdx> [--json]\n");
std::printf(" List every objective on a quest (for --remove-quest-objective)\n");
std::printf(" --list-quest-rewards <p> <questIdx> [--json]\n");
std::printf(" List XP/coin/item rewards on a quest\n");
std::printf(" --info-quest-graph-stats <p> [--json]\n");
std::printf(" Analyze quest chain graph (roots, leaves, depths, cycles, orphans)\n");
std::printf(" --info-creature <p> <idx> [--json]\n");
std::printf(" Print every field for one creature spawn (stats, behavior, AI, flags)\n");
std::printf(" --info-quest <p> <idx> [--json]\n");
std::printf(" Print every field for one quest (objectives + reward + chain in one shot)\n");
std::printf(" --info-object <p> <idx> [--json]\n");
std::printf(" Print every field for one object placement (type, path, transform)\n");
std::printf(" --info-wcp <wcp-path> [--json]\n");
std::printf(" Print WCP archive metadata (name, files) and exit\n");
std::printf(" --info-pack-budget <wcp-path> [--json]\n");
std::printf(" Per-extension byte breakdown of a WCP archive (sized largest-first)\n");
std::printf(" --info-pack-tree <wcp-path>\n");
std::printf(" Render a tree view of a WCP's directory structure with byte sizes\n");
std::printf(" --list-wcp <wcp-path> Print every file inside a WCP archive (sorted by path) and exit\n");
std::printf(" --diff-wcp <a> <b> [--json]\n");
std::printf(" Compare two WCPs file-by-file; exit 0 if identical, 1 otherwise\n");
std::printf(" --diff-zone <a> <b> [--json]\n");
std::printf(" Compare two zone dirs (creatures/objects/quests/manifest); exit 0 if identical\n");
std::printf(" --diff-glb <a> <b> [--json]\n");
std::printf(" Compare two glTF 2.0 binaries structurally; exit 0 if identical\n");
std::printf(" --diff-wom <a-base> <b-base> [--json]\n");
std::printf(" Compare two WOM models (verts, indices, bones, anims, batches, bounds)\n");
std::printf(" --diff-wob <a-base> <b-base> [--json]\n");
std::printf(" Compare two WOB buildings (groups, portals, doodads, totals)\n");
std::printf(" --diff-whm <a-base> <b-base> [--json]\n");
std::printf(" Compare two WHM/WOT terrain pairs (chunks, height range, placements)\n");
std::printf(" --diff-woc <a> <b> [--json]\n");
std::printf(" Compare two WOC collision meshes (triangles, walkable/steep counts, tile)\n");
std::printf(" --diff-jsondbc <a> <b> [--json]\n");
std::printf(" Compare two JSON DBC sidecars (format/source/recordCount/fieldCount)\n");
std::printf(" --diff-extract <a> <b> [--json]\n");
std::printf(" Compare two extracted asset directories (per-extension file count + bytes)\n");
std::printf(" --diff-checksum <a> <b> [--json]\n");
std::printf(" Diff two SHA256SUMS files; report added/removed/changed entries\n");
std::printf(" --pack-wcp <zone> [dst] Pack a zone dir/name into a .wcp archive and exit\n");
std::printf(" --unpack-wcp <wcp> [dst] Extract a WCP archive (default dst=custom_zones/) and exit\n");
std::printf(" --list-commands Print every recognized --flag, one per line, and exit\n");
std::printf(" --list-primitives [--mesh|--texture] [--json]\n");
std::printf(" Filtered list of just procedural primitive flags (--gen-mesh-*, --gen-texture-*)\n");
std::printf(" --list-packs Print every --gen-*-pack composite flag (camp, blacksmith, village, temple…)\n");
std::printf(" --info-cli-stats [--json]\n");
std::printf(" Meta-stats on the CLI surface (command count by category prefix)\n");
std::printf(" --info-cli-categories\n");
std::printf(" Group every --flag by verb prefix (gen/info/list/...) for discovery\n");
std::printf(" --info-cli-help <pattern>\n");
std::printf(" Substring-search the help text and print matching command lines\n");
std::printf(" --validate-cli-help [--json]\n");
std::printf(" Self-check: every kArgRequired flag must appear in the help text\n");
std::printf(" --gen-completion <bash|zsh>\n");
std::printf(" Print a shell-completion script for wowee_editor (source it from your rc file)\n");
std::printf(" --version Show version and format info\n\n");
std::printf("Wowee World Editor v1.0.0 — by Kelsi Davis\n");
std::printf("Novel open formats: WOT/WHM/WOM/WOB/WOC/WCP + PNG/JSON\n");
}
} // namespace cli
} // namespace editor
} // namespace wowee