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.
Novel replacement for the implicit recurring-event
scheduler vanilla WoW encoded across the GameEvent SQL
table + per-holiday script hooks. Each entry binds one
calendar-driven recurring event (yearly holiday like
Hallow's End, monthly tribute day, weekly Double XP
Weekend, anniversary celebration) to its scheduling
rule and its payload (a spell buff applied to all
online players, a gift item granted on first event-
window login).
Eight eventKind values (Holiday / Anniversary /
DoubleXP / DoubleHonor / PetBattleWeekend /
BattlegroundBonus / SeasonalQuest / Misc) and four
recurrenceKind values (Yearly / Monthly / Weekly /
OneOff). The startDay field is polymorphic per
recurrenceKind: Yearly/Monthly/OneOff use it as
1..31 day-of-month, Weekly uses it as 0..6 weekday
(Sun..Sat) — the validator enforces both ranges per
kind.
Three preset emitters: makeStandardHolidays (5 yearly
holidays with realistic spell+item payload bindings —
Hallow's End spell 24710, Winter Veil 26157, Brewfest
42500, etc.), makeBonusEvents (4 weekly recurring
bonuses — Friday triple-day weekends and Saturday-
Sunday double-day pet-battle bonus), makeAnniversary
(3 game-launch anniversaries — WoW Nov 23 / TBC Jan 16
/ WotLK Nov 13 with overlapping celebration windows).
Validator's most novel checks combine calendar +
recurrence semantics: per-kind schedule validity (Weekly
startDay 0..6 weekday, durationDays <= 7 to prevent
self-overlap; Yearly/Monthly/OneOff startMonth 1..12,
startDay 1..31 with calendar sanity — Feb cap at 29,
Apr/Jun/Sep/Nov cap at 30 for "no Feb 30" / "no Apr 31"
errors).
Format count 120 -> 121. CLI flag count 1269 -> 1274.
Dual encoding for both WCFG enums via the readEnumField
template: configKind (int 0..7 OR 255 OR token "xprate"/
"droprate"/"honorrate"/"restedxp"/"realmtype"/
"worldflag"/"performance"/"security"/"misc") and
valueKind (int 0..3 OR token "float"/"int"/"bool"/
"string"). restartRequired accepts bool or int.
The polymorphic value field is preserved on disk by
emitting ALL THREE value carriers (floatValue,
intValue, strValue) regardless of valueKind — only the
matching one is meaningful at runtime, but the others
must round-trip byte-identically. JSON also includes
the activeValue derived field rendering only the
meaningful one per kind, so operators editing the
sidecar see the right value front-and-center.
intValue uses int64_t for the trade-gold-cap and other
high-magnitude configs that exceed uint32 range.
All 3 presets (rates/perf/security with mixed value
kinds: Float, Int, Bool, String) byte-identical
roundtrip OK. CLI flag count 1267 -> 1269.
Novel replacement for the worldserver.conf / mangosd.conf
flat-text configuration files vanilla server forks
shipped. Each entry binds one configId to its
polymorphic value via the valueKind enum (Float / Int /
Bool / String) — only the matching value field is
authoritative per entry.
The polymorphic value is the novel data shape: each
entry stores ALL three value carriers on disk
(floatValue float / intValue int64 / strValue string),
and valueKind picks which is meaningful at runtime. Bool
folds into intValue with strict 0/1 semantics. JSON
export reflects this: an activeValue derived field
renders the right form per kind so operators editing
JSON see only the relevant value.
Nine configKind values cover the full server-tunable
surface: XPRate / DropRate / HonorRate / RestedXP /
RealmType / WorldFlag / Performance / Security / Misc.
Each kind groups settings the server iterates by kind
at startup (all XPRate entries seed the per-class
experience matrix; all Security entries configure the
anti-cheat thresholds).
Three preset emitters: makeRates (4 vanilla baseline
rate multipliers with valueKind=Float), makePerformance
(4 server tuning configs mixing Int and Float kinds —
max creatures per cell, view distance yards, GC
interval seconds, etc.), makeSecurity (4 anti-cheat
configs FIRST format using valueKind=String for the
cheat-detection sensitivity preset name).
Validator's most novel checks are per-valueKind cross-
field consistency: Bool requires intValue strictly 0/1
(error), and warns on cross-field bleed (Float kind
with non-zero intValue means the int is silently
ignored at runtime but persists on disk). Plus name
uniqueness — server name-based config lookups would
be ambiguous otherwise.
Format count 119 -> 120 (multiple-of-10 milestone).
CLI flag count 1262 -> 1267.
No enum coercion (all-numeric payload). Pct/raw dual
encoding for cloudSpeedX10: stored on disk as raw uint8
(0..255 = 0..25.5 mph) for byte-precision round-trip,
exported with both forms — cloudSpeedX10 (raw int,
authoritative) and cloudSpeedMph (float, derived).
Import accepts either form; pct conversion clamps to
0..255 with rounding.
This is the same dual-encoding pattern WHRD uses for
bonusQualityChance — get byte-identical round-trip from
binary AND human-friendly editing in JSON. cloudOpacity
stays as raw 0..255 since percent conversion would lose
precision (cloudOpacity=78 doesn't round-trip cleanly
through %.2f display).
All 3 presets (stormwind/arctic/hellfire) byte-
identical roundtrip OK. CLI flag count 1260 -> 1262.
Novel replacement for the LightParams.dbc + Light.dbc
pair vanilla WoW used to drive the per-zone diurnal sky
cycle. Each entry binds one (mapId, areaId,
timeOfDayHour) triplet to its sky-rendering parameters:
sky-dome zenith and horizon colors, sun angle and color,
fog start/end distances, cloud-layer opacity, and cloud
drift speed in tenths-mph.
The renderer interpolates between adjacent keyframes
when the in-game clock crosses an hour boundary, so a
4-keyframe set (Dawn/Noon/Dusk/Midnight) produces the
full diurnal cycle through linear interpolation. Servers
can author finer-grained keyframes (e.g. every 3 hours)
for smoother transitions.
Three preset emitters demonstrating the catalog's range:
makeStormwindDay (4 standard temperate keyframes from
lavender dawn through bright noon to deep blue-black
midnight), makeNorthrendArctic (4 cold steel-blue
keyframes with high-density ice fog peaking at the
midnight blizzard whiteout — minimum 30yd visibility),
makeOutlandHellfire (3 keyframes — no midnight, since
Outland's permanent gravitational anomaly from the
Twisting Nether keeps the sky lit; iconic crimson +
orange palette throughout).
Validator's most novel checks: per-(mapId, areaId,
timeOfDayHour) triple uniqueness — two keyframes at the
same hour for the same area would render in unstable
order during diurnal interpolation. Plus
fogStartYards >= fogEndYards (inverted falloff) error,
sunAngleDeg outside [0,360] warning (renderer wraps
modulo but suggests authoring confusion).
Format count 118 -> 119. CLI flag count 1255 -> 1260.
Dual encoding for both modeKind fields (the primary
modeKind AND the timeoutFallbackKind disconnect-fallback)
via the readEnumField template — both accept int 0..5
OR token "freeforall"/"roundrobin"/"masterloot"/
"needbeforegreed"/"personal"/"disenchant".
masterLooterRequired accepts bool or int.
thresholdQuality serializes as both int (authoritative,
0..7) AND derived qualityName string ("Poor"/"Common"/
"Uncommon"/"Rare"/"Epic"/"Legendary"/"Artifact"/
"Heirloom") for human-readable JSON. The qualityName is
informational only — int form is authoritative on
import.
All 3 presets (standard/raid/afk) byte-identical
roundtrip OK. CLI flag count 1253 -> 1255.
Novel replacement for the implicit loot-distribution
rules vanilla WoW encoded across the GroupLoot system
(CMSG_LOOT_METHOD), the per-quality thresholds for
Need-roll triggering, and the master-looter permission
gates. Each entry binds one group-loot policy mode to
its kind (FFA / RoundRobin / MasterLoot / Need-Before-
Greed / Personal / Disenchant) plus quality threshold,
master-looter requirement, idle-skip seconds, and
disconnect-fallback policy.
Six modeKind values cover the full loot-distribution
surface. The thresholdQuality field uses the WIQR
quality tier convention (0=Poor through 7=Heirloom)
to gate Need-roll triggering — anything below threshold
auto-distributes via FFA-equivalent semantics.
The disconnect-fallback (timeoutFallbackKind) field is
unique to MasterLoot policies — if the master looter
disconnects mid-distribution, the policy auto-promotes
to the fallback mode for democratic recovery. Common
fallbacks: Need-Before-Greed (full roll system),
FreeForAll (fastest unblock).
Three preset emitters: makeStandard (4 5-man / casual
modes covering FFA farming, RoundRobin trash, NBG
Uncommon, MasterLoot Rare), makeRaidPolicies (3 raid
loot policies including MasterLoot Epic with NBG
fallback, Personal Loot, NBG Rare), makeAFKPrevention
(3 AFK-mitigating modes with idleSkipSec gates).
Validator's most novel check is per-kind consistency:
MasterLoot kind REQUIRES masterLooterRequired=1 (else
the policy contradicts itself — "Master Loot mode
without requiring a master looter"). Personal kind
warns if masterLooterRequired=1 (no-op flag). Tightened
fallback-to-self warning to fire ONLY for MasterLoot
where the field is meaningful — original version fired
falsely for FFA/Personal/RoundRobin where the leader-
disconnect scenario doesn't apply (caught + tightened
during smoke-test).
Format count 117 -> 118. CLI flag count 1248 -> 1253.
Dual encoding for markerKind (int 0..3 OR token
"raidtarget"/"worldmap"/"party"/"custom"). iconPath and
displayChar serialize as plain JSON strings for direct
authoring.
The displayChar field round-trip preserves exact byte
content — important since chat-overlay glyphs may
include short Unicode sequences (the Diamond marker
uses "<>" as a 2-char ASCII fallback for its glyph
rendering).
All 3 presets (raid/world/party) byte-identical
roundtrip OK. CLI flag count 1246 -> 1248.
Novel replacement for the hardcoded 8-marker raid set
vanilla WoW shipped (Star/Circle/Diamond/Triangle/Moon/
Square/Cross/Skull) plus world-map pin markers and
5-man party role markers. Each entry binds one marker
slot to its icon resource, single-character chat-overlay
glyph (for "{star}" chat-style links), and priority for
sort order in the marker-picker UI.
Four markerKind values (RaidTarget / WorldMap / Party /
Custom) cover the full marker-system surface. The chat-
overlay displayChar field enables the WoW-canonical
chat shortcuts: "{star}" gets rendered as a star icon
inline in chat, with "*" as the fallback glyph for
non-rich-text contexts (clipboard, log files, mod-
script handlers).
Three preset emitters: makeRaidTargets (8 canonical
raid markers in /raidicon priority order 0..7 with
their iconic colors and glyph mnemonics), makeWorldMap-
Pins (5 world-map pin markers — Pin/Flag/Crosshair/
Question/Compass), makeParty (4 role markers for group-
finder filtering — Tank/Healer/DPS/Caster).
Validator's most novel checks: per-(markerKind,
priority) tuple uniqueness — two markers at same kind+
priority would render in unstable picker UI order. Plus
RaidTarget priority > 7 warns (exceeds canonical 8-slot
/raidicon dispatch range; client keybind macros may not
reach the slot).
Format count 116 -> 117. CLI flag count 1241 -> 1246.