Commit graph

712 commits

Author SHA1 Message Date
Kelsi
83da3d4849 feat(editor): add --gen-texture-fabric woven warp/weft pattern
Procedural fabric: alternating warp (vertical) and weft
(horizontal) threads on a checkerboard cell grid, with each
thread shaded brighter at center and darker at edges so the
weave reads as 3D yarn rather than flat checker squares.

Defaults: thread=4px, W=H=256. Useful for banners, tapestries,
clothing, sails. Brings the procedural texture pattern set to 15.
2026-05-08 04:47:51 -07:00
Kelsi
1547140816 feat(editor): add --list-project-meshes inventory command
Project-wide companion to --list-zone-meshes. Walks every
zone in <projectDir> and reports per-zone WOM count + bytes/
verts/tris row, plus a project grand total. Both human-
readable table and --json supported.

Useful for "how heavy is the entire project mesh-wise"
budgeting before shipping a content pack.
2026-05-08 04:21:24 -07:00
Kelsi
3587a91df5 feat(editor): add --validate-zone-pack asset audit
Audits a zone's open-format asset pack: counts and total bytes
for textures (PNG signature check), meshes (WOM load + sanity
check), and audio (RIFF/WAVE header check). Reports any
malformed files by relative path and exits 1 if anything is
invalid or the pack is empty.

Ties together the zone-pack orchestrator family — a CI step
can now gate that gen-zone-starter-pack output is healthy
before downstream packaging.
2026-05-08 03:55:03 -07:00
Kelsi
265ae1c95c feat(editor): add --gen-mesh-bridge plank bridge primitive
Procedural plank bridge: deck of N axis-aligned planks running
along the length with small gaps between, optional side rails
(top rail + 3 posts each side). Length runs along +X, width
along Z. Pass railHeight=0 to skip rails for a simpler footbridge.

Defaults: length=6, width=2, planks=6, railHeight=1. Useful for
river crossings, dungeon catwalks, scenic overlooks. Brings the
procedural mesh primitive set to 18.
2026-05-08 03:29:03 -07:00
Kelsi
96c1aee38f feat(editor): add --gen-audio-noise procedural noise WAV synthesis
Three audio-engineering noise colors:
  white — equal energy per Hz (uniform random samples)
  pink  — equal energy per octave via Voss-McCartney 7-band
          cascade (1/f spectrum, sounds like rain or wind)
  brown — 1/f² spectrum via random walk (sounds like distant
          surf or rumbling weather)

All written as PCM-16 mono with same RIFF header + 5ms
attack/release envelope as gen-audio-tone. Replaces typical
proprietary nature-sound MP3 placeholders for ambient zone
audio with hand-synthesized seeded WAVs.
2026-05-08 03:04:01 -07:00
Kelsi
cc436724fd feat(editor): add --list-zone-audio inventory command
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Inventories every WAV under <zoneDir>/audio/ with stats parsed
straight from the RIFF/WAVE header: sample rate, channel count,
bit depth, duration. Both human-readable table and --json
output supported.

Completes the per-zone asset accounting trio alongside
--list-zone-meshes and --list-zone-textures.
2026-05-08 02:38:55 -07:00
Kelsi
be42422495 feat(editor): add --gen-zone-audio-pack starter audio orchestrator
Drops a 6-WAV starter pack into <zoneDir>/audio/: ambient-low
+ ambient-mid (drone pair, fifth interval), music-stinger,
chime, alert, click. All hand-synthesized PCM-16 mono WAVs
via --gen-audio-tone — no MP3 patent or licensing baggage.

Closes the audio side of the open-format zone bootstrap. A
fresh zone now has full coverage across textures (PNG), meshes
(WOM), and audio (WAV) from procedural generators alone.
2026-05-08 02:14:08 -07:00
Kelsi
ddcd89bb4c feat(editor): add --gen-mesh-pillar fluted column primitive
Procedural classical column. Cylindrical shaft with N concave
flutes (radius modulated by cos² of angle×flute-count) capped
by wider disc bases above and below — basic capital + base
geometry. Defaults: radius=0.4, height=4, flutes=12, capScale=1.25.

