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).
This commit is contained in:
Kelsi 2026-05-09 15:44:26 -07:00
parent 062258de8a
commit 81b1897a24
8 changed files with 621 additions and 0 deletions

View file

@ -947,6 +947,16 @@ void printUsage(const char* argv0) {
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(" --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(" --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");