Novel replacement for the implicit chat-link format templates
vanilla WoW carried in client-side LUA — each link kind (item /
quest / spell / achievement / talent / trade-skill) had a hard-
coded sprintf template baked into ChatFrame_OnHyperlinkClick
with no formal data-driven extension point. Each WLNK entry
binds one hyperlink kind to its sprintf-style chat-link
template (with %%d/%%s placeholders for link parameters),
tooltip-popup template, quality color (RGBA), icon source
rule, and server-lookup requirement flag.
Three presets covering the link-kind spectrum:
--gen-lnk-std 4 standard hyperlink kinds (Item with
classic 4-rune-slot template / Quest
yellow / Spell white / Achievement with 9
placeholders for criteria-progress data)
--gen-lnk-talent 2 less-common kinds (Talent green for
passive enhancements / Trade-skill recipe
orange with server lookup for ingredients)
--gen-lnk-quality 3 Item-kind variants distinguished by
quality color (Common gray 0x9D9D9D / Epic
purple 0xA335EE / Legendary orange
0xFF8000) — chat composer picks variant by
item quality at link time
Validator catches: id+name+linkTemplate required, linkKind
0..5, no duplicate linkIds. CRITICAL: linkTemplate MUST contain
at least one %%d/%%s placeholder — composer would emit a static
string regardless of input (every link rendering identically
regardless of which item/spell/quest was clicked). Walks the
template character-by-character counting valid format
specifiers (%%d, %%s, %%u, %%i, %%x, %%X, with %%%% literal
double-percent excluded). Warns on > 12 placeholders (unusual,
verify intentional), colorRGBA=0 (fully transparent — link text
invisible), and requireServerLookup=true with empty
tooltipTemplate (server data has nowhere to render).
Format count 145 -> 146. CLI flag count 1508 -> 1515.
Adds --export-wcfr-json / --import-wcfr-json with the established
readEnumField template factoring int+name dual encoding for both
outputStatKind ("ap"/"sp"/"crit"/"dodge"/"parry"/"hit"/
"spellcrit"/"haste") and inputStatKind ("str"/"agi"/"sta"/
"int"/"spi"). Fixed-point conversion ratios (uint32) preserved
through JSON.
All 3 presets (warrior/mage/rogue) byte-identical binary
roundtrip OK including the level-gated Warrior parry formula
(levelMin=30, levelMax=60).
Live-tested applicability-quad duplicate validator: hand-added
a copy of the Rogue Agi->Crit formula with a new formulaId 99
but the SAME (output=crit, input=agi, classMask=0x10, levelMin=
1) quad. Validator correctly errored: "duplicate (output=crit,
input=agi, classMask=0x16, levelMin=1) — runtime stat-compute
would apply both formulas, doubling the contribution". Catches
the class of stat-balance bugs where a copy-paste of one
formula would silently double an entire stat-conversion path
without breaking the build.
CLI flag count 1506 -> 1508.
Originally attempted WMRR (instance lockouts) for this slot but
hit a name-collision with the existing WHLD catalog at the same
file path. Reverted plumbing additions and pivoted to WCFR — a
distinct, complementary domain not covered by any existing
format.
Novel replacement for the per-stat-conversion ratios vanilla
WoW carried in the gtChanceTo*.dbc gameobject tables + the
per-class hard-coded constants in the server's StatSystem (the
"Strength gives 2 AP for Warriors but 1 AP for Mages" rule was
hard-coded; the "1 Agility = 1 Crit% for Hunters but 0.5 Crit%
for Druids" was hard-coded). Each WCFR entry binds one
(outputStat, inputStat, classMask, level-band) tuple to its
conversion ratio in fixed-point units (fp_x100: 100 = 1.0).
Three presets demonstrating per-class ratio variation:
--gen-cfr-warrior 4 Warrior formulas (Str->AP 2.0 / Agi->
Crit 0.05% / Agi->Dodge 0.05% / Str->
Parry 0.04% gated to level 30+)
--gen-cfr-mage 3 Mage formulas (Int->SpellPower 1.0 /
Int->SpellCrit 0.02% / Spi->OOC SpellPower
0.50)
--gen-cfr-rogue 4 Rogue formulas (Str->AP 1.0 / Agi->AP
1.0 / Agi->Crit 0.07% / Agi->Dodge 0.07%)
— Rogue Agi->Crit ratio (7) significantly
better than Warrior's (5), demonstrating
the per-class differentiation the format
captures
Validator catches: id+name required, outputStatKind 0..7,
inputStatKind 0..4, no duplicate formulaIds, no zero
conversionRatio (no-op formula). CRITICAL: no duplicate
(output, input, classMask, levelMin) quad — runtime stat-
compute would apply both formulas, doubling the contribution.
levelMax >= levelMin when set. Warns on conversionRatio > 100x
(likely units-mismatch typo from porting a percentage table
without dividing by 100).
Format count 144 -> 145. CLI flag count 1499 -> 1506.
Adds --export-wcam-json / --import-wcam-json with the established
readEnumField template factoring int+name dual encoding for
purposeKind ("cinematic"/"combat"/"mounted"/"vehicle"/
"cutscene"/"photomode"). Float fields (FOV, distance, pitch,
yaw, shoulder offset) preserved bit-for-bit through JSON.
All 3 presets (combat/mounted/cinematic) byte-identical binary
roundtrip OK including the Cinematic Portrait preset's offbeat
yaw=15deg + 35deg telephoto + head-bone tracking combination.
Live-tested gimbal-lock validator: hand-mutated Cinematic
Establishing preset pitch from -30 to -95 (beyond the -89
gimbal-lock limit). Validator correctly errored:
"pitchDegrees=-95.000000 gimbal-locks the camera (must be
within (-89, +89))". Catches the class of cinematic-camera bugs
where a pitch of ±90 mathematically aligns with the world up
vector and causes the camera basis to collapse.
CLI flag count 1497 -> 1499.
Novel format covering what vanilla WoW handled with hard-coded
camera profiles in the client's CameraMgr (the standard third-
person camera, the flight-path camera, vehicle cameras and
cinematic cameras were all bespoke C++ classes with no data-
driven extension point). Each WCAM entry binds one camera
preset to its FOV, distance, pitch/yaw offsets, shoulder offset,
focus-bone tracking target (M2 bone index, 0xFFFF = follow root),
motion damping curve (0=instant follow / 255=maximum lag), and
intended purpose (Cinematic / Combat / Mounted / Vehicle /
Cutscene / PhotoMode).
Three presets covering common camera scenarios:
--gen-cam-combat 3 Combat variants (default 75deg / wide
ranged 90deg / tight melee 60deg
shoulder-cam tracking chest bone) with
low motion damping (8-15) for responsive
tracking
--gen-cam-mounted 2 Mounted variants (ground 80deg pulled-
back / flying 85deg high-pitch wing-frame)
with medium-high damping (60-90) for
smooth turning
--gen-cam-cinematic 3 Cinematic angles (over-shoulder
dialogue 50deg telephoto / wide
establishing 100deg / portrait 35deg
3/4-face composition with yaw offset and
head-bone tracking) with high damping
(180-220) for film-quality motion
Validator catches: id+name required, purposeKind 0..5, no
duplicate presetIds, FOV in (0,180) (zero/negative makes no
sense, >=180 inverts the view frustum), distanceFromTarget >= 0
(negative places camera in front of target). CRITICAL: pitch
within (-89, +89) — beyond gimbal-locks the camera. Warns on
FOV outside 30..120 player-comfort range (motion-sickness risk
or extreme telephoto compression), distanceFromTarget < 0.5m
(clips into model), and yawOffsetDegrees beyond ±180° (wraps to
smaller equivalent angle — simplify).
Format count 143 -> 144. CLI flag count 1490 -> 1497.
Adds --export-wcmd-json / --import-wcmd-json with the established
readEnumField template factoring int+name dual encoding for both
minSecurityLevel ("player"/"helper"/"moderator"/"gamemaster"/
"admin") and category ("info"/"movement"/"communication"/
"admincmd"/"debug"). Aliases array serialized as JSON string
array.
All 3 presets (basic/movement/admin) byte-identical binary
roundtrip OK including the movement preset's multi-alias
commands (/stand has aliases ["standup", "su"]).
Live-tested flat-namespace collision validator: hand-mutated
/stand (cmdId=11) to add 'sit' as an alias, colliding with the
canonical /sit command. Validator correctly errored: "alias
'sit' collides with another command name or alias — chat
parser would dispatch ambiguously". Catches the class of typo
bugs where a new alias would shadow an existing command, with
the failure surface being silent dispatch ambiguity rather
than an error.
CLI flag count 1488 -> 1490.
Novel replacement for the implicit slash-command registry
vanilla WoW carried in the client's ChatFrame.lua + server-side
per-command CommandHandler hooks (no formal data-driven catalog;
commands were registered ad-hoc with hard-coded security checks
scattered across LevelMgr / WorldMgr / CharacterMgr). Each WCMD
entry binds one command name to its aliases, minimum security
level required, argument schema string, help text, per-player
throttle in ms, hidden flag (for debug-only commands), and
category.
Three presets covering the security-tier spectrum:
--gen-cmd-basic 4 standard player Info commands (/who
/played /time /ginfo) at Player security
with no throttle
--gen-cmd-movement 3 emote-style Movement commands (/sit
/stand /sleep) with short typing-speed
aliases ("sd" / "su" / "sd")
--gen-cmd-admin 3 GameMaster-only Admin commands
(/announce 5s throttle / /kick 2s
throttle / /ban 10s throttle —
demonstrating per-command rate-limiting
to prevent admin-spam abuse)
Validator catches: id+command required, minSecurityLevel 0..4,
category 0..4, no duplicate cmdIds. CRITICAL: command names
AND aliases share one flat namespace (chat parser dispatches
uniformly by typed string) — duplicate name across canonical+
aliases errors. Warns on uppercase chars in names (parser is
case-insensitive but convention is lowercase), Admin-category
command at Player/Helper security level (likely security
misconfiguration — admin commands usually require GameMaster+),
throttleMs > 60000 (likely ms-vs-s units typo — 60+ second
throttle is nearly unusable), self-alias (canonical already
matches), and empty helpText (/help would show the command
without description).
Format count 142 -> 143. CLI flag count 1481 -> 1488.
Adds --export-wtur-json / --import-wtur-json with the established
readEnumField template factoring int+name dual encoding for
triggerEvent ("login"/"zoneenter"/"levelup"/"itempickup"/
"skilltrain"). Title/body/targetUIElementName multibyte text
preserved through JSON.
All 3 presets (newbie/levelup/bg) byte-identical binary roundtrip
OK including the BG preset's per-mapId trigger gating
(AV=30/WSG=489/AB=529).
Live-tested readability validator: hand-mutated Welcome step
(tutId 1) hideAfterSec from 30 to 3 in JSON. Validator
correctly errored: "hideAfterSec=3 is below 5s — popup vanishes
before the player can read it". Catches the class of UX bugs
where overly-aggressive auto-dismiss timers make tutorials
unhelpful (player sees a flash they can't process).
CLI flag count 1479 -> 1481.
Novel format covering what vanilla WoW had as a hard-coded LUA
tipbox sequence (TutorialFrame.xml + Tutorial.lua client-side
with no data-driven extension point). Each WTUR entry binds
one tutorial step to a trigger event (Login / ZoneEnter /
LevelUp / ItemPickup / SkillTrain), an ordered stepIndex within
that trigger group, a title + body for the popup, optional UI-
element name to highlight, and a hide-after auto-dismiss timer.
Three presets covering the most common tutorial scenarios:
--gen-tut-newbie 5 first-login steps (Welcome / Camera /
Interact NPCs / OpenQuestLog / OpenBags)
with 30s auto-dismiss and UI-element
highlight names ("MovementHint",
"QuestLogButton", "BagButton")
--gen-tut-levelup 3 LevelUp-trigger steps gated on
specific level milestones (level 2
spellbook hint / level 5 trainer visit /
level 10 talent unlock)
--gen-tut-bg 3 ZoneEnter-trigger steps gated to BG
mapIds (AV=30 explains 40v40 / WSG=489
explains capture-flag / AB=529 explains
control-point) — explains each ruleset
on first BG entry
Validator catches: id+name+title+body required, triggerEvent
0..4, stepIndex > 0 (sequence starts at 1), no duplicate
tutIds, no duplicate (event,value,step) triples (sequence
ordering ambiguity). CRITICAL: hideAfterSec MUST be 0 (no
auto-dismiss) OR >= 5s — else popup vanishes before player
can read it. Warns on Login event with non-zero triggerValue
(dead data, Login is unconditional), non-Login event with
triggerValue=0 (would fire for ALL events of that kind), and
body length < 10 chars (likely placeholder text).
Format count 141 -> 142. CLI flag count 1472 -> 1479.
Adds --export-wswp-json / --import-wswp-json with the established
readEnumField template factoring int+name dual encoding for
conditionKind ("always"/"zoneonly"/"classonly"/"raceonly"/
"genderonly"). Signed gain field (gainAdjustDb_x10, int16)
preserved bit-for-bit through JSON.
All 3 presets (bosses/race/ui) byte-identical binary roundtrip
OK.
Live-tested both the duplicate-trigger error AND the same-
priority warning in one mutation: copied Nefarian rule's
trigger triple to match Onyxia (originalSoundId=1234,
ZoneOnly, conditionValue=249). Validator emitted BOTH:
ERROR: duplicate trigger triple
WARNING: same priorityIndex=100 — tie-break order undefined
Demonstrates that the validator catches both layers of the
collision (the deterministic data tie + the priority-based
disambiguation that the runtime would have used to resolve it).
CLI flag count 1470 -> 1472.
Novel format covering a need vanilla WoW lacked entirely:
priority-based sound substitution. Blizzard had no formal
mechanism for swapping a stock SoundEntry for a custom
replacement conditionally on zone/class/race/gender; the
closest equivalents were patch-level SoundEntries.dbc edits
with no condition support. Each WSWP entry binds one
(originalSoundId, condition) trigger to a replacementSoundId,
a priority index for tie-breaking (higher wins), and an
optional gain adjustment in 0.1 dB units (range ±30 dB
practical mixer limit).
Three presets covering common substitution scenarios:
--gen-swp-bosses 3 raid-boss zone-only swaps (Onyxia roar
in Onyxia's Lair / Ragnaros emerge in
Molten Core +2dB / Nefarian shout in BWL).
Priority 100 — beats global rules
--gen-swp-race 3 race-conditional voice swaps (BloodElf
priest / Tauren shaman / Undead warlock
cast voices). Priority 50
--gen-swp-ui 3 always-on UI sound swaps (level-up /
quest-complete / mount-up) at priority
10 with +3dB gain (boss/race overrides
win the priority fight)
Validator catches: id+name+original+replacement required,
conditionKind 0..4, no duplicate ruleIds, no self-replacement
(orig==repl is a no-op slot), non-Always kinds require non-
zero conditionValue (kind without target = matches everything,
duplicating Always semantics). CRITICAL: no duplicate
(originalSoundId, conditionKind, conditionValue) trigger
triple — runtime would have two rules for the same trigger
without a tie-breaker. Warns on priorityIndex=0 (rule never
wins), |gainAdjustDb_x10| > 300 (clip risk), Always condition
with non-zero conditionValue (dead data ignored at runtime),
and same-priority within same originalSoundId (tie-break
undefined when both rules' conditions match simultaneously).
Format count 140 -> 141. CLI flag count 1463 -> 1470.
Adds --export-wbrd-json / --import-wbrd-json. battlegroundName
emitted as informational field; battlegroundId int is
authoritative. All 3 presets (av/wsg/ab) byte-identical binary
roundtrip OK including the AB preset's weekly bonus quest token
binding.
Live-tested incentive-inversion validator: hand-mutated WSG
bracket 6 (rewardId 15) to swap winHonor and lossHonor — loss
becomes 750, win becomes 375. Validator correctly errored:
"lossHonor=750 > winHonor=375 — losing rewards more than winning
(no win incentive)". Catches the class of reward-config bugs
where misordered fields would silently flip the optimal player
strategy from "play to win" to "AFK and lose for max XP/hour".
CLI flag count 1461 -> 1463.
Novel replacement for the per-BG per-bracket reward
configuration vanilla WoW carried in BattlemasterList.dbc +
the hard-coded honor table in the server's BattlegroundMgr
(the "Mark of Honor" item granted on win/loss was hard-coded
per-BG type with no formal data-driven scaling). Each WBRD
entry binds one (battlegroundId, levelBracket) pair to its
win/loss honor amounts, win/loss marks, the mark itemId, an
optional weekly-bonus item, and a minimum-players-to-start
gate.
Three presets covering canonical vanilla BGs:
--gen-brd-av Alterac Valley reward ladder for brackets
5-6 (51-69 only — AV was endgame). 20
players/side, Mark of AV item 17502
--gen-brd-wsg Warsong Gulch ladder for all 6 brackets
(10-69), 10 players/side, Mark of WSG item
20558, monotonically scaling honor
(50/100/200/350/500/750)
--gen-brd-ab Arathi Basin ladder for brackets 2-6
(20-69), 15 players/side, Mark of AB item
20559, includes weekly bonus quest token
(placeholder itemId 20560)
Validator catches: id+battlegroundId required, bracketIndex
1..6 (vanilla), no duplicate rewardIds, no duplicate
(bgId,bracket) pairs (runtime reward-lookup tie),
bonusItemCount > 0 requires non-zero bonusItemId, minPlayers
ToStart > 0 (else BG queue would never start a match).
CRITICAL: lossHonor <= winHonor (else losing rewards more
than winning, no incentive to play to win — would degenerate
into AFK farming). Warns on winMarks=0 with markItemId set
(vanilla wins always granted at least 1 mark).
Format count 139 -> 140. CLI flag count 1454 -> 1461.
Adds --export-wauh-json / --import-wauh-json with the established
readEnumField template factoring int+name dual encoding for
factionAccess ("both"/"alliance"/"horde"/"neutral"). All 3
presets (stormwind/orgrimmar/bootybay) byte-identical binary
roundtrip OK including the Booty Bay neutral 15%/15% rate
configuration.
Live-tested economic-trap validator: hand-mutated Booty Bay
deposit to 60% + cut to 50% (sum 110%). Validator correctly
errored: "depositRatePct=6000 + cutPct=5000 = 11000 basis
points — seller would lose money on every sale (combined rates
>= 100%)". Catches misconfigured AH rates that would silently
trap players into negative expected returns on every listing.
CLI flag count 1452 -> 1454.
Novel replacement for the implicit per-faction auction-house
policy vanilla WoW carried in AuctionHouse.dbc + the hard-
coded deposit/cut rate constants in the server's AuctionMgr
(the 5% Alliance/Horde rate vs 15% neutral Booty Bay rate
was hard-coded on AH faction id). Each WAUH entry binds one
auction house instance to its faction-access rules,
deposit-rate (% of vendor sell price held as deposit), AH cut
(% taken from final sale price before crediting seller),
allowed listing durations (min/max hours), per-slot copper
fee, and the auctioneer NPC binding.
Three presets capturing canonical vanilla AH instances:
--gen-auh-stormwind Alliance Stormwind Trade District AH
with 5%/5% deposit/cut rates, 12-48hr
listing tiers, NPC Auctioneer Tricket
(creatureId 8666)
--gen-auh-orgrimmar Horde Orgrimmar Valley of Strength AH
with same vanilla rates as Stormwind,
NPC Auctioneer Tahesh (9856)
--gen-auh-bootybay Neutral Booty Bay AH with the famous
15%/15% penalty rates, NPC Auctioneer
Beardo (9858)
Validator catches: id+name required, factionAccess 0..3,
depositRatePct + cutPct each in 0..10000 (basis points), no
duplicate ahIds, no duplicate (faction,name) pairs (UI tab
dispatch tie), no duplicate npcAuctioneerId (gossip dispatch
tie), maxListingDurationHours > 0 and minListing <= maxListing.
CRITICAL: combined depositRatePct + cutPct < 10000 (else
seller would lose money on every successful sale — economic
trap). Warns on combined > 50% (sellers may abandon AH;
verify intentional like neutral AH penalty).
Format count 138 -> 139. CLI flag count 1445 -> 1452.
Adds --export-wprc-json / --import-wprc-json with the established
readEnumField template factoring int+name dual encoding for
triggerEvent ("onhit"/"oncrit"/"oncast"/"ontakedamage"/"onheal"/
"ondodge"/"onparry"/"onblock"/"onkill").
All 3 presets (weapon/ret/rage) byte-identical binary roundtrip
OK including the rage preset's previously-fixed Berserker Rage
proc rule (sourceSpellId=18499, procEffectSpellId=23691 distinct).
Live-tested self-loop validator a second time: re-introduced the
Berserker Rage source==effect bug via JSON edit (set effect back
to 18499). Validator correctly errored: "sourceSpellId ==
procEffectSpellId=18499 on OnCast trigger — infinite proc loop
(effect re-casts itself)". Confirms the round-trip path
preserves the self-loop guard and that the validator is ready
to catch this class of bug whenever a hand-edit reintroduces it.
CLI flag count 1443 -> 1445.
Adds --export-wirc-json / --import-wirc-json. allowedSlotsMask
emitted as both int + readable string (e.g. "Helm|Chest|Leg|
Boot") for tooling readability. Variable-length enchants
serialize as JSON object array of {enchantId, weight}.
All 3 presets (bear/eagle/tiger) byte-identical binary roundtrip
OK including the bear pool's totalWeight=100 (30+50+15+5 enchant
weight distribution).
Live-tested totalWeight mismatch validator: hand-mutated bear
pool enchant[1].weight from 50 to 60 (sum=110) while leaving
totalWeight=100 in JSON. Validator correctly errored:
"totalWeight=100 does not match sum of enchant weights=110 —
loot generator would mis-pick". Catches the class of denormal-
ized-cache-staleness bugs where the loot generator's hot-path
roll uses a precomputed total that no longer matches the
enchant table — players would see wrong rates of each enchant
tier without any obvious symptom.
CLI flag count 1434 -> 1436.
Novel replacement for the random-suffix enchant pool that
vanilla WoW carried in ItemRandomProperties.dbc +
ItemRandomSuffix.dbc (TBC+) + the per-item RandomProperty
rolls baked into the LootMgr. Each WIRC entry binds one
random-property pool to a name suffix ("of the Bear", "of the
Eagle"), a weighted enchant table (variable-length array of
{enchantId, weight}), and the equipment slots + class
restrictions where the suffix can roll.
At loot time, each green+ item rolls one pool based on its
slot, then picks one enchant from that pool weighted by
enchant.weight / totalWeight. The denormalized totalWeight
field is precomputed for the loot generator's hot-path roll.
Three presets covering the canonical vanilla suffix archetypes:
--gen-irc-bear STA-focused for plate slots (Helm/Chest/
Leg/Boot) with 4 weighted +Sta enchants
(3/5/7/10 — middle tier most common at
weight 50/100). Warrior+Paladin+DK class
mask
--gen-irc-eagle INT+STA caster pool for cloth slots with
5 weighted +Int+Sta enchants (3/5/7/10/12).
Mage+Priest+Warlock class mask
--gen-irc-tiger STR+AGI hybrid for leather slots with 5
weighted enchants. Rogue+Hunter+Druid class
mask
Validator catches: id+name required, allowedSlotsMask != 0
(else pool is unreachable — no slot would ever roll it),
non-empty enchant array, no zero-id enchants, no duplicate
enchantIds within same pool (should be merged with summed
weight). CRITICAL: totalWeight MUST equal sum of enchant
weights (else the loot generator's denormalized roll
mis-picks the distribution — players would see wrong rates of
each enchant tier). Warns on enchant weight=0 (never picked,
dead entry to remove or assign weight).
Format count 136 -> 137. CLI flag count 1427 -> 1434.
Adds --export-wbhv-json / --import-wbhv-json with the established
readEnumField template factoring int+name dual encoding for both
creatureKind ("melee"/"caster"/"tank"/"healer"/"pet"/"beast")
and evadeBehavior ("resettospawn"/"healatpath"/"fleetospawn"/
"noevade"). Variable-length specialAbilities serialize as JSON
object array of {spellId, cooldownMs, useChancePct}.
All 3 presets (melee/caster/boss) byte-identical binary
roundtrip OK including the boss preset's 4-ability rotation
with NoEvade and 90s-cooldown Deep Breath.
Live-tested leash<aggro un-killable invariant: hand-mutated
Onyxia boss leashRadius from 999 to 10 (below aggroRadius=50).
Validator correctly errored: "leashRadius=10.000000 <
aggroRadius=50.000000 — creature would evade before engaging
(un-killable from outside the leash)". Catches the class of
spawn-config bugs where a creature aggros at distance X but
evades at distance < X — no player can ever close to melee.
CLI flag count 1425 -> 1427.
Novel replacement for the implicit creature-behavior rules
vanilla WoW carried in creature_template.AIName + per-creature
C++ scripts in the server's ScriptMgr (most rare-elites and
bosses had hand-coded class-derived behaviors). Each WBHV entry
binds one combat behavior archetype to its creature kind (Melee
/ Caster / Tank / Healer / Pet / Beast), aggro / leash radii,
evade-on-leash policy (ResetToSpawn / HealAtPath / FleeToSpawn
/ NoEvade for raid bosses), corpse persistence duration,
default rotation spell, and a variable-length list of special
abilities (spellId + cooldown + use-chance triplets in basis
points).
Three presets covering common archetypes:
--gen-bhv-melee 3 entry-tier melee creatures (Kobold Worker
+ Timber Wolf + Stranglethorn Raptor) with
1 special ability each
--gen-bhv-caster 3 caster patterns (Defias Wizard with
Polymorph + Frost Nova / Murloc Coastrunner
with Frost Bolt + Lesser Heal / Voidwalker
Pet Pattern with Taunt + Sacrifice +
Suffering — Sacrifice intentionally has
useChancePct=0 as owner-triggered, exercising
the validator owner-triggered warning)
--gen-bhv-boss 1 Onyxia-pattern dragon (Tank kind,
NoEvade leash, 600s corpse for 40-man loot
distribution, 4 abilities including 90s-CD
Deep Breath)
Validator catches: id+name required, creatureKind 0..5,
evadeBehavior 0..3, aggroRadius > 0, no duplicate behaviorIds,
no zero-spellId specials, no duplicate spellId within same
behavior. CRITICAL invariant: leashRadius >= aggroRadius (else
creature evades back to spawn before reaching its target —
permanently un-killable from outside the leash radius). Warns
on corpseDuration < 60s (looting may fail in busy zones), and
useChancePct=0 on a special ability (correctly flagged on the
Voidwalker Sacrifice spec — verified live in smoke-test).
Format count 135 -> 136. CLI flag count 1418 -> 1425.
Adds --export-wbnd-json / --import-wbnd-json with the established
readEnumField template factoring int+name dual encoding for both
bindKind ("bindonpickup"/"bindonequip"/"bindonuse"/
"bindonaccount"/"soulbound"/"nobind") and itemQualityFloor
("poor"/"common"/"uncommon"/"rare"/"epic"/"legendary"/
"artifact"/"heirloom"). All 3 presets (vanilla/TBC/WotLK)
byte-identical binary roundtrip OK including the WotLK
Heirloom rule with accountBoundCrossFaction=true.
Live-tested raid-trade-window=0 contradiction validator:
hand-mutated TBC Uncommon rule (ruleId 12) tradableWindowSec
to 0 while keeping tradableForRaidGroup=true. Validator
correctly errored: "tradableForRaidGroup=true with
tradableWindowSec=0 — window expires instantly, equivalent
to no window". Catches a subtle policy-config bug where the
flag claims a feature exists but the duration silently
disables it.
CLI flag count 1416 -> 1418.
Novel replacement for the implicit item-binding policy vanilla
WoW carried in ItemTemplate.bondingType + per-item special-case
rules in the server's LootMgr (the 2-hour raid-loot trade
window was hard-coded; the account-bound-shared-across-faction
rule for heirlooms was a TBC+ addition with no formal data-
driven format). Each WBND entry binds one soulbind rule to its
bind kind (BoP / BoE / BoU / BoA / Soulbound / NoBind),
itemQualityFloor predicate (rule applies to items of this
quality and above unless overridden by a stricter rule),
tradable-window duration, raid-trade allowance, BoE-becomes-
BoP trigger, and cross-faction sharing flag.
Three presets capturing actual expansion-era policy evolution:
--gen-bnd-vanilla 4 rules — Poor=NoBind, Common=BoE,
Uncommon+=BoP no-window, Epic+=Soulbound.
NO raid-trade window — the 1.12 master-
loot drama era
--gen-bnd-tbc 5 rules adding the iconic 2-hour raid-
trade window for BoP items (Uncommon and
Rare quality)
--gen-bnd-wotlk 6 rules adding Heirloom = BindOnAccount
+ cross-faction (Alliance<->Horde via
account-mail) for the WotLK level-1-to-80
twink path
Validator catches: id+name required, bindKind 0..5,
itemQualityFloor 0..7, no duplicate ruleIds, no duplicate
(bindKind,qualityFloor) pairs (resolveForQuality lookup tie).
Hard error: tradableForRaidGroup=true with window=0 (window
expires instantly = no window at all). Warns on contradictions:
tradableForRaidGroup with non-BoP kind, window > 0 without
raid-trade flag, boeBecomesBoP without BoE kind,
accountBoundCrossFaction without BoA kind (all flag-ignored at
runtime).
Format count 134 -> 135. CLI flag count 1409 -> 1416.
Adds --export-wloc-json / --import-wloc-json with the established
readEnumField template factoring int+name dual encoding for both
locKind ("poi"/"rarespawn"/"herbnode"/"mineralvein"/"fishingspot"/
"areatrigger"/"portallanding") and factionAccess ("both"/
"alliance"/"horde"/"neutral"). Float coords (x/y/z) and skill
fields preserved bit-for-bit through JSON.
All 3 presets (poi/herb/rare) byte-identical binary roundtrip
OK including the rare-spawns preset's mixed respawn timers
(1800s..7200s).
Live-tested spawnable-kind respawn=0 validator: hand-mutated
Mor'Ladim's respawnSec to 0 in JSON, validator correctly
errored: "spawnable kind (rarespawn) with respawnSec=0 —
entity would spawn once and never come back". Catches the
common "added a rare spawn but forgot the timer" bug class
where rare-elites silently disappear after first kill.
CLI flag count 1407 -> 1409.
Novel unified replacement for the half-dozen proprietary
location tables vanilla WoW scattered across AreaPOI.dbc
(zone-discovery landmarks), gameobject_template.spawn rows
(herb/mineral/fishing nodes), creature_template rare-spawn
entries, and AreaTrigger.dbc (zone boundary teleports). Each
WLOC entry binds one world coord (mapId, x, y, z) to its kind
(POI / RareSpawn / HerbNode / MineralVein / FishingSpot /
AreaTrigger / PortalLanding), respawn timer, gathering-skill
gate, and on-discovery XP.
Three presets covering the major location flavors:
--gen-loc-poi 4 Alliance POIs (Stormwind/Ironforge/
Goldshire/Sentinel Hill) with discoverable
XP (50..100) and POI-kind iconry
--gen-loc-herb 5 Elwynn/Westfall herb nodes (Peacebloom
skill 1 to Stranglekelp skill 85) with 600s
respawn and Herbalism (skillId 182) gating
--gen-loc-rare 4 vanilla rare-elites with realistic
respawn timers (Mor'Ladim 1hr, Princess
Tempestria 2hr, Foreman Rigger 30min, Lord
Sakrasis 1hr)
Validator catches: id+name required, locKind 0..6,
factionAccess 0..3, no duplicate locationIds, spawnable kinds
(Rare/Herb/Mineral/Fishing) MUST have respawnSec > 0 (else
entity spawns once and never returns — common typo when
adding new spawns). Warns on discoverableXp set with non-POI
kind (XP would never award), requiredSkillId set with non-
gather kind (skill check would never fire), and gather kind
with skill > 0 but level = 0 (every player satisfies — almost
certainly a typo).
Format count 133 -> 134. CLI flag count 1400 -> 1407.
Adds --export-wcra-json / --import-wcra-json. Variable-length
reagent arrays serialize as JSON object array of {itemId, count}
allowing hand-edits of recipe formulas. tradeSkillName field on
export is informational (tradeSkillId int is authoritative).
All 3 presets (alchemy/engineering/blacksmithing) byte-identical
binary roundtrip OK including the 5-reagent Target Dummy
recipe.
Live-tested perpetual-motion validator: hand-mutated Minor
Healing Potion (recipeId 1, produces itemId 118) to add itemId
118 to its reagents. Validator correctly errored: "reagent
itemId equals producedItemId=118 — recipe consumes what it
makes (perpetual-motion bug)" — catches a class of crafting
bugs that would silently let players duplicate items.
CLI flag count 1398 -> 1400.
Novel replacement for the implicit recipe expansion vanilla WoW
carried in SpellReagents.dbc + Spell.dbc effect-24 (CREATE_ITEM)
+ per-trade-skill SkillLineAbility rows. Each WCRA entry binds
one trade-skill recipe spell to its variable-length reagent
list (itemId+count pairs, vanilla cap 8, format cap 32),
produced-item id + count, the trade skill it belongs to, the
minimum skill level to cast, and the source item that teaches
the recipe.
Three presets seeded with canonical vanilla item/spell IDs:
--gen-cra-alchemy 4 potions (Minor/Lesser Healing+Mana,
Greater Healing, Major Mana) using
herb itemIds 2447/765/2450/3357/etc
and Empty Vial / Crystal Vial
--gen-cra-engineering 3 recipes including Target Dummy with
5 reagents (variable reagent count
demonstration); learnedFromItemId
references the recipe blueprint
--gen-cra-blacksmithing 3 recipes covering low/mid/high tiers
(skill 1 / 50 / 235) including Heavy
Mithril Helm with 4 different bar/ore
reagents
Validator catches: id+name+spellId+tradeSkillId+producedItemId+
producedCount required, no duplicate recipeIds, no duplicate
spellIds (cast-handler conflict — two recipes responding to the
same cast), no zero-itemId/zero-count reagents, no duplicate
reagent itemIds within a single recipe (should be merged into
single entry with summed count), no self-reagent (recipe
consuming its own produced item is a perpetual-motion bug).
Warns on requiredSkillLevel > 450 (above WotLK cap) and empty
reagent list (free-to-craft is unusual but allowed for some
alchemy transmutes).
Format count 132 -> 133. CLI flag count 1391 -> 1398.
Adds --export-wqgr-json / --import-wqgr-json with the established
readEnumField template factoring int+name dual encoding for both
questType ("normal"/"daily"/"repeatable"/"group"/"raid") and
factionAccess ("both"/"alliance"/"horde"/"neutral"). Variable-
length prereq + followup quest arrays serialize as JSON int
arrays.
All 3 presets (starter/branched/dailies) byte-identical binary
roundtrip OK including the branched preset's converging DAG
(Q200 -> {Q201, Q202} -> Q203 with Q203 carrying [201, 202] in
its prevQuestIds).
Live-tested DFS cycle detection: hand-mutated Northshire chain
head Q100 to depend on Q104 (the chain's last quest), creating
a 5-node loop. Validator correctly errored: "prereq cycle
detected: 100 -> 104 -> 103 -> 102 -> 101 -> 100 — quests would
be unreachable (progression deadlock)" with the full back-edge
path extracted exactly as WMOD does for addon dep cycles.
CLI flag count 1389 -> 1391.
Novel representation of quest-chain dependencies that vanilla
WoW carried implicitly in QuestRelations.dbc (the prequest
column) + per-quest server scripts. Each WQGR entry binds one
quest to its display name, level/class/race gating,
prerequisite quest list (must be completed first), follow-up
quest hints (next-quest suggestions for the journal UI), and
quest type flags (Normal / Daily / Repeatable / Group / Raid).
Three presets:
--gen-qgr-starter 5-quest linear chain (Northshire human-
starter Q100..Q104, levels 1..8) with
chainHeadHint=1 on Q100
--gen-qgr-branched 4-quest converging DAG (Q200 unlocks
both Q201 + Q202, both required for Q203)
— demonstrates true DAG semantics, not
just linear lists
--gen-qgr-dailies 3 standalone daily quests (Daily type,
no prereqs, no followups)
Validator catches: id+name required, questType 0..4,
factionAccess 0..3, maxLevel >= minLevel, no self-prereq
(catch-22), no missing prereq questId, full DFS cycle detection
on prevQuestIds (progression deadlock — quests would be
unreachable). Reuses the proven cycle-extraction pattern from
WMOD addon manifest (extracts back-edge path so the editor sees
the loop). Warns on followup hint to self/missing-id (advisory
only — followups are hints not contracts) and on
chainHeadHint=1 with non-empty prereqs (contradicts chain-head
semantics).
Format count 131 -> 132. CLI flag count 1382 -> 1389.
Novel format providing what vanilla WoW lacked entirely: a
guild-level shared storage facility (Blizzard added guild banks
in TBC, but the Wowee project provides this format from day one
for the Classic-1.12 server flavor as well as later expansions).
Each WGBK entry binds one guild bank tab to its display label,
slot count (1..98 vanilla cap), deposit-only flag, icon, and a
fixed-size per-guild-rank withdrawal limit array (slots/day cap
per rank 0..7, where rank 0 is GuildMaster, kUnlimited =
0xFFFFFFFF).
Three presets:
--gen-gbk Standard 4-tab bank (General/Materials/
Consumables/Officer) for guildId 1 with
progressive per-rank caps
--gen-gbk-raid 5-tab raid guild (Tier1_BWL/Tier2_AQ40/
Tier3_Naxx + Consumables + Officer) — tier
tabs strictly officer-only with 4-slot/day
cap on rank 1
--gen-gbk-small 2-tab small guild (General + Officer) with
tight 5-slot/day caps below officer rank
Validator catches: id+guildId+tabName required, slotCount 1..98
(vanilla cap), GM withdrawal limit > 0 (rank 0 cannot be locked
out — almost certainly a typo), per-rank monotonicity (lower
rank cannot exceed higher rank's cap — kUnlimited treated as
infinity for compare), no duplicate tabIds, no duplicate
(guildId,tabName) pairs (UI dispatch tie). Warns on depositOnly
flag set with non-zero rank-0 limit (self-contradiction — flag
overrides at runtime but data is contradictory).
Format count 130 -> 131. CLI flag count 1373 -> 1380.
Adds --export-wcst-json / --import-wcst-json. Eight uint stat
fields per entry serialize as JSON ints; className field on
export is informational (classId is the authoritative key).
All 3 presets (warrior/mage/starting) byte-identical binary
roundtrip OK including the 9-entry starting-stats preset that
spans 9 different vanilla classes.
Live-tested monotonicity validator: hand-mutated Mage L20
baseStrength to 21 (below L10=22), validator correctly emitted:
"monotonicity: Mage baseStrength regresses from 22 (L10) to 21
(L20) — likely typo". The level-sorting + adjacent-pair walk
correctly identified the violation across the sparse 6-level
sample.
CLI flag count 1371 -> 1373.
Novel replacement for the per-class per-level base-stat scaling
table that vanilla WoW scattered across CharBaseInfo.dbc +
CharStartOutfit.dbc + GtChanceTo*.dbc + the hard-coded HP/mana-
per-level constants in the server's StatSystem. Each WCST entry
binds one (classId, level) pair to base health, mana, armor, and
the five primary stats (Str/Agi/Sta/Int/Spi).
Sparse design: presets emit ~6 sample levels per class with the
runtime stat-interpolator computing intermediate levels.
Three presets:
--gen-cst-warrior Warrior (classId=1) sparse sample at L1/
10/20/30/40/60. baseMana=0 across all
entries (Warrior uses Rage)
--gen-cst-mage Mage (classId=8) same 6 levels with mana
growth tracking Intellect
--gen-cst-starting All 9 vanilla classes at level 1 — shows
per-class flat starting differences
(Warrior/Paladin high Str; Hunter/Rogue
high Agi; Mage/Priest/Warlock high Int;
Shaman/Druid balanced)
Validator catches: id+classId+level required, classId 1..11,
level 1..60, zero baseHealth (player would die instantly),
duplicate statIds, duplicate (classId,level) pairs (runtime
stat-lookup tie). Warns on classId 6/10 (DK/Monk gap unused
in vanilla), Warrior/Rogue baseMana > 0 (these classes use
Rage/Energy not mana), and per-class monotonicity violations
across all 8 stats — sorts by level, walks adjacent pairs,
flags any stat that regresses as level increases (typo guard).
Format count 129 -> 130. CLI flag count 1364 -> 1371.
Adds --export-wprt-json / --import-wprt-json with the established
readEnumField template factoring int+name dual encoding for both
factionAccess ("both"/"alliance"/"horde"/"neutral") and portalKind
("teleport"/"portal"). Float fields (destX/Y/Z, destOrientation)
preserved bit-for-bit through JSON.
All 3 presets (alliance/horde/teleports) byte-identical binary
roundtrip OK including the Teleport: Orgrimmar Horde-faction
entry that crosses the otherwise-Alliance teleports preset.
Live-tested reagent-mismatch warning: hand-mutated Stormwind
Portal entry (Portal kind) to use Rune of Teleportation (17031
— Teleport reagent), validator correctly emitted: "Portal kind
with reagentItemId=17031 — vanilla group portals require Rune
of Portals (itemId 17032). Verify intentional".
CLI flag count 1362 -> 1364.
Novel replacement for the implicit taxi/zeppelin/boat scheduling
that vanilla WoW drove from a tangle of TaxiNodes.dbc +
TaxiPath.dbc + per-zeppelin GameObject scripts + hard-coded
transport interval timers in the server's MapManager. Each WTSC
entry binds one scheduled passenger route to its origin /
destination coords, vehicle type (Taxi/Zeppelin/Boat/Mount),
departure interval, in-flight duration, capacity, and faction-
access gate.
Initially designed with magic 'WTRN' but discovered collision
with existing trainers catalog (also WTRN) — renamed to 'WTSC'
(Transit SChedule) and updated all CLI flags.
Three presets:
--gen-trn-zeppelins 3 vanilla Horde zeppelin routes
(OG<->UC 240s interval, OG<->Grom'Gol,
UC<->Grom'Gol)
--gen-trn-boats 3 vanilla boat routes (Auberdine<->
Stormwind Alliance, Menethil<->Theramore
Alliance, BootyBay<->Ratchet Neutral
cross-faction)
--gen-trn-taxis 3 taxi gryphon/wyvern routes — capacity=0
indicates solo gryphon ride
CRITICAL scheduling invariant validator catches: when capacity > 0
the departureInterval MUST be >= travelDuration. A zeppelin with
interval=60s + travel=90s with capacity=40 would overflow the
vehicle pool — next zeppelin departs before prior arrives. Solo
gryphon (capacity=0) is exempt because each ride is independent.
Validator also catches: id+name+origin+destination required,
vehicleType/factionAccess range, zero intervals/travel, duplicate
routeIds, duplicate route names. Warns on same-map routes
(originMapId == destinationMapId) — preset taxi route Crossroads
to Razor Hill triggered this warning in smoke-test (both in
Kalimdor mapId=1, intentional).
Format count 127 -> 128. CLI flag count 1346 -> 1353.
Adds --export-wphm-json / --import-wphm-json with the established
readEnumField template factoring int+name dual encoding for the
movementState enum ("idle"/"walk"/"run"/"swim"/"fly"/"sit"/
"mount"/"death").
Both encoding paths verified live: stripped the int field from
exported JSON and re-imported using only the string token —
result byte-identical to the int-keyed form. All 3 presets
(human / orc / undead) byte-identical binary roundtrip OK
preserving Undead shambling variant 38 on Run state and slower
swim transition (400ms vs 350ms for living races).
CLI flag count 1344 -> 1346.
Novel replacement for the implicit movementState->animation
binding that vanilla WoW baked into per-race M2 model files.
Each WPHM entry binds one (raceId, genderId, movementState)
tuple to a base M2 animation sequence id, an optional variant
sequence (drunk-walk, wounded-run), and a blend transition
duration in milliseconds.
8-state machine: Idle / Walk / Run / Swim / Fly / Sit / Mount /
Death. Three presets each emit the full 16 bindings (M+F):
--gen-phm-human 16 bindings with drunk-walk variant on Walk
--gen-phm-orc 16 bindings with AttackRun variant on Run
for war-stance flavor
--gen-phm-undead 16 bindings with canonical shambling variant
(anim 38) on Run for low-health renderer
override + slower swim transition (undead are
awkward in water)
Validator catches: id required, raceId 1..10, genderId 0..1,
movementState 0..7, no duplicate mapIds, no duplicate
(race,gender,state) triples (renderer dispatch ambiguity),
baseAnimId=0 forbidden on non-Idle states (model would freeze
when entering that state). Warns on variantAnimId==baseAnimId
(no-op overhead) and transitionMs > 2000 (would feel like
animation hang).
Format count 126 -> 127. CLI flag count 1337 -> 1344.
Adds --export-wspk-json / --import-wspk-json. spellIds serialize
as JSON int arrays preserving spellbook display order (top-to-
bottom in tab). All 3 presets (warrior/mage/rogue) byte-identical
binary roundtrip OK including Mage Frost tab [116, 122, 10] —
Frostbolt rank 1 still first, Frost Nova second, Blizzard third
after roundtrip.
Importer also restores className via implicit lookup from
classId on the export side, so a hand-edited JSON only needs
classId int — className field is informational.
CLI flag count 1335 -> 1337.
Novel replacement for the implicit per-class spellbook layout
that vanilla WoW derived from SkillLineAbility.dbc + the hard-
coded per-spec tab order baked into the client UI. Each WSPK
entry binds one (classId, tabIndex) pair to an ordered list of
spellIds shown in that spellbook tab.
Three presets seeded with canonical vanilla low-rank spellIds:
--gen-spk-warrior 4 tabs (General + Arms/Fury/Protection)
including Charge, Mortal Strike,
Bloodthirst, Shield Block
--gen-spk-mage 4 tabs (General + Arcane/Fire/Frost)
including Frostbolt rank 1 (spellId 116)
— the canonical "every mage starts here"
--gen-spk-rogue 4 tabs (General + Assassination/Combat/
Subtlety) with poison + lethality picks
Validator catches: packId+tabName required, classId in 1..11,
tabIndex in 0..3, no duplicate packIds, no duplicate
(classId,tabIndex) pairs (spellbook UI dispatch tie), no zero
spellIds, no duplicate spellIds within any single tab (would
render twice in spellbook). Warns on classId 6 and 10 (vanilla
PlayerClass DBC gaps) and on empty tabs (player would see a
blank spellbook tab).
Format count 125 -> 126. CLI flag count 1328 -> 1335.
Adds --export-wgch-json / --import-wgch-json with the established
readEnumField template factoring int+name dual encoding for both
channelKind ("global"/"realmzone"/"faction"/"custom") and accessKind
("publicjoin"/"inviteonly"/"autojoinonzone"/"moderated").
All 3 presets (std-channels / roleplay / admin) byte-identical
binary roundtrip OK including the zoneDefaultMapId=1519 Stormwind
auto-join binding on TradeStormwind. Validators pass on round-
tripped binaries.
CLI flag count 1317 -> 1319.
Novel replacement for vanilla ChatChannels.dbc + the per-server
zone-default chat-join behavior. Each WGCH entry binds one chat
channel to its access policy: PublicJoin, InviteOnly,
AutoJoinOnZone (with zoneDefaultMapId), or Moderated. Entries
also carry channelKind (Global/RealmZone/Faction/Custom),
passwordRequired, levelMin, maxMembers cap, topic-mod-only flag,
and an icon RGBA color.
Three presets:
--gen-gch 4 standard server channels (LookingForGroup,
World, Trade auto-join Stormwind, General)
--gen-gch-rp 4 RP channels (RP_OOC public, RP_IC moderated
200-cap, RP_Forum invite-only 50-cap, RP_Events
password-protected)
--gen-gch-admin 3 moderator-only channels (GMTraffic, AuditLog,
Backstage — all password-gated)
Validator catches: id+name required, channelKind/accessKind
range, duplicate channelIds, duplicate channel names (which
would route /join ambiguously), AutoJoinOnZone with
zoneDefaultMapId=0 (auto-join trigger would never fire). Warns
on dead zoneDefaultMapId set with non-AutoJoin kind.
Format count 123 -> 124. CLI flag count 1290 -> 1317.
Dual encoding for both WLAN enums via the readEnumField
template: languageCode (int 0..10 OR token "enUS" /
"enGB" / "deDE" / "esES" / "frFR" / "itIT" / "koKR" /
"ptBR" / "ruRU" / "zhCN" / "zhTW") and namespace (int
0..7 OR token "ui" / "quest" / "item" / "spell" /
"creature" / "tooltip" / "gossip" / "system").
UTF-8 round-trip preserved through JSON: nlohmann::json
escapes multibyte sequences as \uXXXX surrogate pairs
on export and decodes them back to bytes on import,
producing byte-identical binary output. The Korean
"취소" and Chinese "取消" UI translations from the
preset round-trip cleanly through the binary -> JSON ->
binary cycle.
All 3 presets (ui-basics / quest-sample / tooltip-set)
byte-identical roundtrip OK including the multibyte
strings. CLI flag count 1288 -> 1290.
Novel replacement for the per-language overlay tables
vanilla WoW carried as Locale_*.MPQ patches plus the
Spell.dbc / Item.dbc trailing 16-locale string columns.
Each entry binds one (originalKey, languageCode,
namespace) triple to its localized translation,
forming a per-language overlay applied AFTER any
per-format catalog has resolved its primary text.
Eleven languageCode values cover the canonical WoW
locales (enUS / enGB / deDE / esES / frFR / itIT /
koKR / ptBR / ruRU / zhCN / zhTW) plus Unknown=255 as
escape hatch. Eight namespace values segment the
lookup space (UI / Quest / Item / Spell / Creature /
Tooltip / Gossip / System) so a UI button "Cancel"
doesn't collide with an item description containing
the word "Cancel".
UTF-8 multibyte support is the novel demonstration —
the originalKey field is typically ASCII (English
canonical key), but localizedText holds Korean (취소),
Simplified Chinese (取消), or other non-Latin scripts.
The string-length-prefixed binary serialization
preserves byte-identical round-trip regardless of
encoding.
Three preset emitters: makeUIBasics (5 UI translations
of the "Cancel" button across deDE/frFR/esES/koKR/zhCN
including the Korean and Chinese multibyte UTF-8 strings),
makeQuestSample (3 entries — one quest title in
deDE/frFR/koKR illustrating the dotted-key convention
"QUEST.123.title"), makeTooltipSet (4 item-tooltip
strings in deDE+frFR — the high-volume client
localization use case).
Validator's most novel check is per-(originalKey,
languageCode, namespace) triple uniqueness — two
entries with all three matching would tie at runtime
when the locale-aware text layer looks up an override.
Plus the warning on empty localizedText (the override
would render blank — possibly worse than fallback to
the catalog default).
Format count 122 -> 123. CLI flag count 1283 -> 1288.
Dual encoding for factionFilter (int 1=Alliance / 2=Horde
OR token "alliance" / "horde"). titlePrefix as plain
JSON string — round-trip preserves the spaces and
hyphens in titles like "Knight-Lieutenant" exactly.
honorRequiredWeekly + honorRequiredAchieve serialize as
plain uint32 — vanilla rank thresholds top out around
1,000,000 lifetime RP, well within uint32 range.
All 3 presets (alliance/horde/high-tier) byte-identical
roundtrip OK. CLI flag count 1281 -> 1283.
Novel replacement for the hardcoded 14-rank vanilla WoW
PvP ladder (Private through Grand Marshal Alliance,
Scout through High Warlord Horde). Each entry binds one
(factionFilter, tier) combination to its display name,
weekly RP threshold to maintain rank, lifetime honor
for first-time achievement, title prefix for player-
name display, and tier-set gear reward.
The vanilla rank-ladder system used a weekly RP-decay
mechanic that punished any week without play with rank-
loss; this catalog stores both the weekly threshold
(maintenance) and the lifetime threshold (achievement)
since both are needed for accurate rank-progression
simulation.
Three preset emitters spanning the rank ladder:
makeAllianceRanks (7 lower-tier ranks Private through
Knight-Lieutenant), makeHordeRanks (7 mirrored Horde
titles Scout through Blood Guard with identical honor
thresholds — factionFilter disambiguates the shared
"Sergeant" title), makeHighRanks (8 high-tier ranks
across both factions Knight-Captain through Lt.
Commander, tiers 8-11 with the iconic legendary
battlegear shoulder unlocks).
Tier 14 (Grand Marshal / High Warlord) intentionally
omitted from presets — it's the legendary top-rank
that historically required dedicated 24/7 grinding.
Catalog supports tiers 1..14 in the schema; consumers
extend as needed.
Validator's most novel checks: per-(faction, tier)
tuple uniqueness — two ranks at the same tier for the
same faction would tie at runtime when the rank-
progression UI looks up "what's tier 5 for Alliance?"
Plus per-faction honor-threshold monotonicity — a
higher tier requiring less honor than a lower tier
would let players "downrank" by gaining honor, which
is a content authoring bug.
Format count 121 -> 122. CLI flag count 1276 -> 1281.
Dual encoding for both WANV enums via the readEnumField
template: eventKind (int 0..6 OR 255 OR token "holiday"
/ "anniversary" / "doublexp" / "doublehonor" /
"petbattle" / "bgbonus" / "seasonalquest" / "misc") and
recurrenceKind (int 0..3 OR token "yearly" / "monthly"
/ "weekly" / "oneoff").
The polymorphic startDay field (1..31 day-of-month for
Yearly/Monthly/OneOff vs 0..6 weekday for Weekly) is
serialized as a plain int — operators editing JSON
need to know the recurrenceKind context to interpret
the value, which the validator already enforces on
import.
All 3 presets (holidays / weekly bonus / anniversary)
byte-identical roundtrip OK. CLI flag count 1274 ->
1276.