Useful for ruins, temples, dungeons, plaza decoration. Brings
the procedural mesh primitive set to 17.
2026-05-08 01:49:32 -07:00
Kelsi
6b0b6f6652 feat(editor): add --gen-audio-tone procedural WAV synthesis
Hand-rolled RIFF/WAVE PCM-16 mono encoder — no library deps,
44-byte header written field-by-field. Supports four waveforms
(sine/square/triangle/saw), 5ms attack/release envelope to
prevent click on tone start/stop, freq 0..24kHz, duration
0..600s, sampleRate 8k..192k.

Opens a brand-new file family in the open-format ecosystem
alongside WOM/WOB/PNG/JSON. Proprietary MP3 placeholders for
zone audio can now be replaced by hand-synthesized WAVs with
no patent or licensing baggage.
2026-05-08 01:25:17 -07:00
Kelsi
410a0bf7d1 feat(editor): add --list-zone-meshes inventory command
Companion to --list-zone-textures. Walks every WOM under a
zone dir and reports per-mesh size, vertex/triangle counts,
bone/anim/batch counts, and texture-ref counts. Both human-
readable table and --json output supported.

Useful for "how heavy is this zone" budgeting and verifying
that bootstrapping orchestrators (gen-zone-mesh-pack, gen-zone-
starter-pack) produced what they claim.
2026-05-08 01:01:18 -07:00
Kelsi
6a9c1302e4 feat(editor): add --gen-zone-starter-pack unified bootstrap
Convenience entry point that runs --gen-zone-texture-pack and
--gen-zone-mesh-pack in sequence so a fresh zone gets a full
open-format asset bootstrap from one command. Seed propagates
to both children for reproducibility.

A new zone now goes from "just a zone.json" to "11 placeholder
assets across textures/ and meshes/" with one invocation —
zero proprietary M2/BLP imports required.
2026-05-08 00:40:56 -07:00
Kelsi
ee715e3bbf feat(editor): add --gen-zone-mesh-pack orchestrator
Companion to --gen-zone-texture-pack. Drops a 5-mesh starter
pack (3 rock variants + tree + fence) into <zoneDir>/meshes/ by
fanning out to the procedural --gen-mesh-* family. Each rock
variant uses a distinct seed so they read as different boulders.

Together with the texture-pack orchestrator a fresh zone can now
be bootstrapped to first-render-ready state with zero proprietary
M2/BLP imports — entirely from open WOM/PNG output.
2026-05-08 00:20:53 -07:00
Kelsi
4f1aaaab22 feat(editor): add --gen-texture-grass tiling pattern command
Procedural grass texture: jittered base color + scattered short
vertical blade strokes blending toward a brighter highlight hue.
Strokes wrap on Y so the texture tiles vertically. Reproducible
from a seed.

Defaults: density=0.15, seed=1, W=H=256. Brings procedural
texture pattern set to 14 commands.
2026-05-08 00:01:12 -07:00
Kelsi
5441b39869 feat(editor): add --gen-mesh-rock procedural boulder primitive
Subdivided octahedron + smooth sin-product noise displacement.
Each seed produces a unique silhouette so scattering looks
varied. Defaults: radius=1, roughness=0.25, subdiv=2 (128
triangles), seed=1.

Pairs naturally with random-populate-zone for outdoor decoration
and brings the procedural mesh primitive set to 16.
2026-05-07 23:41:24 -07:00
Kelsi
f40126dffc feat(editor): add --gen-zone-texture-pack orchestrator
New zone-scope command that drops a 6-texture starter pack
(grass, dirt, stone, brick, wood, water) into <zoneDir>/textures/
by fanning out to the procedural --gen-texture-* family. Saves
users from sourcing proprietary art when bringing up a new zone —
the entire pack is open PNG output from procedural generators.

Adds to the gen-random-zone family of project orchestrators.
2026-05-07 23:18:00 -07:00
Kelsi
39ea13965e feat(editor): add --gen-texture-wood grain pattern command
Procedural wood grain texture: vertical streaks of varying width
between two hues, plus pseudo-random knots. Reproducible from a
seed via tiny LCG (no <random> dep). Suitable for doors, planks,
fences, crates.

