Kelsidavis-WoWee/tools/editor/cli_help.cpp

1557 lines
154 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(" --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");
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