feat(editor): add WMNL (Minimap Multi-Level) — 110th open format

Novel replacement for the WorldMapTransforms.dbc +
WorldMapOverlay.dbc pair vanilla used to describe zones
with multiple vertical layers visible on the minimap
(Stormwind has Old Town / Cathedral / Keep at three
altitudes; Dalaran has Sewers / Street / Above Street /
Floating; Undercity has 5 distinct levels Sewer to
Throne Room). Each entry binds one (mapId, areaId,
levelIndex) triplet to a Z-range, minimap layer texture,
and display label.

The catalog acts as a per-level overlay on top of WMPX
world-map mappings: at every camera tick, the minimap
renderer queries findContainingZ(playerZ) to swap the
overlay layer when the player crosses a floor boundary.

Three preset emitters one per layered city: makeStormwind
(3 levels), makeDalaran (4 levels), makeUndercity (5
levels — deepest stack). Z-ranges abut precisely to
ensure clean transitions: Sewer Z[-110, -85), Canal
Z[-85, -65), Outer Ring Z[-65, -45), Inner Ring Z[-45,
-20), Throne Z[-20, 30) — half-open intervals so the
boundary Z value belongs to the upper level.

Validator's most novel checks combine grouping +
geometric constraints unique to multi-level layouts:
- per-area levelIndex uniqueness (no two levels at the
  same index — picker UI would show duplicate slot)
- per-area Z-range non-overlap (overlapping ranges
  would cause minimap-flicker as the player crosses
  the overlap region; the renderer can't decide which
  layer to display)
Plus the standard: id+name+areaId required, minZ<maxZ
(non-empty range), no duplicate levelIds.

Format count 109 -> 110. CLI flag count 1191 -> 1196.
This commit is contained in:
Kelsi 2026-05-10 02:03:43 -07:00
parent 4aa2138749
commit d49080db92
10 changed files with 769 additions and 0 deletions

View file

@ -2293,6 +2293,16 @@ void printUsage(const char* argv0) {
std::printf(" Export binary .wrpr to a human-editable JSON sidecar (defaults to <base>.wrpr.json; emits both unlockedItemIds and unlockedRecipeIds as JSON arrays of IDs; standingTier derived label \"Friendly/Honored/Revered/Exalted\" added for readability)\n");
std::printf(" --import-wrpr-json <json-path> [out-base]\n");
std::printf(" Import a .wrpr.json sidecar back into binary .wrpr (grantsTabard/grantsMount accept bool OR int; both unlock arrays serialize as plain JSON int arrays; standingTier label is informational only — minStanding int is authoritative)\n");
std::printf(" --gen-mnl <wmnl-base> [name]\n");
std::printf(" Emit .wmnl 3 Stormwind minimap levels (Old Town Z 0-80 / Keep Approach Z 80-130 / Throne Room Z 130-200) at mapId 0 / areaId 1519\n");
std::printf(" --gen-mnl-dalaran <wmnl-base> [name]\n");
std::printf(" Emit .wmnl 4 Dalaran minimap levels (Sewers / Street / Above Street / Floating Cathedral) — Wrath's most vertical city\n");
std::printf(" --gen-mnl-undercity <wmnl-base> [name]\n");
std::printf(" Emit .wmnl 5 Undercity minimap levels (Sewer / Canal / Outer Ring / Inner Ring / Throne Room) — deepest layered city in EK\n");
std::printf(" --info-wmnl <wmnl-base> [--json]\n");
std::printf(" Print WMNL entries (id / map / area / levelIndex / Z-range / displayName / name)\n");
std::printf(" --validate-wmnl <wmnl-base> [--json]\n");
std::printf(" Static checks: id+name+areaId required, minZ<maxZ, no duplicate levelIds; per-area: no two levels at same levelIndex (picker UI conflict), no Z-range overlap between sibling levels (minimap flicker in overlap region); warns on empty texturePath (untextured layer) or empty displayName (blank picker entry)\n");
std::printf(" --catalog-pluck <wXXX-file> <id> [--json]\n");
std::printf(" Extract one entry by id from any registered catalog format. Auto-detects magic, dispatches to the per-format --info-* handler internally, then prints just the matching entry. Primary-key field is auto-detected (first *Id field, or first numeric)\n");
std::printf(" --catalog-find <directory> <id> [--magic <WXXX>] [--json]\n");