Defaults: spacing=12px, seed=1, W=H=256. Brings procedural
texture pattern set to 13 commands.
2026-05-07 22:58:28 -07:00
Kelsi
bd4e7a302d feat(editor): add --gen-texture-brick wall pattern command
Procedural brick wall texture with offset rows + mortar lines. Each
row alternates by half a brick width, mortar gap appears between
rows and within each row. Useful for walls, chimneys, paths, and
medieval-zone props.

Defaults: brickW=64 brickH=24 mortarPx=4 W=H=256. Brings procedural
texture pattern set to 12 commands.
2026-05-07 22:39:07 -07:00
Kelsi
6bd7f18328 feat(editor): add --gen-mesh-tree composite tree primitive
Procedural tree: cylindrical trunk (12 segments) + UV-sphere
foliage (12 segs × 8 stacks). Foliage centered above the trunk
with a slight overlap so the trunk pokes into the bottom of the
canopy.

Useful for ambient zone decoration, distant tree placeholders,
magic-grove props. The 15th procedural primitive — composite of
cylinder + sphere with everything in a single batch so it ships
as one mesh.

Args: <wom-base> [trunkRadius] [trunkHeight] [foliageRadius]
Defaults: 0.1 / 2.0 / 0.7 (3.2y total height — small/medium tree).

Pairs naturally with --add-texture-to-mesh for the bark+leaf
textures, or just one composite texture since this is a single-
batch mesh. Verified: defaults produce 143 verts / 216 tris.
2026-05-07 22:19:51 -07:00
Kelsi
010cf3b6c5 feat(editor): add --gen-mesh-fence repeating post + rail primitive
Repeating fence: N square posts along +X spaced postSpacing apart,
with two horizontal rails (top at 80% of post height, bottom at
30%) connecting consecutive posts. Posts are railThick × 2 wide;
rails are thinner.

Useful for fences around plots, pen boundaries, walkway dividers,
garden beds, paddock perimeters. The 14th procedural primitive.

Args: <wom-base> [posts] [postSpacing] [postHeight] [railThick]
Defaults: 5 / 1.0 / 1.0 / 0.05. Posts hard-capped at 256.

Verified: 5 posts × 1.0 spacing → 312 verts / 156 tris, X span
of 4.0 (= 4 gaps × 1.0).
2026-05-07 21:59:58 -07:00
Kelsi
01c6c306f2 feat(editor): add --bench-audit-project per-step perf breakdown
Times each --audit-project sub-step end-to-end so users can see
where the slow checks are. Useful for tuning a CI pipeline: drop
the slowest check from a fast-feedback pre-commit hook, run the
full audit on push.

Reports wall-clock per step, share of total, and pass/fail status
for each. Status column means the check ran cleanly even on a zone
with content issues — bench timings are about runtime, not
correctness.

On a small test zone, items-schema and spawn-placement dominate
the share (~25% each) since they involve loading content +
sampling terrain. Format/open-only/refs/content are sub-10ms each.
2026-05-07 21:40:49 -07:00
Kelsi
41d50c824a feat(editor): add --gen-texture-noise-color two-color noise blends
Same value-noise function as --gen-texture-noise but interpolated
between two RGB endpoints rather than emitted as grayscale.
Useful for terrain detail (grass+dirt mottle), magic fog, marble
veining, or any "natural variation" pass that shouldn't be
desaturated.

Args: <out.png> <colorAHex> <colorBHex> [seed] [W H]
Defaults: seed 1, 256x256.

11th procedural texture pattern (joining solid, BW checker, color
checker, grid, vertical/horizontal/radial gradients, grayscale
noise, stripes, dots, rings).
2026-05-07 21:22:33 -07:00
Kelsi
f4fd009a0c feat(editor): add --diff-zone-spawns for zone-to-zone comparison
Compares two zones' creatures + objects with two-pointer match-by-
(kind, name): paired entries with mismatched positions report as
"moved" with the (dx, dy, dz) delta; entries that exist in only one
zone are added/removed.

Useful for "what did this branch change vs main" before merging,
for confirming a copy-zone-items produced the expected result, and
for cross-zone consistency reviews.

Move threshold is 0.5y; smaller deltas count as "same" since
typical drift from snap-zone-to-ground is sub-yard. Exit 1 if
anything differs so CI can gate on cross-zone consistency.

