feat(editor): add WHLD (Instance Lockout Schedule) open catalog format

Open replacement for the engine-side instance reset timer logic
plus the per-map InstanceTemplate.dbc reset fields. Defines how
often each (map × difficulty) combination resets its lockout,
how many boss kills each character can claim per lockout window,
and the number of bonus rolls available (Cataclysm+ stub for
forward compatibility).

One entry per (map × difficulty × group size). Icecrown Citadel
10-Normal weekly, ICC 25-Normal weekly, ICC 10-Heroic weekly,
and ICC 25-Heroic weekly are four separate entries with the same
mapId but different difficultyId and resetIntervalMs.

Cross-references back to WMS (mapId), WCDF (difficultyId), and
forward to WBOS — the encounters bound to one lockout are the
WBOS entries whose (mapId, difficultyId) pair matches.

Four lockout kinds capture the canonical reset cadences:
  - Daily (24h, 86400000ms) — heroic dungeons, daily quests
  - Weekly (7d, 604800000ms) — raid lockouts
  - SemiWeekly (3.5d, 302400000ms) — Cata+ split lockouts
  - Custom (arbitrary intervalMs) — Wintergrasp 2.5h, holiday
    events with non-standard cadence

nextResetMs(lockoutId, currentMs) is the engine helper that
returns the next reset wall-clock millis after a given current
time, rounded up to the nearest interval boundary. The engine
overrides the epoch with its configured server reset time
(typically Tuesday 8:00am server-local), but the catalog
provides the interval shape.

The info renderer pretty-prints intervals: 86400000ms reads as
"1d", 9000000ms as "150m", which matches how server admins
think about reset cadences.

Three preset emitters: --gen-hld (4 ICC raid weekly lockouts),
--gen-hld-dungeon (4 5-man heroic daily lockouts),
--gen-hld-event (3 world-event lockouts including Wintergrasp's
canonical Custom 2.5h interval).

Validation enforces id+name+kind+resetIntervalMs presence, no
duplicate ids; warns on non-standard raidGroupSize, kind/interval
mismatches (Daily kind without 24h interval, Weekly kind without
7d interval), and 0 boss kill cap (instance grants no
lockout-bound progress, every visit is fresh).

Wired through the cross-format table; WHLD appears in all 18
cross-format utilities. Format count 91 -> 92; CLI flag count
1062 -> 1067.
This commit is contained in:
Kelsi 2026-05-09 23:51:49 -07:00
parent b16ee9886b
commit 321c2610d0
10 changed files with 670 additions and 0 deletions

View file

@ -2041,6 +2041,16 @@ void printUsage(const char* argv0) {
std::printf(" Export binary .wbos to a human-editable JSON sidecar (defaults to <base>.wbos.json)\n");
std::printf(" --import-wbos-json <json-path> [out-base]\n");
std::printf(" Import a .wbos.json sidecar back into binary .wbos (all per-entry fields preserved verbatim)\n");
std::printf(" --gen-hld <whld-base> [name]\n");
std::printf(" Emit .whld 4 ICC raid weekly lockouts (10N / 25N / 10H / 25H) — 12 boss kill cap, 7-day reset\n");
std::printf(" --gen-hld-dungeon <whld-base> [name]\n");
std::printf(" Emit .whld 4 5-man heroic dungeon daily lockouts (HoR / FoS / PoS / TotC) — 24h reset, 5-player size\n");
std::printf(" --gen-hld-event <whld-base> [name]\n");
std::printf(" Emit .whld 3 world-event lockouts (Brewfest daily / Hallow's End daily / Wintergrasp 2.5h Custom interval)\n");
std::printf(" --info-whld <whld-base> [--json]\n");
std::printf(" Print WHLD entries (id / map / diff / kind / interval (formatted) / boss kills / size / bonus rolls / name)\n");
std::printf(" --validate-whld <whld-base> [--json]\n");
std::printf(" Static checks: id+name+kind+interval required, no duplicate ids; warns on non-standard group size, kind/interval mismatches (Daily not 24h, Weekly not 7d), 0 boss kill cap\n");
std::printf(" --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");