Verified: 2 different random-populated zones → reports 6 added /
5 removed / 1 moved / 0 same with the moved entry showing its
position delta.
2026-05-07 21:04:14 -07:00
Kelsi
525b48ade7 feat(editor): add --gen-mesh-pyramid N-sided polygonal pyramid
N-sided polygonal pyramid with apex at +Y. 3 sides → tetrahedron-
like shape; 4 → classic square pyramid; 8+ → faceted approximation
of a cone.

Different from --gen-mesh cone: cone has smooth curved sides with
per-vertex radial-ish normals; pyramid has flat per-face normals
on each triangular side, giving a clearly faceted look.

Args: <wom-base> [sides] [baseRadius] [height]
Defaults: 4 / 1.0 / 1.0. Sides hard-capped at 256.

Useful for monuments, witch hats, gem props, treasure piles, dunce
caps. Brings the procedural primitive set to 13.

Verified: 4-sided pyramid → 17 verts / 8 tris (4 side + 4 base);
3-sided → 13 verts / 6 tris.
2026-05-07 20:45:11 -07:00
Kelsi
7a43e4eb3b feat(editor): --audit-project also runs items schema + spawn placement
Extended the composite project audit from 4 sub-checks to 6,
adding the two new gates:
  - validate-project-items (items.json schema across zones)
  - audit-project-spawns (spawn Z within 5y of terrain)

Sub-checks now ordered cheapest → most expensive so a fast failure
surfaces before the slow ones run: format validation runs in
milliseconds, but spawn-placement audit requires loading every WHM
tile and casting rays.

Single-command release-readiness gate: a green --audit-project
now means format-clean + open-only + items-clean + refs-resolve +
content-sane + spawns-grounded.
2026-05-07 20:26:17 -07:00
Kelsi
fb4421e5df feat(editor): add --gen-texture-checker with custom colors
The existing --gen-texture's "checker" pattern is fixed black/white
at 32px. This is the configurable variant: pass two colors and an
optional cell size to generate boards in any palette. Useful for
chess/checker game boards, kitchen-floor textures, hazard markers
in colors other than monochrome, retro UI backgrounds.

Args: <out.png> <colorAHex> <colorBHex> [cellPx] [W H]
Defaults: cellPx 32, 256x256.

10th procedural texture pattern (joining solid, the BW checker,
grid, vertical/horizontal/radial gradients, noise, stripes, dots,
rings).
2026-05-07 20:08:34 -07:00
Kelsi
f6f3a7b060 feat(editor): add --gen-mesh-arch doorway/portal frame
Builds a doorway arch from two rectangular columns (one on each
side of the opening) plus a semicircular curved band across the
top. Total height = openingHeight + archRadius (where archRadius
= openingWidth/2). Aligned so the inside of the opening is
centered on the Y axis.

Args: <wom-base> [openingW] [openingH] [thickness] [depth] [segments]
Defaults: 1.0 / 1.5 / 0.2 / 0.3 / 12. Curve segments hard-capped
at 256.

Useful for cave entrances, gates, portal frames, dungeon
thresholds. The 12th procedural primitive (joining cube/plane/
sphere/cylinder/torus/cone/ramp/grid/disc/tube/capsule).

Verified: 1.0×1.5 opening with 12 segments produces 96 verts /
48 tris, bounds Z spans 0..2.0 (= 1.5 + 0.5 arch radius).
2026-05-07 19:50:00 -07:00
Kelsi
ef38998a4e feat(editor): add --copy-project recursive project tree copy
Recursively copies an entire project tree (every zone subdir +
manifests + content files). Refuses to overwrite an existing
destination so a typo doesn't silently merge into the wrong
project — user must delete the destination first if intentional.

Reports zone count, total file count, and total byte count of
what was copied. Useful for forking a project for experimentation,
or for creating snapshot backups before risky bulk operations
like --strip-data-tree or --remove-project-orphans.

Verified: 2-zone gen-random-project source → copy-project →
"zones: 2, files: 12, total bytes: 395989" reported correctly,
existing destination correctly rejected.
2026-05-07 19:31:09 -07:00
Kelsi
6b6a3189e4 feat(editor): add --info-project-overview project-wide health check
Project-wide companion to --info-zone-overview. Single-page table
with one row per zone showing biome / tile count / creature / object
/ quest / item counts and audio yes-or-no, plus aggregate totals
across the project.

Useful for "what's in this project" at-a-glance reviews and CI
artifacts that need a compact status snapshot. Cheap content
counts via direct JSON parse (same approach as --info-zone-overview)
so even a 100-zone project takes milliseconds.

Verified on 3-zone gen-random-project run: per-zone rows show
identical counts (5c/2o/3i each), totals row aggregates correctly
to 15c/6o/9i.
2026-05-07 19:13:11 -07:00
Kelsi
e91764a168 feat(editor): add --info-zone-overview compact one-line summary
Where --info-zone dumps every manifest field, --info-zone-overview
is a tweet-length status: zone name, biome, content counts, audio
flag. Easy to grep through --for-each-zone output to spot
outliers.

Format: name [biome] Nt/Cc/Oo/Qq/Ii [+audio]
  N = tiles, C = creatures, O = objects, Q = quests, I = items
  +audio shown when music or any ambience is set

Cheap content counts via direct JSON parse — no need to spin up
the full NpcSpawner/ObjectPlacer/etc classes for an overview.
Both human and --json output modes.
2026-05-07 18:55:28 -07:00
Kelsi
d7a389c848 feat(editor): add --gen-mesh-capsule pill-shaped primitive
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Capsule along the Y axis: cylindrical body of length cylHeight
bookended by two hemispheres of `radius`. Total height is
cylHeight + 2*radius.

Useful for character collision shells, pill-shaped buttons,
hot-dog props, physics-friendly placeholders, sausages.

Layout: top hemisphere (stacks rings, north pole at the top) →
cylindrical body (1 quad band) → bottom hemisphere (mirror).
Per-vertex normals follow the smooth surface so shading works
correctly through the body-cap transition.

Args: <wom-base> [radius] [cylHeight] [segments] [stacks]
Defaults: 0.5 / 1.0 / 16 / 8.

Verified: defaults produce 340 verts / 544 tris with bounds
spanning the expected total height of 2.0 (= 1.0 cylinder +
2*0.5 hemispheres). Brings the procedural primitive set to 11.
2026-05-07 18:36:56 -07:00
Kelsi
9479973366 feat(editor): status bar shows active biome
Adds a public EditorApp::getActiveBiome() accessor and renders the
biome name in the status bar between the tile coords and the
dirty marker. So the bar reads, e.g., "[Sculpt] mymap [32,32]
Forest *" — at a glance the user knows what biome the painting/
auto-paint passes will use.

Useful especially after creating a new terrain: the biome
selection previously only mattered at create time + during
"Generate Complete Zone", with no visible feedback that it was
remembered.
2026-05-07 18:19:24 -07:00
Kelsi
135b89299b feat(editor): add --gen-mesh-tube hollow cylinder/pipe primitive
Hollow cylinder along the Y axis with separate outer wall, inner
wall (normals pointing inward + reversed winding so the inside
faces the viewer when looking through), and two annular caps. UV
coords on the caps map the inner ring proportionally so a radial
texture stays continuous across both rings.

Useful for railings, fence posts, pipes, hollow logs, ring towers
— anywhere a solid cylinder would feel wrong because you should
be able to see through the middle.

Args: <wom-base> [outerR] [innerR] [height] [segments]
Defaults: 1.0 / 0.7 / 2.0 / 24. Validates innerR < outerR so the
walls don't intersect.

Verified: defaults produce 200 verts / 192 tris (4 surfaces × 48
quads × 2 tris = 384, halved by per-segment-pair indexing → 192).
Brings the procedural primitive set to 10 (cube/plane/sphere/
cylinder/torus/cone/ramp/grid/disc/tube + stairs + heightmap).
2026-05-07 18:01:42 -07:00
Kelsi
5e4a5f350f feat(editor): add --info-cli-categories grouped command discovery
Where --info-cli-stats just counts per category, this lists every
command in each category. Categories sorted by count descending,
commands within each alphabetically. Header shows total count for
context.

Useful for "I know I want to gen something but what shapes/textures
are available?" — opens a clear view into the procedural toolset
that --help's flat dump buries. With 250 commands today the
categorization makes the surface tractable.
2026-05-07 17:43:58 -07:00
Kelsi
cedeeffc6c feat(editor): add --gen-mesh-disc circular flat plane primitive
Flat circular disc on XY centered at origin. Center vertex + ring
of <segments>+1 vertices (extra one to break the UV seam),
indexed as a fan.

Useful for magic circles, coin meshes, lily pads, top caps for
cylinders the user wants without making a full cylinder, table
tops, etc. UV mapping is centered (0.5, 0.5) at the disc's center
and unit-circle-aligned at the rim, so radial textures (like the
new --gen-texture-rings) align correctly.

Args: <wom-base> [radius] [segments]
Defaults: radius 1.0, segments 32. Hard-capped at 1024 segments.

Verified: radius 0.5 / segments 24 produces 26 verts / 24 tris.
2026-05-07 17:27:33 -07:00
Kelsi
3810dfc510 feat(editor): add --info-spawn single-spawn detail view
Detailed view of one creature or object by index. Where
--list-zone-spawns shows headline fields in a table, --info-spawn
dumps every field for a single record:

Creatures: id, name, modelPath, displayId, position, orientation,
level, health/mana, faction, scale, behavior mode, wander/aggro/
leash radii, respawn time, flag set (hostile/questgiver/vendor/
trainer), patrol-path waypoint count.

Objects: uniqueId, path, type (m2/wmo), position, rotation, scale.

Both kinds support --json for downstream scripts. Useful for
spot-checking individual records discovered via list-zone-spawns or
audit-zone-spawns.

Verified: random-populated zone → info-spawn creature 0 shows
Spider with all behavior fields populated; object 0 shows mushroom
WMO with rotation/scale.
2026-05-07 17:10:25 -07:00
Kelsi
d64d188b13 feat(editor): add --displace-mesh for heightmap displacement on existing meshes
Offsets each vertex along its current normal by heightmap brightness
× scale. UVs determine where each vertex samples the heightmap;
sampling uses bilinear filtering with UV-wrap so repeating UVs
tile correctly.

Pairs naturally with --gen-mesh-grid: gen a flat grid, then
--displace-mesh with a noise PNG to create procedural terrain. Or
use it on a sphere to make a bumpy planet, on a cylinder for tree-
bark deformation, etc.

Output reports the actual delta range produced so the user can
gauge the scale. A hint suggests running --smooth-mesh-normals
afterward since the post-displacement shading would otherwise
follow the original (now-stale) flat normals.

Verified pipeline: --gen-mesh-grid (32×32, 1089 verts) → --gen-
texture-noise → --displace-mesh (scale 2) → bounds X/Y stay ±5
while Z spans 0..1.93.
2026-05-07 16:53:36 -07:00
Kelsi
b5bd7cdc05 feat(editor): add --gen-mesh-grid subdivided plane
Flat plane on the XY plane subdivided into NxN cells. Useful for
LOD demos, deformable surfaces (later --displace passes), and
testbench geometry that needs many triangles for benchmarking.

Default size 1.0 (centered on origin), subdivisions hard-capped at
256 so a typo can't generate 130k+ vertices accidentally.

Vertex count is (N+1)² and triangles are 2N², both reported in the
output so the user sees the budget. Verified: N=16 size=4 produces
289 verts / 512 tris.
2026-05-07 16:36:50 -07:00
Kelsi
c9f054657d feat(editor): add --gen-texture-rings concentric ring pattern
Concentric circles centered on the image, alternating between two
colors every ringPx pixels. Useful for archery targets, magic-seal
floors, dartboards, hypnosis backdrops — anywhere a circular
alternation reads as intentional design.

Args: <out.png> <colorAHex> <colorBHex> [ringPx] [W H]
Defaults: ringPx 16, 256x256.

9th procedural texture pattern (joining solid, checker, grid,
vertical/horizontal/radial gradients, noise, stripes, dots).
Matches the established hex parser + size guards.
2026-05-07 16:20:27 -07:00
Kelsi
2a0cae36d4 feat(editor): add --gen-texture-dots polka-dot pattern
Solid background with circular dots arranged on a regular grid.
Useful for fabric/clothing textures, game-board patterns, mushroom
caps, and decorative tiling.

Args: <out.png> <bgHex> <dotHex> [radius] [spacing] [W H]
Defaults: radius 8, spacing 32, 256x256.

Adds the 8th procedural texture pattern (joining solid, checker,
grid, vertical/horizontal/radial gradients, noise, stripes).
Verified: 128x128 white-bg / red-dot at radius 12 + spacing 48
writes successfully.
2026-05-07 16:03:51 -07:00
Kelsi
6a7ea6dcfc feat(editor): in-editor "Audit Spawns Against Terrain" menu
EditorApp::auditSpawnsAgainstTerrain(threshold) counts every
creature + object whose Z is more than `threshold` yards off the
sampled terrain. Returns the issue count; non-mutating.

Generate menu gets a new "Audit Spawns Against Terrain" item that
runs the audit at the default 5y threshold and shows a toast: a
clean count if everything's fine, or the issue count + a hint to
run "Snap All" if not. Surfaces placement bugs without dropping
to the CLI.

Pairs with the existing "Snap All Spawns to Ground" so the
workflow stays inside the editor: audit → see count → snap →
audit again.
2026-05-07 15:47:26 -07:00
Kelsi
6c84a8da6c feat(editor): add --gen-random-project for batch zone generation
Generates <count> random zones in a single pass. Names default to
"Zone1, Zone2..."; tile coordinates step outward from (32, 32) in
a small raster so the zones don't overlap. Each zone gets a
unique sub-seed (seed + n) so its random content differs from its
siblings.

Args:
  <count>          required (1..100)
  --prefix S       name prefix (default "Zone")
  --seed N         base seed (default 100)
  --creatures N    per zone (default 20)
  --objects N      per zone (default 10)
  --items N        per zone (default 25)

Useful for spinning up a multi-zone playtest project from a single
command. Verified: count=3 with prefix Test produces Test1/Test2/
Test3 each with 4 creatures + 2 objects + 5 items, all on separate
tile coordinates around (32,32).
2026-05-07 15:31:20 -07:00
Kelsi
e316cae94c feat(editor): add --list-project-spawns cross-zone spawn listing
Project-wide companion to --list-zone-spawns. Combines creatures +
objects across every zone into one listing keyed by (zone, kind,
name) with position + a per-kind extra column (level for creatures,
scale for objects). Both human (table) and --json output.

Useful for project-wide review and for piping into spreadsheets via
--json — the zone column lets a pivot table group/filter by zone
which the per-zone command can't.
2026-05-07 15:15:20 -07:00
Kelsi
fba11943e3 feat(editor): add --gen-random-zone end-to-end one-shot generator
Composes scaffold-zone + random-populate-zone + random-populate-
items into a single invocation. Useful for "I just want a complete
test zone, don't make me chain three commands" — common case for
playtest fixtures and CI scenario setup.

Args:
  <name>          required (becomes the slug)
  [tx ty]         optional (default 32 32)
  --seed N        default 42 (items use seed+1 so they don't
                  correlate with creature/object placements)
  --creatures N   default 20
  --objects N     default 10
  --items N       default 25

Each sub-step's output streams through. If any step fails the
generator stops and reports which one. Slug-cleans the name to
match scaffold-zone's expectations.

Verified: TestZone with 5/3/8 counts produces a complete zone dir
containing creatures.json, items.json, objects.json, the .whm/.wot
tile pair, and zone.json.
2026-05-07 14:59:42 -07:00
Kelsi
998ee7ce04 feat(editor): add --list-zone-spawns combined creature+object listing
One-command survey of "what's in this zone" without running both
--info-creatures and --info-objects separately. Pretty-prints two
tables (creatures and objects) with idx / level-or-type / position /
key-field columns. Also supports --json for downstream scripts.

Useful for quick spot-checks during placement and for piping into
review notes — the JSON form makes it easy to grep/jq for
specific entries.
2026-05-07 14:43:46 -07:00
Kelsi
bc5d22e357 feat(editor): zone audio panel gets "Play" preview buttons
Adds a small "Play" button next to the music and ambience dropdowns
that hands the selected file off to the OS default audio player —
'open' on macOS, 'xdg-open' on Linux, 'start ""' on Windows.
Buttons only appear when a file is set.

Avoids pulling SDL_mixer (or any new dep) into the editor binary
just for the preview workflow. Designers already have a working
audio player; this just routes the file there with one click
instead of having them hunt through Data/Sound/Music in their file
manager.

Closes the third leg of the original "dropdown + browse + play
audio to hear it" ask: dropdown is the auto-scanned combo from the
prior commit; browse-via-real-files works through the dropdown;
play preview works now via OS shell.
2026-05-07 14:28:52 -07:00
Kelsi
96ec734d06 feat(editor): add --audit-project-spawns project-wide placement audit
Project-wide companion to --audit-zone-spawns. Spawns the binary
per-zone (only those with creatures.json or objects.json),
streams the per-zone reports, and exits 1 if any zone has spawns
flagged.

Optional --threshold N flag is forwarded to each sub-invocation so
projects with stricter or looser placement requirements get
consistent audits across zones.

CI-friendly pre-release placement check: drop into a pipeline,
fail the build if any spawn is more than N yards off terrain.
2026-05-07 14:27:51 -07:00
Kelsi
f870a516dd feat(editor): in-editor "Snap All Spawns to Ground" menu
Adds EditorApp::snapAllSpawnsToGround() and a menu item under
Generate, mirroring the --snap-zone-to-ground CLI for users who
want the action without context-switching to a terminal.

Walks every NPC + object, casts a downward ray from baseZ+500y,
and writes the hit Z into spawn.position.z. Patrol waypoints on
each NPC are snapped too. Marks the project dirty + auto-save
pending so the change persists without a manual Ctrl+S.

Existing per-selection "Snap Ground" button on the right panel is
unchanged; this is the bulk version for "I just edited terrain
under a populated area."
2026-05-07 14:12:39 -07:00
Kelsi
f589fa20ce feat(editor): add --audit-zone-spawns to flag misplaced spawns
Non-destructive companion to --snap-zone-to-ground. Loads the
zone's terrain, samples the height under each creature + object,
and flags any whose Z is more than <threshold> yards (default 5)
off from the terrain.

Useful for surveying placement issues before deciding whether to
run --snap-zone-to-ground (which would silently rewrite every
spawn). The flagged report shows kind / delta / spawnZ / terrainZ /
name so the user can spot whether the spawn is buried, floating,
or just out of bounds.

Threshold configurable via --threshold N. Exit 1 if any issue is
flagged so CI can gate on placement validity.

Verified: zone with FloatyMurloc at Z=5000 (vs terrain Z=99.9)
correctly flags it with +4900.1 delta; GroundedWolf at Z=100 is
within threshold and not flagged.
2026-05-07 13:57:25 -07:00
Kelsi
8f17f5ae34 feat(editor): add --snap-project-to-ground orchestrator
Project-wide companion to --snap-zone-to-ground. Spawns the binary
per-zone (only zones that actually have creatures.json or
objects.json — pure-terrain zones are skipped to avoid noise),
streams the per-zone output, then aggregates a summary.

Useful after a project-wide terrain regen, or after batch-running
--random-populate-zone across multiple zones — one command snaps
all the spawns at once.

Verified: 2-zone test where only z1 has populated content correctly
selects z1 alone, snaps 3 creatures + 1 object, exits 0.
2026-05-07 13:42:19 -07:00
Kelsi
ddea06a0e4 feat(editor): add --info-project-audio cross-zone audio rollup
Project-wide companion to --info-zone-audio. Walks every zone in
<projectDir>, reads the audio fields, emits a per-zone yes/no
table for music + ambience plus the volume sliders. Counts how
many zones have music and how many have any ambience set so the
header tells you at a glance how much audio work remains.

Useful for spotting zones still missing audio assignment before a
release pass — rather than opening each zone in the editor to
check.

Verified: 2-zone project where only A has audio configured →
header reports "with music: 1, with ambience: 1" and the per-zone
table shows A=yes/yes, B=no/no.
2026-05-07 13:27:16 -07:00