Commit graph

1295 commits

Author SHA1 Message Date
Kelsi
b983ef6d48 feat(pipeline): add WPCN (Wowee Player Condition) catalog
49th open format — replaces PlayerCondition.dbc plus the
AzerothCore-style condition resolver. Defines reusable
boolean checks that other catalogs reference by conditionId
to gate gossip options, vendor items, quest availability,
achievement criteria, spell trainer offerings.

16 condition kinds (Always, Race, Class, Level, Zone, Map,
Reputation, AchievementWon, QuestComplete, QuestActive,
SpellKnown, ItemEquipped, Faction, InCombat, Mounted,
Resting), 8 comparison ops (==, !=, >, >=, <, <=, in-set,
not-in-set), and 4 chain ops (none, and, or, not) — chain
multiple conditions via chainNextId to express arbitrary
boolean trees.

Cross-references with prior formats — targetIdA is
polymorphic by conditionKind: resolves to WCHC raceId/classId,
WMS areaId/mapId, WFAC factionId, WACH achievementId, WQT
questId, WSPL spellId, or WIT itemId. chainNextId resolves
within the same WPCN catalog.

CLI: --gen-pcn (3 single-check starters), --gen-pcn-quest-gates
(4 cross-format quest gates with real WQT/WFAC/WACH/WMS IDs),
--gen-pcn-composite (3 leaves + 3 chained roots showing AND/
OR/NOT). Validator catches id=0/duplicates, kind/op out of
range, chain self-loop (infinite recursion), chainOp set
without chainNextId (dangling chain), chainNextId set without
chainOp (dead pointer warning), and unresolved chainNextId
references.
2026-05-09 19:36:56 -07:00
Kelsi
d332229a79 feat(pipeline): add WWUI (Wowee World-State UI) catalog
48th open format — replaces WorldStateUI.dbc plus the
AzerothCore-style world_state SQL data. Defines on-screen UI
elements that surface server-side world-state variables: BG
scoreboards (flag captures, base controls), Wintergrasp tank
counters, Eye of the Storm flag-carrier indicator, dungeon
boss progress, world-event collection trackers.

Each entry binds a server-side variableIndex to a UI panel
kind (counter / timer / flag-icon / progress-bar / two-sided
score / custom) gated by mapId+areaId, with optional
alwaysVisible and hideWhenZero flags and a chosen panel
position (top / bottom / top-left / top-right / center).

Cross-references with prior formats — mapId points at
WMS.mapId and areaId points at WMS.areaId.

CLI: --gen-wsui (3-entry BG scoreboard starter for WSG/AB/
EotS), --gen-wsui-wintergrasp (4-entry full Wintergrasp UI),
--gen-wsui-dungeon (3-entry boss/keys/treasure hunt UI),
--info-wwui, --validate-wwui with --json variants. Validator
catches id=0/duplicates, kind/position out of range,
variableIndex=0 warning, alwaysVisible+hideWhenZero conflict
warning, and (mapId, variableIndex) collision warning when
two entries would read the same server slot on the same map.

Also extends --list-formats and --info-magic with WWUI.
2026-05-09 19:32:15 -07:00
Kelsi
beca69352a feat(pipeline): add WSVK (Wowee Spell Visual Kit) catalog
47th open format — replaces SpellVisualKit.dbc +
SpellVisualEffectName.dbc plus the AzerothCore-style spell
visual SQL data. Defines per-spell visual presentations:
cast-bar effect model, projectile model + travel speed +
arc gravity, impact effect model, hand effect on the caster,
and the animations + sounds that fire at cast / channel /
impact time.

Cross-references with prior formats — castAnimId / impactAnimId
/ precastAnimId point at WANI.animationId, castSoundId /
impactSoundId point at WSND.soundId. Spell catalogs (WSPL)
will reference visualKitId here to bind "what mechanically
happens" to "what plays visually."

CLI: --gen-svk (3-kit Frostbolt/Fireball/HealingTouch starter
showing projectile + AoE + heal patterns), --gen-svk-combat
(5 melee/ranged with WANI animation refs), --gen-svk-utility
(4 portal/hearth/mount/resurrect with no projectile),
--info-wsvk, --validate-wsvk with --json variants. Validator
catches id=0/duplicates, missing name, negative speeds/radii,
projectile-model + speed coherence (model without speed =
never travels; speed without model = invisible), and a
no-effect catch-all (no models + no anims + no sounds).
2026-05-09 19:23:36 -07:00
Kelsi
dadf72307c feat(pipeline): add WANI (Wowee Animation) catalog
46th open format — replaces AnimationData.dbc plus the
hard-coded animation-id tables in M2 model loaders. Defines
named animations (Stand, Walk, Run, Cast, Death, MountIdle,
Fly, ...) with fallback chains, behavior tier (default /
mounted / sitting / aerial / swimming), and weapon-flag
bitmasks that select the correct animation variant when the
model wields 1H / 2H / dual / bow / rifle / wand / etc.

Cross-references with prior formats — fallbackId resolves
within the same WANI catalog (graceful degradation when the
requested animation is absent: Run falls back to Walk,
Attack2H falls back to Attack1H).

10 weapon-flag constants + 6 behavior flags (Looped,
BlendableCycle, Interruptable, MovementSync, OneShot,
PreserveAtEnd). 5 behavior tiers. CLI: --gen-animations
(5 essentials), --gen-animations-combat (8 weapon-typed
attacks + parry + channel), --gen-animations-movement (6
locomotion anims with tier transitions). Validator catches
id-uniqueness, fallback-self-loop, looped-without-duration,
mutually-exclusive Looped+OneShot flags, weaponFlags=0
warning, and unresolved fallback warnings.

Also extends --list-formats and --info-magic with WANI.
2026-05-09 19:19:13 -07:00
Kelsi
fb07b276f2 feat(pipeline): add WLIQ (Wowee Liquid Type) catalog
45th open format — replaces LiquidType.dbc plus the
AzerothCore-style terrain liquid descriptor data. Defines
liquid materials used by terrain MCNK liquid layers, WMO
interior pools, and procedurally generated fluid bodies in
custom zones. Each liquid pairs a render material (shader +
texture array + flow vectors + fog color) with gameplay data
(damage spell, raw DPS, ambient + splash audio cross-refs).

Cross-references with prior formats — ambientSoundId and
splashSoundId point at WSND.soundId, damageSpellId points at
WSPL.spellId.

10 liquid kinds (Water / Magma / Slime / OceanSalt / FelFire /
HolyLight / TarOil / AcidBog / FrozenWater / UnderworldGoo)
covering both natural and magical fluids. Per-liquid: viscosity
(0=water, 1=slime), flow direction+speed (radians+units/sec),
fog density+RGB, packed minimap RGBA color.

CLI: --gen-liquids (3-entry classic Water/Lava/Slime starter),
--gen-liquids-magical (4 magical pools incl. fel/holy/cosmic),
--gen-liquids-hazardous (3 high-damage liquids with WSPL DoT
cross-refs), --info-wliq, --validate-wliq with --json variants.
Validator catches id=0/duplicates, missing shader/material,
kind out of range, fog/viscosity outside 0..1, hazardous-kind-
without-damage warning, and Water/OceanSalt-with-damage warning
to flag accidental misconfiguration.
2026-05-09 19:10:03 -07:00
Kelsi
2666b976c9 feat(pipeline): add WHOL (Wowee Holiday) catalog
44th open format — replaces Holidays.dbc + HolidayDescriptions.dbc
+ HolidayNames.dbc plus the AzerothCore-style game_event SQL
tables. Defines time-gated world events: seasonal holidays
(Hallow's End / Brewfest / Winter Veil), weekly call-to-arms BG
bonuses, world-PvP windows (Wintergrasp), one-shot specials, and
recurring daily / weekly resets. Each holiday has a recurrence
rule (Annual / Monthly / WeeklyRecur / OneTime), a calendar
window (startMonth + startDay + durationHours), and optional
cross-refs to a feature creature, an intro quest, and a token /
item reward issued during the window.

Cross-references with prior formats — holidayQuestId points at
WQT.questId, bossCreatureId points at WCRT.creatureId,
itemRewardId points at WIT.itemId, and areaIdGate / mapIdGate
point at WMS.areaId / WMS.mapId.

CLI: --gen-holidays (3 seasonal starter), --gen-holidays-weekly
(3 weekly BG call-to-arms windows), --gen-holidays-special (3
world-PvP / lunar / children events), --info-whol, --validate-whol
with --json variants. Validator catches id=0/duplicates, kind /
recurrence out of range, durationHours=0, invalid month/day for
non-weekly recurrence, and calendar-only events (no quest +
boss + reward) as a warning.
2026-05-09 19:05:33 -07:00
Kelsi
dcbc9706f2 feat(pipeline): add WVHC (Wowee Vehicle) catalog
43rd open format — replaces Vehicle.dbc + VehicleSeat.dbc plus
the AzerothCore-style vehicle_template SQL tables. Defines
drivable vehicles: tanks, demolishers, motorcycles, gryphons,
choppers, siege weapons, multi-passenger transports. Each entry
pairs a creature template (the rendered model) with a fixed
seat layout — driver / passenger / gunner seats with their own
attachment points, control flags (kSeatDriver / kSeatGunner /
kSeatPassenger / kSeatHidesPlayer / kSeatNoEjectByCC), and
per-seat abilities mounted to the action bar.

Cross-references with prior formats — creatureId points at
WCRT.creatureId (the rendered model), flightCapabilityId points
at WMNT.mountId for shared fly-speed tables, and per-seat
controlSpellId / exitSpellId point at WSPL.spellId.

CLI: --gen-vehicles (3-vehicle starter chopper/wind-rider/tank),
--gen-vehicles-siege (3 siege weapons with control spellIds),
--gen-vehicles-flying (3 flying mounts cross-ref WMNT
flightCapabilityIds), --info-wvhc, --validate-wvhc with --json
variants. Validator catches id=0/duplicates, missing creature,
enum out-of-range, empty seat list, flying-vehicle on wrong
movement (would fall through world), driver-flag exclusivity
(0 or >1 drivers), and duplicate seatIndex within a vehicle.
2026-05-09 18:59:49 -07:00
Kelsi
92327fc0c0 feat(pipeline): add WGLY (Wowee Glyph) catalog
42nd open format — replaces GlyphProperties.dbc + GlyphSlot.dbc
plus the AzerothCore-style glyph_properties SQL table. Defines
the WotLK glyph system: per-class inscribable glyphs that
modify spell behavior. Each entry pairs a glyph item (the
inscriber's craft output) with the spell aura that applies the
modification, tagged with a Major / Minor / Prime slot type and
a classMask of allowed classes.

Cross-references with prior formats — spellId points at
WSPL.spellId (the aura), itemId points at WIT.itemId (the
inscribed item), and classMask bit positions match the WCHC
classId enum (1=Warrior, 2=Paladin, 3=Hunter, etc).

CLI: --gen-glyphs (3-entry per-role starter), --gen-glyphs-warrior
(6-entry full warrior allotment), --gen-glyphs-universal (4
classMask=All utility glyphs), --info-wgly, --validate-wgly with
--json variants. Validator catches id=0/duplicates, empty name,
spellId=0 (glyph applies no aura), classMask=0 (no class can
inscribe), itemId=0 warning, requiredLevel<25 warning (below
WotLK glyph threshold), and glyphType out-of-range.
2026-05-09 18:55:22 -07:00
Kelsi
0be9bf86db feat(pipeline): add WCMS (Wowee Cinematic) catalog
41st open format — replaces Movie.dbc / CinematicCamera.dbc /
CinematicSequences.dbc plus the AzerothCore cinematic_camera SQL
table. Defines pre-rendered videos, in-engine camera flythroughs,
text crawls, and still images, each with a media path, duration,
skippable flag, and a polymorphic trigger that fires on quest
events / class first-login / zone entry / dungeon clear /
achievements / level milestones.

Cross-references with prior formats — triggerTargetId resolves
by triggerKind to WQT.questId / WMS.areaId / WMS.mapId /
WCHC.classId / WACH.achievementId, and soundtrackId points at
WSND.soundId.

CLI: --gen-cinematics (3-entry starter), --gen-cinematics-intros
(4 class intros), --gen-cinematics-quests (3 quest-bound
cinematics referencing demo questIds 1/100/102), --info-wcms,
--validate-wcms with --json variants. Validator catches
id=0/duplicates, empty name/mediaPath, kind/trigger out of
range, missing target id for non-Manual/Login/LevelUp triggers,
zero-duration entries, and non-skippable pre-rendered videos.
2026-05-09 18:50:43 -07:00
Kelsi
af214d8b34 feat(pipeline): add WCHN (Wowee Chat Channel) catalog
Novel open replacement for Blizzard's ChatChannels.dbc +
the AzerothCore-style chat_channel SQL tables. The 40th
open format added to the editor.

Defines the world chat channel system: General, Trade,
LookingForGroup, GuildRecruitment, LocalDefense, plus
per-zone area channels and custom user-created channels.
Each channel has access rules (faction / level), join
behavior (auto vs opt-in), broadcast policy (announce /
moderated), and optional area / map gating that auto-joins
or auto-leaves the channel as the player moves.

Cross-references with previously-added formats:
  WCHN.entry.areaIdGate -> WMS.area.areaId
                           (channel auto-attaches in this area)
  WCHN.entry.mapIdGate  -> WMS.map.mapId
                           (channel auto-attaches on this map)

Format:
  • magic "WCHN", version 1, little-endian
  • per channel: channelId / name / description /
    channelType / factionAccess / autoJoin / announce /
    moderated / minLevel / areaIdGate / mapIdGate

Enums:
  • ChannelType (10): AreaLocal / Zone / Continent / World /
                      Trade / LookingForGroup / GuildRecruit /
                      LocalDefense / Custom / Pvp
  • FactionAccess (3): Alliance / Horde / Both

API: WoweeChannelLoader::save / load / exists / findById.

Three preset emitters:
  • makeStarter   — 4 stock channels (General Zone +
                     Trade + LFG + GuildRecruit) with
                     default autoJoin policies
  • makeCity      — 5 city-specific channels (3 Stormwind +
                     2 Orgrimmar) with mapId / areaId gates
                     so they auto-attach on entry
  • makeModerated — 3 moderated / restricted channels
                     (LocalDefense level 10+, WorldDefense
                     moderated, RaidCoordination level 60+)

CLI added (5 flags, 677 documented total now):
  --gen-channels / --gen-channels-city / --gen-channels-moderated
  --info-wchn / --validate-wchn

Validator catches: channelId=0 + duplicates, empty name,
unknown channelType / factionAccess, world / continent
channel with area or map gate (gate is silently ignored at
runtime — usually a typo), minLevel=0 (no level gate at all).
2026-05-09 18:43:26 -07:00
Kelsi
ab72380ee0 feat(pipeline): add WAUC (Wowee Auction House) catalog
Novel open replacement for Blizzard's AuctionHouse.dbc +
the AzerothCore-style auctionhouse / auctionhousebot SQL
tables. The 39th open format added to the editor.

Defines per-house rules for the auction system: faction
access, deposit rate (basis points of buyout price), house
cut on successful sale, three listing duration tiers with
per-tier deposit multipliers, disallowed item-class bitmask,
and the auctioneer NPC.

Cross-references with previously-added formats:
  WAUC.entry.auctioneerNpcId   -> WCRT.entry.creatureId
                                   (Auctioneer-flagged NPC)
  WAUC.entry.disallowedClassMask
                                  bitmask of WIT.Class values
                                  that may not be auctioned at
                                  this house

The faction-pair preset captures the canonical asymmetry:
faction houses charge 5% on a successful sale while neutral
houses charge 15% — the cross-faction tax that makes
neutral AHs profitable for goblins.

Format:
  • magic "WAUC", version 1, little-endian
  • per house: houseId / auctioneerNpcId / name /
    factionAccess / baseDepositRateBp / houseCutRateBp /
    maxBidCopper / 3 duration hours + 3 deposit multipliers /
    disallowedClassMask

Enums:
  • FactionAccess (4): Alliance / Horde / Neutral / Both
  • Rates use basis points: 10000 = 100%

API: WoweeAuctionLoader::save / load / exists / findById.

Three preset emitters showcase typical auction setups:
  • makeStarter        — 1 neutral house with default 12h /
                          24h / 48h tiers
  • makeFactionPair    — 3 houses (Stormwind / Orgrimmar /
                          Booty Bay) with auctioneer NPC IDs
                          + canonical faction-vs-neutral cut
                          rates
  • makeRestricted     — 1 house disallowing Containers (1) +
                          Quest items (12) + Keys (13) with
                          tighter durations + 1000g bid cap

CLI added (5 flags, 670 documented total now):
  --gen-auction / --gen-auction-pair / --gen-auction-restricted
  --info-wauc / --validate-wauc

Validator catches: houseId=0 + duplicates, empty name,
factionAccess out of range, duration tier=0, durations not
in short<=medium<=long order, houseCutRateBp >= 100% (seller
loses money on a sale), warns on >50% cut.
2026-05-09 18:34:54 -07:00
Kelsi
62e793800c feat(pipeline): add WPET (Wowee Pet System) catalog
Novel open replacement for AzerothCore-style pet_template +
pet_levelstats SQL + the pet-related subsets of
CreatureFamily.dbc + SpellFamilyName.dbc. The 38th open
format added to the editor.

Defines two related kinds of player-controlled NPCs in
one catalog:
  • Pet families  — hunter pet families (Wolf / Cat / Bear /
                     Boar / Raptor / Spider / etc.) with
                     per-family ability sets, base stat
                     multipliers, and diet preferences
  • Warlock minions — Imp / Voidwalker / Succubus /
                       Felhunter / Felguard, each with
                       their own summon spell, creature
                       template, and ability list

Cross-references with previously-added formats:
  WPET.family.familyId          -> WCRT.entry.familyId
                                    (matches creature family)
  WPET.family.abilities.spellId -> WSPL.entry.spellId
  WPET.minion.summonSpellId     -> WSPL.entry.spellId
  WPET.minion.creatureId        -> WCRT.entry.creatureId
                                    (used for stat scaling)
  WPET.minion.abilities.spellId -> WSPL.entry.spellId

The starter preset's familyIds (1=Wolf, 2=Cat) match
WCRT::FamilyId enum values, so a hunter taming a wolf via
WCRT links straight through to WPET ability sets.

Format:
  • magic "WPET", version 1, little-endian
  • families[]: familyId / name / description / icon /
    petType / baseAttackSpeed / damageMultiplier /
    armorMultiplier / dietMask / abilities[]
  • minions[]: minionId / name / summonSpellId / creatureId /
    abilities[] (each: spellId / rank / autocastDefault)

Enums:
  • PetType (3): Cunning / Ferocity / Tenacity
                  (WotLK+ talent tree categorization)
  • DietFlags:   Meat / Fish / Bread / Cheese / Fruit / Fungus

API: WoweePetLoader::save / load / exists +
WoweePet::findFamily / findMinion + dietMaskName helper
that decodes a dietMask into a "meat+fish" string.

Three preset emitters showcase typical pet catalogs:
  • makeStarter — 2 hunter families (Wolf + Cat) with full
                   3-ability sets + 1 warlock Imp
  • makeHunter  — 8 classic hunter families covering all
                   3 petType categories with appropriate
                   diet masks
  • makeWarlock — 5 warlock minions each with summon spell
                   ID and creatureId pointing into WCRT

CLI added (5 flags, 663 documented total now):
  --gen-pets / --gen-pets-hunter / --gen-pets-warlock
  --info-wpet / --validate-wpet

Validator catches: ids=0 + duplicates, empty name, petType
out of range, baseAttackSpeed<=0 (would divide by zero in
DPS calc), dietMask=0 (pet cannot be fed for happiness),
minion missing summonSpellId / creatureId.
2026-05-09 18:27:02 -07:00
Kelsi
f290a0d4a9 feat(pipeline): add WPCD (Wowee Player Condition) format
Novel open replacement for Blizzard's PlayerCondition.dbc +
the AzerothCore-style condition_template SQL tables. The
37th open format added to the editor.

Defines reusable boolean conditions that other formats can
reference for gating: "player has quest X completed",
"player level >= N", "player class is mage", "player has
item Y in inventory", "WSEA event Z is active".

Conditions can be grouped and combined with AND/OR
aggregators on a per-group basis: a quest-giver gossip
option that says "show only to level 60 alliance mages
who completed quest 1234" composes 4 conditions sharing
the same groupId with AND aggregation. The runtime walks
each group, applies the group's aggregator, and returns
the boolean result to the caller.

Cross-references with previously-added formats — the
targetId field has a polymorphic interpretation by kind:
  WPCD.targetId (kind=QuestCompleted/Active) -> WQT.questId
  WPCD.targetId (kind=HasItem)               -> WIT.itemId
  WPCD.targetId (kind=HasSpell)              -> WSPL.spellId
  WPCD.targetId (kind=HasAchievement)        -> WACH.achievementId
  WPCD.targetId (kind=AreaId)                -> WMS.areaId
  WPCD.targetId (kind=EventActive)           -> WSEA.eventId
  WPCD.targetId (kind=HasTitle)              -> WTIT.titleId
  WPCD.targetId (kind=FactionRep)            -> WFAC.factionId
  WPCD.targetId (kind=Class/Race)            -> WCHC class/race id

Future format extensions can reference WPCD.conditionId in
their own gating fields — WTRG triggers gated by player
state, WGSP options visible only when conditions are met,
WMOU summon spells condition-gated by quest progress, etc.

Format:
  • magic "WPCD", version 1, little-endian
  • per condition: conditionId / groupId (0 = standalone) /
    name / description / kind / aggregator / negated /
    targetId / minValue / maxValue

Enums:
  • Kind (17): AlwaysTrue / AlwaysFalse / QuestCompleted /
                QuestActive / HasItem / HasSpell / MinLevel /
                MaxLevel / ClassMatch / RaceMatch /
                FactionRep / HasAchievement / TeamSize /
                GuildLevel / EventActive / AreaId / HasTitle
  • Aggregator (2): And / Or

API: WoweeConditionLoader::save / load / exists / findById.

Three preset emitters showcase typical usage:
  • makeStarter — 4 standalone conditions covering the most
                   common kinds (quest-done / has-item /
                   min-level / class)
  • makeGated   — 5 conditions in 2 groups demonstrating
                   AND-aggregation (alliance + mage + lvl 60)
                   and OR-aggregation (did quest 1 OR quest 100)
  • makeEvent   — 3 event-gated conditions cross-referencing
                   WSEA event IDs (Hallow's End / Brewfest /
                   Winter's Veil)

CLI added (5 flags, 656 documented total now):
  --gen-conditions / --gen-conditions-gated / --gen-conditions-event
  --info-wpcd / --validate-wpcd

Validator catches: conditionId=0 + duplicates, kind /
aggregator out of range, kinds requiring targetId having
target=0 (skips AlwaysTrue/False, MinLevel/MaxLevel,
TeamSize, GuildLevel which use min/max instead), TeamSize
with min > max.
2026-05-09 18:18:27 -07:00
Kelsi
80ebf1dba5 feat(pipeline): add WGLD (Wowee Guild) catalog format
Novel open replacement for AzerothCore-style guild +
guild_member + guild_rank + guild_bank_tab + guild_perk
SQL tables. The 36th open format added to the editor.

Each guild entry holds the complete social-organization
state: header (name, leader, faction, MOTD, info,
creation date, level + experience, bank money, packed
emblem), rank ladder with permissions bitmask + daily
withdraw caps, member roster with rank + join date +
public/officer notes, bank tabs with per-tab and per-rank
deposit / withdraw / view permission masks, and purchased
guild perks referencing WSPL spell IDs.

Cross-references with previously-added formats:
  WGLD.entry.factionId       ~  WCHC.race.factionId
                                 (guilds are faction-locked)
  WGLD.entry.perks.spellId  -> WSPL.entry.spellId

Format:
  • magic "WGLD", version 1, little-endian
  • per guild: header (12 scalar fields + 4 strings) +
    ranks[] + members[] + bankTabs[] + perks[]
  • per rank: rankIndex / name / permissionsMask /
    moneyPerDayCopper
  • per member: characterName / rankIndex / joinedDate /
    publicNote / officerNote
  • per bankTab: tabIndex / name / iconPath /
    deposit+withdraw+view permission masks
  • per perk: perkId / name / spellId / requiredGuildLevel

Enums:
  • Faction (2): Alliance / Horde
  • RankPermissionFlags (14): GuildChat / OfficerChat /
                               Invite / Remove / Promote /
                               Demote / SetMotd /
                               EditPublicNote /
                               EditOfficerNote / ViewBank /
                               Deposit / Withdraw / Disband /
                               RepairFromBank

API: WoweeGuildLoader::save / load / exists / findById +
shared addDefaultRanks helper used by both starter and
faction-pair presets.

Three preset emitters:
  • makeStarter     — 1 small guild, default 5-rank ladder
                       (GM/Officer/Veteran/Member/Initiate),
                       3 members borrowing names from WCRT
                       merchants for cross-format consistency
  • makeFull        — 1 fleshed-out guild: 6 ranks (with
                       Recruit added) + 8 members + 4 bank
                       tabs (officer-only withdraw on tabs
                       3+4) + 3 perks referencing WSPL
                       spell IDs (Heroic Strike / Battle
                       Shout / Thunder Clap as placeholder
                       perk procs)
  • makeFactionPair — 2 parallel guilds, one Alliance + one
                       Horde, with identical rank structures

CLI added (5 flags, 649 documented total now):
  --gen-guilds / --gen-guilds-full / --gen-guilds-pair
  --info-wgld / --validate-wgld

Validator catches: guildId=0 + duplicates, empty name /
leaderName, factionId out of range, no ranks (members can't
exist without a rank ladder), member.rankIndex exceeding
the highest defined rank (intra-format cross-reference
resolution), duplicate bank tabIndices, perks with
spellId=0 (perk does nothing).
2026-05-09 18:10:45 -07:00
Kelsi
a50d83f6c9 feat(pipeline): add WGEM (Wowee Gem / Enchantment) format
Novel open replacement for Blizzard's ItemEnchantment.dbc +
GemProperties.dbc + SpellItemEnchantment.dbc. The 35th
open format added to the editor.

Defines two related kinds of item enhancement in one
catalog:
  • Gems         — socketable jewelry pieces with color
                    (red / blue / yellow / meta) that fit
                    into gear sockets, granting stats or
                    triggering passive spells when socketed
  • Enchantments — persistent buffs applied to weapon /
                    armor pieces, either by an enchanter
                    spell or by an item proc (Mongoose,
                    Crusader, Berserking)

Cross-references with previously-added formats:
  WGEM.gem.itemIdToInsert  -> WIT.entry.itemId
  WGEM.gem.spellId         -> WSPL.entry.spellId
  WGEM.enchantment.spellId -> WSPL.entry.spellId

Format:
  • magic "WGEM", version 1, little-endian
  • gems[]: gemId / itemIdToInsert / name / color /
    statType + statValue / requiredItemQuality / spellId
  • enchantments[]: enchantId / name / description /
    iconPath / enchantSlot / statType + statValue /
    spellId / durationSeconds / chargeCount

Enums:
  • Color (8):       Meta / Red / Yellow / Blue / Purple /
                      Green / Orange / Prismatic
  • EnchantSlot (5): Permanent / Temporary / SocketColor /
                      Ring / Cloak

API: WoweeGemLoader::save / load / exists +
WoweeGem::findGem / findEnchant.

Three preset emitters showcase common shapes:
  • makeStarter  — 3 gems (one per primary color) +
                    2 enchantments (proc + stat)
  • makeGemSet   — 6-gem full color palette covering
                    primary + secondary combinations
  • makeEnchants — 5 enchant variants spanning slots
                    (Mongoose / Deadly Poison / stats ring /
                    cloak / Berserking)

CLI added (5 flags, 642 documented total now):
  --gen-gems / --gen-gems-set / --gen-gems-enchants
  --info-wgem / --validate-wgem

Validator catches: ids=0 + duplicates, empty name, color /
slot out of range, stat-only entries with statValue=0 (gem
provides nothing), chargeCount > 0 on non-Temporary
enchant slots (charges silently ignored at runtime).

The validator caught a real preset issue on first run —
the proc enchants (Mongoose / Deadly Poison / Berserking)
had spellId=0 and statValue=0, providing nothing. Fixed by
adding placeholder spellIds in the 28000-29000 range, with
a comment noting they resolve to real WSPL proc spells
when the spell catalog is extended.
2026-05-09 18:01:48 -07:00
Kelsi
492d626c3b feat(pipeline): add WMAL (Wowee Mail Template) format
Novel open replacement for AzerothCore-style
mail_loot_template SQL + the in-game mail subset of the
inventory + currency systems. The 34th open format added
to the editor.

Defines templated mail messages with currency + item
attachments. Triggered by quest reward delivery (overflow
when bag is full), auction house bid wins / sales,
achievement reward attachments, GM correspondence, holiday
event mailings (Brewfest samples, Hallow's End candy), and
returned-mail-on-rejection.

Cross-references with previously-added formats:
  WMAL.entry.senderNpcId        -> WCRT.entry.creatureId
  WMAL.entry.attachments.itemId -> WIT.entry.itemId

Format:
  • magic "WMAL", version 1, little-endian
  • per template: templateId / senderNpcId / subject / body /
    senderName / moneyCopperAttached / categoryId / cod /
    returnable / expiryDays / attachments[] (each: itemId +
    quantity)

Enums:
  • Category (8): QuestReward / Auction / GmCorrespondence /
                   AchievementReward / EventMailing / Raffle /
                   ScriptDelivery / ReturnedMail

API: WoweeMailLoader::save / load / exists / findById.

Three preset emitters showcase typical mail templates:
  • makeStarter — 3 templates (quest overflow / auction won /
                   GM gift) covering the 3 most common
                   categories
  • makeHoliday — 4 holiday samples that cross-reference the
                   WTKN seasonal token IDs (200=Tricky Treats,
                   201=Brewfest, 202=Coin of Ancestry,
                   203=Stranger's Gift) so the demo content
                   stack ships a full holiday onboarding
                   experience
  • makeAuction — 5-template auction-house family (outbid /
                   won / sold / expired / cancelled) — runtime
                   fills in actual bid amounts / sold items
                   at send time

CLI added (5 flags, 635 documented total now):
  --gen-mail / --gen-mail-holiday / --gen-mail-auction
  --info-wmal / --validate-wmal

Validator catches: templateId=0 + duplicates, empty subject,
neither senderNpcId nor senderName set (no displayable
sender), unknown category, expiryDays=0 (mail expires
immediately), cod=1 with no money attached (free COD),
empty mail in categories where the runtime doesn't fill in
content (skips Auction / GmCorrespondence / ReturnedMail
where empty templates are intentional).

Two bugs caught + fixed during smoke-test on the auction
preset:
  • print formatting glued the `0` from senderNpcId after
    the senderName when no NPC was set (rendered as
    "Postmaster0" instead of "Postmaster") — fixed with an
    explicit if/else split
  • validator's "no money + no items" warning was too
    aggressive for the Auction category, where templates
    are intentionally informational and the runtime fills
    in the real values — added Auction + ReturnedMail to
    the skip list
2026-05-09 17:41:03 -07:00
Kelsi
1b385fb39c feat(pipeline): add WBGD (Wowee Battleground Definition) format
Novel open replacement for Blizzard's BattlemasterList.dbc +
PvpDifficulty.dbc + the AzerothCore-style
battleground_template SQL tables. The 33rd open format
added to the editor.

Defines per-BG gameplay rules: player count brackets,
score-to-win, time limit, objective type (annihilation /
capture flag / control nodes / king of hill / resource race
/ carry object), per-team start positions, respawn timer,
and the WTKN currency token awarded on win.

Cross-references with previously-added formats:
  WBGD.entry.mapId       -> WMS.map.mapId
                            (where mapType=Battleground)
  WBGD.entry.markTokenId -> WTKN.entry.tokenId
                            (Mark of Honor for that BG)

The classic preset's markTokenId fields (102/103/104)
deliberately match WTKN.makePvp's "Mark of Honor: Warsong
Gulch / Arathi Basin / Alterac Valley" token ids — so the
demo content stack already wires together: WBGD.scoreToWin
reached → grant WTKN mark → which a vendor in WTRN can
charge in via item.extendedCost.

Format:
  • magic "WBGD", version 1, little-endian
  • per BG: battlegroundId / mapId / name / description /
    objectiveKind / min+maxPlayersPerSide /
    minLevel..maxLevel / scoreToWin / timeLimitSeconds /
    bracketSize / allianceStart vec3 + facing /
    hordeStart vec3 + facing / respawnTimeSeconds /
    markTokenId

Enums:
  • ObjectiveKind (6): Annihilation / CaptureFlag /
                       ControlNodes / KingOfHill /
                       ResourceRace / CarryObject

API: WoweeBattlegroundLoader::save / load / exists /
findById.

Three preset emitters showcase typical BG shapes:
  • makeStarter — 1 king-of-hill BG (10v10, 3-cap to win)
  • makeClassic — 3 classic BGs with authentic player
                   counts, level brackets, scoring, and
                   WTKN cross-refs (Warsong Gulch CTF /
                   Arathi Basin nodes / Alterac Valley
                   resource race)
  • makeArena   — 3 arena formats (Nagrand 2v2 / Blade's
                   Edge 3v3 / Lordaeron 5v5) — annihilation
                   objective + no respawn + 25 min cap

CLI added (5 flags, 628 documented total now):
  --gen-bg / --gen-bg-classic / --gen-bg-arena
  --info-wbgd / --validate-wbgd

Validator catches: bgId=0 + duplicates, empty name, unknown
objectiveKind, player count=0, min>max counts/levels,
scoreToWin=0 (no win condition), non-annihilation BG with
respawnTimeSeconds=0 (losing side cannot recover).
2026-05-09 17:30:59 -07:00
Kelsi
8ab049ff9d feat(pipeline): add WMOU (Wowee Mount catalog) format
Novel open replacement for Blizzard's Mount.dbc +
MountCapability.dbc + MountType.dbc + the mount-related
subsets of Spell.dbc / Item.dbc. The 32nd open format added
to the editor.

Defines all summonable steeds: ground mounts, flying mounts,
swimming mounts, racial mounts (Tauren Plainsrunner for
druids), and class mounts (Warlock dreadsteed, Paladin
charger). Each mount has a summon spell, optional teach
item, riding skill prerequisite, speed bonus, and faction
/ race availability mask.

Cross-references with previously-added formats:
  WMOU.entry.summonSpellId   -> WSPL.entry.spellId
  WMOU.entry.itemIdToLearn   -> WIT.entry.itemId
  WMOU.entry.requiredSkillId -> WSKL.entry.skillId
                                 (Riding skill ID 762)
  WCHC.race.mountSpellId     ~= WMOU.entry.summonSpellId
                                 (loose match by spellId)

Format:
  • magic "WMOU", version 1, little-endian
  • per mount: mountId / name / description / icon /
    displayId / summonSpellId / itemIdToLearn /
    requiredSkillId+Rank / speedPercent / mountKind /
    factionId / categoryId / raceMask

Enums:
  • Kind (5):     Ground / Flying / Swimming / Hybrid /
                   Aquatic
  • Faction (3):  Both / Alliance / Horde
  • Category (8): Common / Epic / Racial / Event /
                   Achievement / Pvp / Quest / ClassMount

API: WoweeMountLoader::save / load / exists / findById.

Three preset emitters showcase typical mount catalogs:
  • makeStarter — 3 mounts (ground horse + epic flying
                   gryphon + aquatic riding turtle)
  • makeRacial  — 6 racial mounts (4 Alliance: Pinto / Ram /
                   Frostsaber / Mechanostrider; 2 Horde:
                   Dire Wolf / Skeletal Horse) with raceMask
                   gating per WCHC race bit positions
  • makeFlying  — 4 flying mounts spanning Common (60%) ->
                   Epic (100%) -> Achievement (280%) -> Pvp
                   (310%) speed tiers

CLI added (5 flags, 621 documented total now):
  --gen-mounts / --gen-mounts-racial / --gen-mounts-flying
  --info-wmou / --validate-wmou

Validator catches: mountId=0 + duplicates, empty name,
summonSpellId=0 (mount cannot be cast), unknown enum values,
speedPercent=0 (no speed bonus), flying mount with
requiredSkillRank<150 (player can't fly), Racial category
without raceMask (any race could use — usually a typo).
2026-05-09 17:23:00 -07:00
Kelsi
ff4159c369 feat(pipeline): add WSEA (Wowee Seasonal Event) format
Novel open replacement for Blizzard's GameEvents.dbc + the
AzerothCore-style game_event / game_event_creature /
game_event_gameobject SQL tables. The 31st open format
added to the editor.

Calendar-based content: holidays (Hallow's End, Winter's
Veil), recurring promotional events (Children's Week,
Lunar Festival, Brewfest), one-time anniversaries, and
XP-bonus weekends. Each event has a start date, duration,
optional recurrence (yearly / monthly / weekly), faction
restriction, optional XP bonus, and a reward currency
cross-reference into WTKN.

Cross-references with previously-added formats:
  WSEA.entry.tokenIdReward -> WTKN.entry.tokenId
                              (the seasonal currency the
                               event hands out — Tricky
                               Treats during Hallow's End,
                               Brewfest Tokens during
                               Brewfest, etc.)

The yearly preset's tokenIdReward values (200/201/202/203)
deliberately match WTKN.makeSeasonal's seasonal token ids
so the demo content stack already wires together: WSEA
yearly events grant WTKN tokens that vendors can charge in
via WTRN.item.extendedCost.

Format:
  • magic "WSEA", version 1, little-endian
  • per event: eventId / name / description / iconPath /
    announceMessage / startDate (Unix epoch seconds) /
    duration_seconds / recurrenceDays (0=one-shot, 365=yearly) /
    holidayKind / factionGroup / bonusXpPercent / tokenIdReward

Enums:
  • HolidayKind (7): Combat / Collection / Racial /
                     Anniversary / Fishing / Cosmetic /
                     WorldEvent
  • FactionGroup (3): Both / Alliance / Horde

API: WoweeEventLoader::save / load / exists / findById.

Three preset emitters showcase typical event shapes:
  • makeStarter       — 3 events covering Combat /
                         Fishing / Anniversary kinds
  • makeYearly        — 4 yearly holidays with full WTKN
                         cross-refs (Hallow's End / Brewfest /
                         Lunar Festival / Winter's Veil)
  • makeBonusWeekends — 3 monthly Fri-Sun bonus tiers
                         (50% / 100% / 200% RAF-style)

CLI added (5 flags, 614 documented total now):
  --gen-events / --gen-events-yearly / --gen-events-weekends
  --info-wsea / --validate-wsea

Validator catches: eventId=0 + duplicates, empty name,
unknown holidayKind / factionGroup, duration_seconds=0
(event never runs), duration > recurrence period (events
would overlap themselves on next iteration), bonusXpPercent
> 200 (very high — verify intentional).
2026-05-09 17:14:46 -07:00
Kelsi
eefaa5653b feat(pipeline): add WTIT (Wowee Title catalog) format
Novel open replacement for Blizzard's CharTitles.dbc + the
AzerothCore-style character_title SQL table. The 30th open
format added to the editor.

Defines the player-display titles awarded for completing
achievements ("the Versatile"), reaching PvP ranks
("Sergeant Major" / "Stone Guard"), participating in raids
("Champion of the Naaru"), levelling a profession ("Master
Locksmith"), or seasonal events ("Brewmaster", "the
Hallowed").

Closes a long-standing gap: WACH.entry.titleReward has been
a free-form string since batch 116 with no formal catalog
to resolve against. WTIT systematizes those strings into a
real catalog — the runtime resolves WACH.titleReward to a
WTIT entry by name, then displays the titleId in the player
title selector.

Cross-references:
  WACH.entry.titleReward (string) ~= WTIT.entry.name
                                     (string match — runtime
                                      resolves achievement-
                                      granted titles by
                                      looking up matching WTIT
                                      entry by name)

Format:
  • magic "WTIT", version 1, little-endian
  • per title: titleId / name / nameMale / nameFemale /
    iconPath / prefix (suffix vs prefix display) /
    category / sortOrder

Enums:
  • Category (8): Achievement / Pvp / Raid / ClassTitle /
                   Event / Profession / Lore / Custom

API: WoweeTitleLoader::save / load / exists +
WoweeTitle::findById / findByName.

Three preset emitters showcase typical title catalogs:
  • makeStarter     — 4 titles (Versatile / Sergeant /
                       Champion / Hallowed) covering 4
                       categories
  • makePvp         — 28-title classic Honor System ladder
                       (14 Alliance ranks Private->Grand
                       Marshal + 14 Horde ranks Scout->High
                       Warlord)
  • makeAchievement — 8 achievement titles including "the
                       Versatile" matching WACH.makeMeta's
                       achievement 250 titleReward + capstone
                       profession titles

CLI added (5 flags, 608 documented total now):
  --gen-titles / --gen-titles-pvp / --gen-titles-achievement
  --info-wtit / --validate-wtit

Validator catches: titleId=0 + duplicates, empty name,
unknown category, gender variants set on only one side
(causes mixed-gender display when the runtime falls back to
canonical for the unset side).
2026-05-09 17:07:56 -07:00
Kelsi
8c78f8aeb2 feat(pipeline): add WTRG (Wowee Area Trigger) format
Novel open replacement for Blizzard's AreaTrigger.dbc +
AreaTriggerTeleport.dbc + the AzerothCore-style
areatrigger_template / areatrigger_teleport SQL tables.
The 29th open format added to the editor.

Defines proximity-based event zones — when a player enters
a defined region (box or sphere), the runtime fires the
trigger's action: teleport to another map, award
exploration XP for a quest, run a server script, gate an
instance entrance behind a key item, mark a PvP boundary,
or simply display a "Discovered: {area name}" banner.

Cross-references with previously-added formats — every
trigger field has a real format target:
  WTRG.entry.mapId / areaId  -> WMS.map.mapId / WMS.area.areaId
  WTRG.actionTarget (Teleport)        -> WMS.mapId
  WTRG.actionTarget (QuestExploration) -> WQT.questId
  WTRG.requiredQuestId       -> WQT.entry.questId
  WTRG.requiredItemId        -> WIT.entry.itemId (key)

Format:
  • magic "WTRG", version 1, little-endian
  • per trigger: triggerId / mapId / areaId / name /
    center vec3 / shape / kind / boxDims vec3 / radius /
    actionTarget / dest vec3 / destOrientation /
    requiredQuestId / requiredItemId / minLevel

Enums:
  • Shape (2): Box / Sphere
  • Kind (7): Teleport / QuestExploration / Script /
              InstanceEntrance / AreaName / CombatStartZone /
              Waypoint

API: WoweeTriggerLoader::save / load / exists / findById.

Three preset emitters showcase common trigger shapes:
  • makeStarter    — area-name + quest-exploration with
                      cross-ref to WQT 100 ("Investigate the
                      Camp")
  • makeDungeon    — outdoor area-name + portal-style
                      InstanceEntrance with Deadmines key
                      gate (WIT itemId 5200, matches
                      WLCK.makeDungeon's Boss Vault Seal) +
                      interior exit teleport back outdoors
  • makeFlightPath — 2 sphere waypoints near flight masters
                      so the runtime can auto-open the
                      flight UI on proximity (matches WTAX
                      starter node positions)

CLI added (5 flags, 601 documented total now):
  --gen-triggers / --gen-triggers-dungeon / --gen-triggers-flightpath
  --info-wtrg / --validate-wtrg

Validator catches: triggerId=0 + duplicates, unknown shape /
kind, non-finite center, sphere with radius<=0, box with
all-zero half-extents, teleport / instance with dest=(0,0,0)
(silently does nothing — usually a typo), QuestExploration
without an actionTarget questId.
2026-05-09 17:01:43 -07:00
Kelsi
b632554b5b feat(pipeline): add WTKN (Wowee Token catalog) format
Novel open replacement for Blizzard's Currency.dbc +
CurrencyCategory.dbc + CurrencyTypes.dbc + the AzerothCore-
style player_currency SQL tables. The 28th open format
added to the editor.

Defines secondary currency tokens beyond gold: Honor Points
(PvP), Arena Points (rated PvP), Marks of Honor (per
battleground), faction reputation tokens, holiday-event
currencies. Each token has a balance cap, optional weekly
cap (regenerating earnings limit), and a category for
grouping in the player's currency tab.

Cross-references:
  WTRN.item.extendedCost -> WTKN.entry.tokenId
                             (vendors can charge in tokens
                              instead of copper — when
                              extendedCost > 0 the runtime
                              looks up the matching token)

Format:
  • magic "WTKN", version 1, little-endian
  • per token: tokenId / name / description / iconPath /
    category / maxBalance / weeklyCap / flags

Enums:
  • Category (6): Misc / Pvp / Reputation / Crafting /
                   Seasonal / Holiday
  • Flags:        AccountWide / Tradeable / HiddenUntilEarned /
                   ResetsOnLogout / ConvertsToGold

API: WoweeTokenLoader::save / load / exists / findById.

Three preset emitters showcase typical token shapes:
  • makeStarter  — 3 tokens (Honor / Marks / Stormwind Guard
                    rep) covering Pvp + Reputation categories
  • makePvp      — full PvP set: Honor (75k) + Arena (5k +
                    weekly 1500) + 6 BG marks of honor for
                    classic + TBC + WotLK battlegrounds
  • makeSeasonal — 4 holiday tokens (Tricky Treats /
                    Brewfest / Coin of Ancestry / Stranger's
                    Gift) all flagged ResetsOnLogout to make
                    them event-bound

CLI added (5 flags, 592 documented total now):
  --gen-tokens / --gen-tokens-pvp / --gen-tokens-seasonal
  --info-wtkn / --validate-wtkn

Validator catches: tokenId=0 + duplicates, empty name,
unknown category, weeklyCap > maxBalance (cap unreachable),
ResetsOnLogout + AccountWide combo (incoherent — account
state survives logout by definition).
2026-05-09 16:53:11 -07:00
Kelsi
e66601c208 feat(pipeline): add WCHC (Wowee Character Classes/Races) format
Novel open replacement for Blizzard's CharClasses.dbc +
CharRaces.dbc + CharStartOutfit.dbc trio. The 27th open
format added to the editor — completes the foundational
character-creation surface.

One file holds three flat arrays:
  • classes — playable classes (Warrior / Mage / etc.) with
              power type (mana/rage/focus/energy/runic),
              base HP+power scaling, faction availability
  • races   — playable races with faction (Alliance/Horde/
              Neutral), starting map+zone, default language
              spell, base stats, racial mount spell
  • outfits — starting gear loadout per (class, race, gender)
              triple, listing item IDs and display slots

Cross-references with previously-added formats:
  WCHC.race.startingMapId        -> WMS.map.mapId
  WCHC.race.startingZoneAreaId   -> WMS.area.areaId
  WCHC.race.defaultLanguageSpellId -> WSPL.entry.spellId
  WCHC.race.mountSpellId         -> WSPL.entry.spellId
  WCHC.outfit.items.itemId       -> WIT.entry.itemId

The starter preset's outfits use real WIT itemIds (1=Worn
Shortsword, 2=Linen Vest, 3=Healing Potion) so the demo
content stack is consistent: a freshly created Human Warrior
in WCHC starts with WIT items 1/2/3, drops them on death
into a WLOT-tracked corpse loot, and can be respawned via
WSPN, etc.

Format:
  • magic "WCHC", version 1, little-endian
  • classes[]: classId / name / icon / powerType / display /
    baseHP+perLevel / basePower+perLevel / factionAvailability
  • races[]: raceId / name / icon / factionId / male+female
    displayId / 5 base stats / startingMap+zone /
    defaultLanguage+mount spell IDs
  • outfits[]: classId+raceId+gender + items[]
    (each: itemId + displaySlot)

Enums:
  • PowerType (6): Mana / Rage / Focus / Energy / RunicPower / Runes
  • RaceFaction (3): Alliance / Horde / Neutral
  • Gender: Male / Female
  • FactionAvailability bitmask: AvailableAlliance, AvailableHorde

API: WoweeCharsLoader::save / load / exists +
WoweeChars::findClass / findRace / findOutfit (by class+race+gender).

CLI added (5 flags, 585 documented total now):
  --gen-chars / --gen-chars-alliance / --gen-chars-allraces
  --info-wchc / --validate-wchc

Validator catches: ids unique, baseHealth=0 (instant-death
character), factionAvailability=0 (no faction can pick),
empty names, factionId out of range, outfit references to
non-existent class/race ids (cross-format resolution),
gender > 1, outfit items with itemId=0, outfit with no
items (warning — naked character).
2026-05-09 16:47:04 -07:00
Kelsi
82a8c3559e feat(pipeline): add WMS (Wowee Map / Area) catalog format
Novel open replacement for Blizzard's Map.dbc + AreaTable.dbc
+ the AzerothCore-style world_zone SQL tables. The 26th open
format added to the editor.

Defines two related kinds of locator in one catalog:
  • Maps  — top-level worlds (continents / instances / raids /
            battlegrounds / arenas) with a friendly name,
            type, expansion tag, and player-count cap.
  • Areas — sub-zones within maps with friendly names, parent-
            area chain, recommended level range, faction-
            territory marker (alliance / horde / contested /
            both), exploration XP, and an ambient-sound
            cross-reference into WSND.

The runtime uses Areas for minimap labels, location strings
under the player frame, "Discover Sub-zone" XP gains, and
ambient-music selection on zone entry.

Cross-references with previously-added formats:
  WMS.area.ambienceSoundId    -> WSND.entry.soundId
  WMS.area.parentAreaId       -> WMS.area.areaId (intra-format
                                   sub-zone hierarchy)
  WSPN entries are tied to WMS.area boundaries by
  world position (no direct ID — the runtime resolves
  position -> area at lookup time)

Format:
  • magic "WMSX", version 1, little-endian
  • maps[] (each): mapId / name / shortName / mapType /
    expansionId / maxPlayers
  • areas[] (each): areaId / mapId / parentAreaId / name /
    minLevel..maxLevel / factionGroup / explorationXP /
    ambienceSoundId

Enums:
  • MapType (5):     Continent / Instance / Raid / Battleground / Arena
  • ExpansionId (5): Classic / Tbc / Wotlk / Cata / Mop
  • FactionGroup:    Both / Alliance / Horde / Contested
                      (PvP-flagging zone)

API: WoweeMapsLoader::save / load / exists +
WoweeMaps::findMap / findArea.

Three preset emitters showcase the catalog shape:
  • makeStarter — 1 continent + 3 areas with parent chain
                   (Goldshire is a sub-zone of Elwynn Forest)
  • makeClassic — 2 continents + Deadmines instance + 6
                   areas (Stormwind/Elwynn/Goldshire/Westfall/
                   Duskwood/Teldrassil/Deadmines) with WSND
                   ambient-sound refs
  • makeBgArena — Alterac Valley (40-player BG) + Nagrand
                   Arena (5v5 with maxPlayers=10)

CLI added (5 flags, 578 documented total now):
  --gen-maps / --gen-maps-classic / --gen-maps-bgarena
  --info-wms / --validate-wms

Validator catches: empty map name, unknown mapType / expansion,
BG/Arena with maxPlayers=0 (no participant cap), area ids=0
+ duplicates, empty area name, maxLevel < minLevel, areas
referencing non-existent maps, parentAreaId chains crossing
maps (sub-zones must be on the same world), self-parent.
2026-05-09 16:40:00 -07:00
Kelsi
99baf2d0c4 feat(pipeline): add WTAL (Wowee Talent catalog) format
Novel open replacement for Blizzard's TalentTab.dbc +
Talent.dbc + the AzerothCore-style talent_progression SQL
tables. The 25th open format added to the editor.

Defines class talent specialization trees: per-class set
of named tabs (Arms / Fury / Protection for warrior, Fire
/ Frost / Arcane for mage), each with talents arranged in
a row/column grid, each talent having up to 5 ranks and
an optional prerequisite chain.

Cross-references with previously-added formats:
  WTAL.talent.prereqTalentId -> WTAL.talent.talentId
                                 (intra-format chain)
  WTAL.talent.rankSpellIds[] -> WSPL.entry.spellId
                                 (spell granted at each rank)

Format:
  • magic "WTAL", version 1, little-endian
  • per tree: treeId / name / iconPath / requiredClassMask /
    talents[] (row, col, maxRank, prereqTalentId+rank,
    rankSpellIds[5] zero-padded for unused ranks)

Enums:
  • ClassMask: bit positions match canonical CharClasses.dbc
    classIds — Warrior / Paladin / Hunter / Rogue / Priest /
    DK / Shaman / Mage / Warlock / Druid

API: WoweeTalentLoader::save / load / exists +
WoweeTalent::findTree / findTalent (global lookup across
all trees in the catalog).

Three preset emitters showcase tree shapes:
  • makeStarter — 1 small tree (3-talent vertical chain)
  • makeWarrior — 3 trees (Arms 4 / Fury 4 / Protection 3)
                   with WSPL cross-refs at capstones
                   (Mortal Strike -> WSPL 12294, Battle Shout
                   -> WSPL 6673, Thunder Clap -> WSPL 6343)
  • makeMage    — 3 trees (Arcane / Fire / Frost) with
                   capstones referencing Frostbolt 116 /
                   Fireball 133 / Blink 1953 from WSPL

CLI added (5 flags, 571 documented total now):
  --gen-talents / --gen-talents-warrior / --gen-talents-mage
  --info-wtal / --validate-wtal

Validator catches: tree+talent ids=0 or duplicates, empty
tree name, requiredClassMask=0 (every class would see this
tree — usually a typo), maxRank not in 1..5, talent listing
itself as prerequisite, prereqTalentId pointing at a
talent that doesn't exist in this catalog (intra-format
cross-reference resolution), prereqRank=0 or > the prereq
talent's maxRank (catches off-by-one references), gaps in
rankSpellIds progression (rank N has spell but rank N-1
doesn't — usually a typo).

The validator caught a real authoring bug in the makeMage /
makeWarrior presets during smoke testing — initial check
was comparing prereqRank against the WRONG talent's maxRank
(this talent's rather than the prereq's). Fixed in the same
commit by hoisting the check into the cross-reference
resolution pass where the prereq talent is in hand.
2026-05-09 16:33:45 -07:00
Kelsi
3b107459b2 feat(pipeline): add WTAX (Wowee Taxi catalog) format
Novel open replacement for Blizzard's TaxiNodes.dbc +
TaxiPath.dbc + TaxiPathNode.dbc. The 24th open format
added to the editor.

Defines the flight-master network: a set of named nodes
(positions on the world map) plus the paths between them
(sequences of waypoints with per-segment delay and a
per-path gold cost). The same file holds both node and
path lists — flat arrays keyed by id, with intra-format
references from path.fromNodeId / toNodeId to node.nodeId.

Cross-references:
  WCRT.entry (with FlightMaster npcFlag) ~= WTAX.nodeId
                                            (matched by world
                                             position; flight
                                             master NPCs stand
                                             at their nodes)
  WTAX.path.fromNodeId / toNodeId -> WTAX.entry.nodeId
                                     (intra-format graph)

Format:
  • magic "WTAX", version 1, little-endian
  • nodes (each): nodeId / mapId / name / iconPath /
    position / faction restrictions
  • paths (each): pathId / from+toNodeId / moneyCostCopper /
    waypoints[] each with position + per-waypoint delaySec

API: WoweeTaxiLoader::save / load / exists +
WoweeTaxi::findNode / findPath / findPathBetween.

Three preset emitters showcase different graph shapes:
  • makeStarter  — 2 nodes + 2 paths (round-trip)
  • makeRegion   — 4 nodes at a 500m square + 4-path
                    directed ring (NW->NE->SE->SW->NW)
  • makeContinent — 6 nodes hub-spoke + 3 perimeter
                     shortcuts; intermediate waypoints
                     climb to altitude 120m for visual
                     arc effect

CLI added (5 flags, 564 documented total now):
  --gen-taxi / --gen-taxi-region / --gen-taxi-continent
  --info-wtax / --validate-wtax

Validator catches: nodeId/pathId=0 + duplicates, empty node
name, non-finite positions, fromNodeId == toNodeId
(self-loop path), path references to non-existent nodes
(intra-format cross-reference resolution), negative
waypoint delays.
2026-05-09 16:26:27 -07:00
Kelsi
2de08a3fd0 feat(pipeline): add WGSP (Wowee Gossip Menu) format
Novel open replacement for AzerothCore-style gossip_menu +
gossip_menu_option + npc_text SQL tables PLUS the Blizzard
NpcText.dbc family. The 23rd open format added to the
editor.

An NPC's dialogue tree: a menu of options the player can
pick from when right-clicking the NPC. Each option may
bridge to another menu, trigger a vendor / trainer
interaction, offer a quest, etc. The simplified per-option
model (kind + actionTarget + flags + moneyCost) covers the
common cases without needing separate npc_text condition
tables.

Closes a major cross-format gap: WCRT.entry.gossipId has
existed since batch 116 (when WCRT was added) but pointed
to a format that didn't exist yet. The innkeeper preset's
menuId=4001 deliberately matches WCRT's Bartleby NPC so
the demo content stack can wire WCRT.gossipId = 4001 once
that field is plumbed through the runtime.

Cross-references:
  WCRT.entry.gossipId        -> WGSP.entry.menuId
  WGSP.option.actionTarget (Submenu) -> WGSP.entry.menuId
  WGSP.option.actionTarget (Vendor / Trainer)
                              -> WTRN.entry.npcId
  WGSP.option.actionTarget (Quest)  -> WQT.entry.questId

Format:
  • magic "WGSP", version 1, little-endian
  • per menu: menuId / titleText + options[]
  • per option: optionId / text / kind / actionTarget /
    requiredFlags / moneyCostCopper

Enums:
  • OptionKind (13): Close / Submenu / Vendor / Trainer /
                     Quest / Tabard / Banker / Innkeeper /
                     FlightMaster / TextOnly / Script /
                     Battlemaster / Auctioneer
  • OptionFlags:    AllianceOnly / HordeOnly / Coinpouch /
                     QuestGated / Closes

API: WoweeGossipLoader::save / load / exists / findById;
presets makeStarter (1 menu with vendor + trainer + close),
makeInnkeeper (2-menu tree: main menu 4001 with hearth /
vendor / flight / submenu options + lore submenu 4002 that
links back), makeQuestGiver (1 menu with 2 quest options
referencing WQT 1 and 100, plus a paid respec script
exercising the Coinpouch flag with a 10g cost).

CLI added (5 flags, 558 documented total now):
  --gen-gossip / --gen-gossip-innkeeper / --gen-gossip-questgiver
  --info-wgsp / --validate-wgsp

Validator catches: menuId=0 + duplicates, empty title /
options, unknown option kind, empty option text, Submenu
options pointing at non-existent menuIds (intra-format
cross-reference resolution), Coinpouch flag without
moneyCost (misleading UI), AllianceOnly+HordeOnly conflict.
2026-05-09 16:20:07 -07:00
Kelsi
d2ca3ea22b feat(pipeline): add WTRN (Wowee Trainer / Vendor catalog) format
Novel open replacement for AzerothCore-style npc_trainer +
npc_vendor SQL tables PLUS the Blizzard TrainerSpells.dbc
family. The 22nd open format added to the editor.

Unifies trainer spell lists and vendor item inventories
into one per-NPC entry. A creature flagged Trainer or
Vendor in WCRT references a WTRN entry that lists what they
teach / sell. The same NPC can be both — kindMask is a
bitmask covering the Trainer (0x01) and Vendor (0x02) kinds.

This format closes a major cross-format gap: WCRT.npcFlags
already had Vendor / Trainer bits, but until now there was
no format defining what a vendor sells or what a trainer
teaches. Now an NPC marked Vendor in WCRT has a real
inventory, and an NPC marked Trainer has a real spell list.

Cross-references — every WTRN field has a real format target:
  WTRN.entry.npcId           -> WCRT.entry.creatureId
  WTRN.spell.spellId         -> WSPL.entry.spellId
  WTRN.spell.requiredSkillId -> WSKL.entry.skillId
  WTRN.item.itemId           -> WIT.entry.itemId

Format:
  • magic "WTRN", version 1, little-endian
  • per NPC: npcId / kindMask / greeting + spells[] + items[]
  • per spell offer: spellId / moneyCostCopper /
    requiredSkillId / requiredSkillRank / requiredLevel
  • per item offer: itemId / stockCount (0xFFFFFFFF =
    unlimited) / restockSec / extendedCost / moneyCostCopper
    (0 = inherit from WIT.buyPrice)

API: WoweeTrainerLoader::save / load / exists / findByNpc;
presets makeStarter (innkeeper 4001 as both trainer +
vendor: teaches First Aid + sells starter items),
makeMageTrainer (NPC 4003 teaches the WSPL mage spells
at scaling cost), makeWeaponVendor (NPC 4002 sells WIT
weapons with mixed unlimited/finite stock + restock timers).

CLI added (5 flags, 551 documented total now):
  --gen-trainers / --gen-trainers-mage / --gen-trainers-weapons
  --info-wtrn / --validate-wtrn

Validator catches: npcId=0 + duplicates, kindMask=0 (NPC
offers nothing), Trainer flag without spells, Vendor flag
without items, spells/items present without the matching
kind bit (silently ignored at runtime), spellId=0 / itemId=0
in offers, finite stock with restockSec=0 (single-fill —
usually intentional but worth surfacing).

The 3 presets deliberately use npcIds matching WCRT village
merchants (4001/4002/4003) so the demo content stack is
self-consistent: WCRT 4001 has the Vendor + Trainer flag,
and WTRN 4001 actually defines what they sell and teach.
2026-05-09 16:12:58 -07:00
Kelsi
e5eaf13866 feat(pipeline): add WACH (Wowee Achievement Catalog) format
Novel open replacement for Blizzard's Achievement.dbc +
AchievementCriteria.dbc + AchievementCategory.dbc + the
AzerothCore-style character_achievement /
character_achievement_progress SQL tables. The 21st open
format added to the editor.

Each achievement carries display metadata (name, description,
icon, points, faction restriction) plus a list of criteria
the player must satisfy. Criteria mirror the WQT objective
model (kind + targetId + quantity), so the runtime can
reuse the same progress-tracking machinery for both quests
and achievements.

Cross-references with previously-added formats — every
criterion kind has a real format target:
  WACH.criteria.targetId (kind=KillCreature)    -> WCRT.creatureId
  WACH.criteria.targetId (kind=CompleteQuest)   -> WQT.questId
  WACH.criteria.targetId (kind=LootItem)        -> WIT.itemId
  WACH.criteria.targetId (kind=CastSpell)       -> WSPL.spellId
  WACH.criteria.targetId (kind=ReachSkillLevel) -> WSKL.skillId
  WACH.criteria.targetId (kind=EarnReputation)  -> WFAC.factionId
  WACH.criteria.targetId (kind=CompleteAchievement) -> WACH.achievementId
                                                       (meta-achievements)

Format:
  • magic "WACH", version 1, little-endian
  • per achievement: id / categoryId / name / description /
    iconPath / titleReward / points / minLevel / faction /
    flags / criteria[]
  • per criterion: criteriaId / kind / targetId / quantity /
    description

Enums:
  • CriteriaKind (9): KillCreature / CompleteQuest / LootItem /
                      ReachLevel / EarnReputation / CastSpell /
                      ReachSkillLevel / VisitArea /
                      CompleteAchievement
  • Faction:    Both / Alliance / Horde
  • Flags:      HiddenUntilEarned / ServerFirst / RealmFirst /
                 Tracking / Counter / Account

API: WoweeAchievementLoader::save / load / exists /
findById; presets makeStarter (3 simple kill/quest/level
demos), makeBandit (3 with WCRT/WGOT/WQT cross-refs),
makeMeta (3 base + 1 meta-achievement granting "the
Versatile" title, exercising CompleteAchievement criterion
kind that lets achievements depend on other achievements).

CLI added (5 flags, 542 documented total now):
  --gen-achievements / --gen-achievements-bandit / --gen-achievements-meta
  --info-wach / --validate-wach

Validator catches: achievementId=0 + duplicates, empty name,
faction out of range, no criteria (achievement can never
be earned), criterion quantity=0, unknown criterion kind,
targetId=0 on criterion kinds that need a real resource
reference (everything except ReachLevel which uses the
quantity field for the level number).

The bandit preset's cross-references close the gameplay
graph end-to-end: kill 50 creatureId=1000 (matches WCRT/
WSPN/WLOT bandit), loot objectId=2000 (matches WGOT bandit
strongbox), complete questId=1 (matches WQT Bandit Trouble).
The meta preset closes a separate loop: 3 sub-achievements
covering Mining (skillId=186), Lockpicking (skillId=633),
and Frostbolt cast count (spellId=116) — each pointing at
a real WSKL/WSPL entry that already exists in the demo
content stack.
2026-05-09 16:04:30 -07:00
Kelsi
5ea1f7ee2a feat(pipeline): add WSPL (Wowee Spell Catalog) format
Novel open replacement for Blizzard's Spell.dbc +
SpellEffect.dbc + the AzerothCore-style spell_dbc /
spell_proc tables. The 20th open format added to the
editor — completes the canonical-data side of the gameplay
graph.

Each entry holds the metadata side of a spell: name,
description, school, range, mana / cast / cooldown times,
plus a single primary effect. The simplified effect model
(one effectKind + min/max value + misc field) covers the
common cases (damage / heal / buff / debuff / teleport /
summon / dispel) without needing to reproduce the full
multi-effect graph that classic Spell.dbc carries.

Cross-references with previously-added formats:
  WLCK.channel.targetId (kind=Spell)       -> WSPL.entry.spellId
  WQT.objective.targetId (kind=SpellCast)  -> WSPL.entry.spellId
  WCRT.equippedMain (item with on-use)     -> WIT -> WSPL

Format:
  • magic "WSPL", version 1, little-endian
  • per spell: spellId / name / description / iconPath /
    school / targetType / effectKind / cast & cooldown &
    GCD ms / manaCost / range min..max / minLevel /
    maxStacks / durationMs / effectValueMin..Max /
    effectMisc / flags

Enums:
  • School (7):     Physical / Holy / Fire / Nature / Frost /
                     Shadow / Arcane
  • TargetType (6): Self / Single / Cone / AoeFromSelf /
                     Line / Ground
  • EffectKind (7): Damage / Heal / Buff / Debuff / Teleport /
                     Summon / Dispel
  • Flags:          Passive / Hidden / Channeled / Ranged /
                     AreaOfEffect / Triggered / UnitTargetOnly /
                     FriendlyOnly / HostileOnly

API: WoweeSpellLoader::save / load / exists / findById;
presets makeStarter (Strike + Lesser Heal + Power Word:
Fortitude + Hearthstone, one per major effect kind),
makeMage (Frostbolt 116 + Fireball 133 + Arcane Intellect
1459 + Blink 1953, canonical Classic spellIds), makeWarrior
(Heroic Strike 78 + Thunder Clap 6343 + Battle Shout 6673 +
Mortal Strike 12294).

CLI added (5 flags, 535 documented total now):
  --gen-spells / --gen-spells-mage / --gen-spells-warrior
  --info-wspl / --validate-wspl

Validator catches: spellId=0 + duplicates, empty name,
school out of range, effectKind out of range, NaN range,
range/value min>max, FriendlyOnly+HostileOnly conflict
(incoherent), friendly-only with damage/debuff effect
(incoherent), hostile-only with heal/buff effect, buff/debuff
effect with durationMs=0 (instant fade — almost certainly
authoring oversight).

The validator caught a real preset-emitter authoring error
during initial smoke testing — buff spells were setting
effectValueMin without effectValueMax (validator's range
check immediately flagged it), prompting an in-batch fix
to set both fields. This is exactly the catch-the-typo
purpose validators serve.
2026-05-09 15:58:09 -07:00
Kelsi
95e593e59c feat(pipeline): add WSKL (Wowee Skill Catalog) format
Novel open replacement for Blizzard's SkillLine.dbc +
SkillLineCategory.dbc + the AzerothCore-style player skill
base tables. The 19th open format added to the editor.

Defines every player-trackable skill: weapon proficiencies
(Swords, Axes, Bows), professions (Mining, Alchemy,
Cooking), languages (Common, Dwarvish), class
specializations (Fire, Frost, Holy, Protection), armor
proficiencies (Mail, Plate), and secondary skills (First
Aid, Lockpicking, Riding).

Cross-references with previously-added formats:
  WLCK.channel.targetId (kind=Lockpick) -> WSKL.entry.skillId
  WGOT.entry.requiredSkill              -> WSKL.entry.skillId

The starter preset's skillIds 186 (Mining) and 633
(Lockpicking) deliberately match the canonical IDs already
referenced by WGOT.makeGather and WLCK.makeDungeon —
so the demo content stack now wires together end-to-end:
WGOT herb-node requires skill 186 -> WSKL Mining at rank 1+;
WLCK bandit-strongbox channel requires skill 633 -> WSKL
Lockpicking at rank 1+.

Format:
  • magic "WSKL", version 1, little-endian
  • per skill: skillId / name / description / categoryId /
    canTrain / maxRank / rankPerLevel / iconPath

Enums:
  • CategoryId (8): Weapon / Class / Profession /
    SecondaryProfession / Language / ArmorProficiency /
    Riding / WeaponSpec

API: WoweeSkillLoader::save / load / exists / findById;
presets makeStarter (5-skill demo with cross-referenced
canonical IDs), makeProfessions (12 classic professions:
9 primary + 3 secondary), makeWeapons (16 weapon skills
with canonical SkillLine IDs and rankPerLevel=5 auto-grow).

CLI added (5 flags, 528 documented total now):
  --gen-skills / --gen-skills-professions / --gen-skills-weapons
  --info-wskl / --validate-wskl

Validator catches: skillId=0 + duplicates, empty name,
maxRank=0, unknown categoryId, suspicious maxRank=1 on
non-Language skill (only languages cap at 1), weapon skill
with rankPerLevel=0 (won't auto-grow on use).
2026-05-09 15:50:25 -07:00
Kelsi
81b1897a24 feat(pipeline): add WLCK (Wowee Lock Template) format
Novel open replacement for Blizzard's Lock.dbc. The 18th
open format added to the editor. Closes the cross-reference
gap from WGOT.entry.lockId — until now that field pointed
to a format that didn't exist yet.

A lock is a multi-channel security check. Each lock has up
to 5 independent channels; a player can open the lock by
satisfying ANY ONE channel:
  • Item     — requires a specific key item (WIT cross-ref)
  • Lockpick — requires the lockpicking skill at minimum rank
                (rogue / engineering profession)
  • Spell    — requires casting a specific spell
  • Damage   — can be forced open with attack damage

Cross-references with previously-added formats:
  WGOT.entry.lockId               -> WLCK.entry.lockId
  WLCK.channel.targetId (Item)    -> WIT.entry.itemId
  WLCK.channel.targetId (Lockpick) -> future WSKL skillId
  WLCK.channel.targetId (Spell)   -> future WSPL spellId

The starter and dungeon presets' lockIds (1 and 2)
deliberately match WGOT.makeDungeon's iron-door lockId=1
and bandit-strongbox lockId=2, so the demo content stack
already wires together: WSPN spawn -> WGOT object template
-> WLCK lock template -> WIT key items.

Format:
  • magic "WLCK", version 1, little-endian
  • per lock: lockId / name / flags / 5 fixed channel slots
  • per channel: kind / skillRequired / targetId
  • all 5 slots written even when unused (kind=None +
    zeroed fields), keeping the per-entry size constant for
    fast random access

Enums:
  • ChannelKind: None / Item / Lockpick / Spell / Damage
  • Flags:       DestructOnOpen / RespawnOnKey / TrapOnFail

API: WoweeLockLoader::save / load / exists / findById;
presets makeStarter (Iron Door + Wooden Chest), makeDungeon
(matches WGOT cross-references; light/heavy lockpicks +
boss-key-only seal), makeProfessions (4-tier rogue lockpick
progression at ranks 1/100/175/250).

CLI added (5 flags, 521 documented total now):
  --gen-locks / --gen-locks-dungeon / --gen-locks-professions
  --info-wlck / --validate-wlck

Validator catches: lockId=0 + duplicates, all-None channels
(lock can never open), Item/Spell/Lockpick channels with
targetId=0 (no resource referenced), unknown channel kind,
skillRequired set on non-Lockpick channel (silently ignored
at runtime — flag as warning).
2026-05-09 15:44:26 -07:00
Kelsi
4868f780cc feat(pipeline): add WFAC (Wowee Faction Catalog) format
Novel open replacement for Blizzard's Faction.dbc +
FactionTemplate.dbc + the AzerothCore-style
reputation_reward / reputation_spillover SQL tables. The
17th open format added to the editor.

Combines the "displayable Faction" (player-facing name +
reputation thresholds for friendly/honored/revered/exalted)
with the "FactionTemplate matrix" (which factions are
hostile to which) into one entry. The runtime walks the
catalog to answer two questions:
  • "Will faction A attack faction B on sight?" -> enemy list
  • "What rep tier is the player with X?"      -> thresholds

Cross-references with previously-added formats:
  WCRT.entry.factionId       -> WFAC.entry.factionId
  WFAC.entry.parentFactionId -> WFAC.entry.factionId
  WFAC.entry.enemies[]       -> WFAC.entry.factionId
  WFAC.entry.friends[]       -> WFAC.entry.factionId

The starter preset's factionId 35 (Friendly) and 14
(Hostile) deliberately match the WCRT preset defaults, so
the demo content stack is consistent: WCRT.makeBandit's
factionId=14 has a real entry in WFAC.makeStarter that
declares it hostile to friendly NPCs (35) and players (1).

Format:
  • magic "WFAC", version 1, little-endian
  • per faction: factionId / parentFactionId / name /
    description / reputationFlags / baseReputation /
    7 ascending tier thresholds (hostile..exalted) /
    enemies[] / friends[]

Enums:
  • ReputationFlags: VisibleOnTab / AtWarDefault / Hidden /
                      NoReputation / IsHeader (group label)
  • Tier (canonical): Hated / Hostile / Unfriendly /
                       Neutral / Friendly / Honored /
                       Revered / Exalted

API: WoweeFactionLoader::save / load / exists / findById +
WoweeFaction::isHostile(a, b); presets makeStarter (3-faction
demo matching WCRT defaults), makeAlliance (header +
Stormwind / Darnassus / Ironforge with reciprocal friend
lists + Defias enemy), makeWildlife (4 beast factions, each
hostile to player but ignoring other beasts).

CLI added (5 flags, 514 documented total now):
  --gen-factions / --gen-factions-alliance / --gen-factions-wildlife
  --info-wfac / --validate-wfac

Validator catches: factionId=0 + duplicates, empty name,
threshold ordering violations (hostile must be < unfriendly
< neutral < ... < exalted), self-listed as enemy or friend,
faction in both enemies and friends (incoherent).
2026-05-09 15:37:59 -07:00
Kelsi
f8d7b6b6bd feat(pipeline): add WGOT (Wowee Game Object Template) format
Novel open replacement for AzerothCore-style
gameobject_template SQL tables PLUS the Blizzard
GameObjectDisplayInfo.dbc / GameObject types metadata. The
16th open format added to the editor.

Game objects are the non-creature interactable scenery:
chests (with loot), doors, buttons, mailboxes, herb / ore
gathering nodes, fishing pools, signposts, mounts. Each
has a displayId for the model, a typeId driving its
interaction logic, and optional cross-references to a lock
(future WLCK) and loot table (existing WLOT).

Cross-references with previously-added formats:
  WSPN.entry.entryId (kind=GameObject) -> WGOT.entry.objectId
  WGOT.entry.lootTableId               -> WLOT.entry.creatureId
                                          (loot tables are
                                           universal — chests
                                           and creatures both
                                           key by ID)

The dungeon preset's Bandit Strongbox uses lootTableId=2000
to match WLOT's bandit chest table id, so the demo content
stack already wires together: spawn (WSPN object kind 2000)
-> object template (WGOT 2000) -> loot table (WLOT 2000).

Format:
  • magic "WGOT", version 1, little-endian
  • per object: objectId / displayId / name / typeId /
    size / castBarCaption / requiredSkill +
    requiredSkillValue / lockId / lootTableId /
    minOpenTimeMs..maxOpenTimeMs / flags

Enums:
  • TypeId (16): Door / Button / Chest / Container /
    QuestGiver / Text / Trap / Goober / Transport /
    Mailbox / MineralNode / HerbNode / FishingNode /
    Mount / Sign / Bonfire
  • Flags: Disabled / ScriptOnly / UsableFromMount /
    Despawn / Frozen / QuestGated

API: WoweeGameObjectLoader::save / load / exists /
findById; presets makeStarter (chest + mailbox + sign),
makeDungeon (door + button + 2 chests + trap with proper
WLOT cross-references), makeGather (Peacebloom herb +
Tin Vein ore + fishing pool with skill requirements).

CLI added (5 flags, 507 documented total now):
  --gen-objects / --gen-objects-dungeon / --gen-objects-gather
  --info-wgot / --validate-wgot

Validator catches: objectId=0 + duplicates, size<=0,
minOpenTime>maxOpenTime, gathering node without skill
requirement (anyone can harvest — usually a typo), chest
without loot table (script must populate), requiredSkillValue
set without requiredSkill (incoherent).
2026-05-09 15:31:49 -07:00
Kelsi
02ae17740e feat(pipeline): add WQT (Wowee Quest Template) format
Novel open replacement for AzerothCore-style quest_template
SQL tables PLUS the Blizzard Quest.dbc / QuestObjective.dbc
trio. The 15th open format added to the editor — and the
last gameplay-graph piece the catalog needed.

Cross-references with previously-added formats:
  WQT.giverCreatureId    -> WCRT.entry.creatureId
  WQT.turninCreatureId   -> WCRT.entry.creatureId
  WQT.objective.targetId -> WCRT (kill) / WIT (collect) /
                             WOB (interact)
  WQT.rewardItem.itemId  -> WIT.entry.itemId
  WQT.prevQuestId        -> WQT.entry.questId (intra-format)
  WQT.nextQuestId        -> WQT.entry.questId

Together with WIT / WCRT / WLOT / WSPN / WOMX / WOL / WOW /
WSND, a content pack can now ship a complete RPG zone
(terrain + props + atmosphere + sounds + creatures + items
+ loot + spawns + quests) entirely in open formats with no
SQL or .dbc dependencies. 15 of 15 expected slots filled.

Format:
  • magic "WQTM", version 1, little-endian
  • per quest: questId / title / objective / description /
    minLevel..maxLevel + questLevel / requiredClass+RaceMask /
    prev+nextQuestId / giver+turninCreatureId /
    objectives[] / xpReward + moneyCopperReward /
    rewardItems[] / flags

Per-objective:
  kind (kill/collect/interact/visit/escort/cast),
  targetId, quantity

Per-reward:
  itemId, qty, pickFlags (AutoGiven / PlayerChoice)

Quest flags: Daily / Weekly / Raid / Group / AutoComplete /
              AutoAccept / Repeatable / ClassQuest / Pvp

API: WoweeQuestLoader::save / load / exists / findById;
presets makeStarter (1 simple kill quest, references the
bandit creatureId=1000), makeChain (3-quest chain with
prev/next links + AutoComplete bridge + player-choice
rewards), makeDaily (Daily+Repeatable+AutoAccept combo).

CLI added (5 flags, 500 documented total — round milestone):
  --gen-quests / --gen-quests-chain / --gen-quests-daily
  --info-wqt / --validate-wqt

Validator catches: questId=0+duplicates, level=0,
maxLevel<minLevel, empty title, no objectives without
AutoComplete (player can't finish), no rewards at all,
Daily without Repeatable (incoherent), targetId=0,
quantity=0, unknown objective kind, reward itemId=0 or qty=0.

The 3-quest chain demo exercises every major feature:
  • multiple objective kinds (visit / collect / kill)
  • prev/next chain links
  • AutoComplete dialogue-bridge quest
  • PlayerChoice reward (1 of 2 weapons)
2026-05-09 15:25:02 -07:00
Kelsi
b2b84139aa feat(pipeline): add WCRT (Wowee Creature Template) format
Novel open replacement for the AzerothCore-style
creature_template SQL table PLUS the Blizzard
CreatureTemplate / CreatureFamily / CreatureType.dbc trio.
The 14th open format added to the editor.

This is the canonical metadata side of creatures shared
across every spawn instance: HP, level range, faction,
behavior flags, NPC role bits (vendor / trainer /
quest-giver / innkeeper), base damage, equipped gear
references.

Cross-references with the previously-added formats:
  WSPN.entry.entryId    -> WCRT.entry.creatureId
  WLOT.entry.creatureId -> WCRT.entry.creatureId
  WCRT.entry.equipped*  -> WIT.entry.itemId

The 4-format set (WIT + WLOT + WSPN + WCRT) now lets a
content pack define a complete RPG zone's creature
ecosystem: what creatures are, where they spawn, what they
drop, and what gear they carry — entirely in open formats
with no SQL dependencies.

Format:
  • magic "WCRT", version 1, little-endian
  • per entry: creatureId / displayId / name / subname /
    minLevel..maxLevel / baseHealth + healthPerLevel /
    baseMana + manaPerLevel / factionId / npcFlags /
    typeId / familyId / damageMin..Max / attackSpeedMs /
    baseArmor / walkSpeed + runSpeed / gossipId /
    equippedMain + equippedOffhand + equippedRanged /
    aiFlags

Enums:
  • TypeId:   Beast / Dragon / Demon / Elemental / Giant /
              Undead / Humanoid / Critter / Mechanical
  • FamilyId: Wolf / Cat / Bear / Boar / Raptor / Hyena /
              Spider / Gorilla / Crab (for Beast types)
  • NpcFlags: Vendor / QuestGiver / Trainer / Banker /
              Innkeeper / FlightMaster / Auctioneer /
              Repair / Stable
  • Behavior: Passive / Aggressive / FleeLowHp / CallHelp /
              NoLeash

API: WoweeCreatureLoader::save / load / exists /
findById; presets makeStarter (1 innkeeper),
makeBandit (creatureId=1000 matches WSPN/WLOT bandit
references, equips WIT itemId=1001 sword), makeMerchants
(creatureIds 4001/4002/4003 match WSPN village labels).

CLI added (5 flags, 493 documented total):
  --gen-creatures / --gen-creatures-bandit / --gen-creatures-merchants
  --info-wcrt / --validate-wcrt

Validator catches: creatureId=0, duplicates, level=0,
minLevel>maxLevel, baseHealth=0, damageMin>damageMax,
attackSpeed=0, non-positive walk/runSpeed, behavior flag
contradictions (passive+aggressive), vendor with
aggressive behavior (player can't trade).
2026-05-09 15:18:44 -07:00
Kelsi
ff0aa1a3c8 feat(pipeline): add WLOT (Wowee Loot Table) format
Novel open replacement for AzerothCore-style
creature_loot_template / gameobject_loot_template SQL
tables. The 13th open format added to the editor.

Pairs naturally with the WIT item catalog from the
preceding commit: each loot drop's itemId references an
entry in a WIT file, so a content pack ships both the
item definitions and the loot tables that reference them.
The runtime composes WIT + WLOT + WSPN to drive the full
"creature dies, drops items" flow without any SQL.

Format:
  • magic "WLOT", version 1, little-endian
  • per table: creatureId / flags / dropCount /
    moneyMin..Max / itemDropCount + drops[]
  • per drop: itemId / chancePercent (float, 0..100) /
    minQty / maxQty / drop_flags

Table flags: QuestOnly, GroupOnly, Pickpocket
Drop flags:  QuestRequired, GroupRollOnly, AlwaysDrop

dropCount is the slot budget — how many distinct drops
to roll per kill. Each item drop is rolled independently
against its chancePercent (so dropCount=2 with 4 candidate
drops at varying chances gives the classic "up to 2 distinct
items per kill" behavior). Drops with the AlwaysDrop flag
bypass the slot budget — used for guaranteed quest items.

API: WoweeLootLoader::save / load / exists /
findByCreatureId; presets makeStarter (1 table, 1 drop),
makeBandit (4 candidates, dropCount=2, matches the camp
spawns from WSPN at creatureId=1000), makeBoss (6 candidates
including guaranteed quest item via AlwaysDrop and a
group-only epic at 5%).

CLI added (5 flags, 486 documented total now):
  --gen-loot / --gen-loot-bandit / --gen-loot-boss
  --info-wlot / --validate-wlot

Validator catches: creatureId=0, duplicates, chance not in
0..100, NaN chance, money min > max, minQty > maxQty,
dropCount=0 with non-empty drops list (silent dead config).

All 3 presets save / load / re-validate clean. The bandit
table's creatureId=1000 deliberately matches WSPN's
makeCamp creatureId so the open-format demo content pack
already has working cross-references.
2026-05-09 15:11:08 -07:00
Kelsi
9093975bdd feat(pipeline): add WIT (Wowee Item Template) format
Novel open replacement for Blizzard's Item.dbc +
ItemDisplayInfo.dbc + the SQL item_template tables that
AzerothCore-style servers store item definitions in. The
12th open format added to the editor.

A WIT file holds the catalog of all items in a content
pack: weapons, armor, consumables, quest items, trade
goods. Each entry pairs gameplay metadata (stats, level
reqs, flags, weapon damage / speed) with display metadata
(displayId for icon / model, quality color), so the
runtime can render inventory tooltips and equip slots
from a single load.

Format:
  • magic "WITM", version 1, little-endian
  • per item: itemId / displayId / quality / itemClass /
    itemSubClass / inventoryType / flags / requiredLevel /
    itemLevel / sellPrice / buyPrice / maxStack / durability
    / damageMin / damageMax / attackSpeedMs /
    statCount + stats[] / name / description

Enums:
  • Quality:       Poor..Heirloom (8 levels)
  • Class:         Consumable, Weapon, Armor, Quest, ... (13)
  • InventoryType: Head..Cloak..Weapon2H (18 slots)
  • Flags:        Unique, BoP, BoE, QuestItem, Conjured, ...
  • StatType:     Stamina, Strength, Intellect, Defense, ...

API: WoweeItemLoader::save / load / exists / findById;
presets makeStarter (4-item demo), makeWeapons (5 items
common -> legendary), makeArmor (6-piece mail set with
BoE flag).

CLI added (5 flags, 480 documented total now):
  --gen-items / --gen-items-weapons / --gen-items-armor
  --info-wit / --validate-wit

Validator catches: itemId=0, duplicate itemIds, weapons
with 0 damage or attackSpeed, weapons with non-weapon
slot, equippables with durability=0 or maxStack>1, sell
price >= buy price (vendor would lose money), out-of-range
quality.

All 3 presets save / load / re-validate clean. Info-table
output includes a gold/silver/copper price formatter for
hand-readability.
2026-05-09 15:04:48 -07:00
Kelsi
88d1e6229f feat(pipeline): add WSPN (Wowee Spawn Point catalog) format
Novel open replacement for AzerothCore-style scattered
creature_template / gameobject SQL spawn tables PLUS the
ADT MDDF / MODF doodad-placement chunks. The 11th open
format, and the first that covers the live world-content
side (atmosphere + sounds + spawns now form the runtime
"what fills this zone" picture).

A WSPN file holds all spawn points for a zone in a single
table, with kind discriminating creature vs game object
vs static doodad. The same format powers:
  • server runtime  — knows what NPCs / objects to spawn
  • editor          — draws spawn markers
  • renderer        — reads the doodad subset directly to
                       draw static props without going
                       through a server roundtrip

Format:
  • magic "WSPN", version 1, little-endian
  • per entry: kind / entryId / position(3f) / rotation(3f)
    / scale / flags / respawnSec / factionId /
    questIdRequired / wanderRadius / label

Flags packed: disabled (0x01), event-only (0x02),
quest-phased (0x04). Reserved bits for future per-entry
encoding extensions.

API: WoweeSpawnsLoader::save / load / exists; presets
makeStarter (1 each kind), makeCamp (4-bandit ring +
chest + 2 tents), makeVillage (6 NPCs + 2 signs + 4
corner trees).

CLI added (5 flags, 473 documented total now):
  --gen-spawns / --gen-spawns-camp / --gen-spawns-village
  --info-wspn / --validate-wspn

Validator catches: out-of-range kind, NaN/inf coords,
non-positive scale, doodad with non-zero respawn (static
prop misuse), creature with respawn=0 (won't respawn after
kill), entryId=0 (orphan reference).

All 3 presets save / load / re-validate clean. Doodad and
game-object entries explicitly set wanderRadius=0 so the
generated catalogs are noise-free.
2026-05-09 14:57:53 -07:00
Kelsi
36d63d8dd0 feat(pipeline): add WSND (Wowee Sound Catalog) format
Novel open replacement for Blizzard's SoundEntries.dbc +
SoundEntriesAdvanced.dbc. The 10th open format added to the
editor — covers the audio-metadata gap (the previous 9 cover
geometry, terrain, atmosphere, and world manifests, but no
sound metadata).

Format:
  • magic "WSND", version 1, little-endian
  • catalogName + entry count
  • per entry: soundId / kind / flags / volume /
    minDistance / maxDistance / filePath / label

Kind enum (7 categories):
  sfx, music, ambient, ui, voice, spell, combat

Flags packed (3 bits used, rest reserved):
  loop (0x01), 3d (0x02), stream (0x04)

API: WoweeSoundLoader::save / load / exists; presets
makeStarter (one entry per kind), makeAmbient (wilderness
loops + footsteps), makeTavern (fire + crowd + drink + door
+ lute).

CLI added (5 flags, 465 documented total now):
  --gen-sound-catalog <base> [name]
  --gen-sound-catalog-ambient <base> [name]
  --gen-sound-catalog-tavern <base> [name]
  --info-wsnd <base> [--json]
  --validate-wsnd <base> [--json]

Validator catches: out-of-range kind, NaN/inf volume or
distances, 3D sounds with bad min/max, duplicate sound IDs,
empty filePaths.

All 3 presets verified: save / load / validate clean
on first run. Variable-length string fields use length-
prefixed encoding with a 1 MiB sanity cap on read to
prevent corrupted-file allocation blowups.
2026-05-09 14:47:16 -07:00
Kelsi
db47f00657 feat(pipeline): add WOMX (Wowee World Map index) format
Novel open replacement for Blizzard's WDT (top-level world
definition table). The 9th open format added to the editor.

A WOMX file holds the manifest of which terrain tiles exist
within a world plus a tiny bit of map-level metadata. The
runtime consults it before attempting to load any individual
tile (so missing tiles produce a clean "no data" result
instead of a file-not-found error).

Format:
  • magic "WMPX", version 1, little-endian
  • mapName + worldType (continent/instance/battleground/arena)
  • gridSize 1..128 (typically 64 for continents)
  • defaultLightId / defaultWeatherId (atmosphere preset
    refs, 0 if none — wires into the WOL/WOW pair)
  • packed bitmap, 1 bit per tile, row-major
  • A 64x64 manifest is exactly 512 bytes of bitmap

API: WoweeWorldMapLoader::save / load / exists; presets
makeContinent (64x64 full), makeInstance (4x4 full),
makeArena (1x1 full).

CLI added (5 flags, 456 total now):
  --gen-world-map <base> [name]            (continent)
  --gen-world-map-instance <base> [name]   (4x4)
  --gen-world-map-arena <base> [name]      (1x1)
  --info-womx <base> [--json]
  --validate-womx <base> [--json]

Round-trip verified: continent + instance + arena presets
all save / load / re-validate to byte-identical state with
correct tile counts.
2026-05-09 14:38:05 -07:00
Kelsi
d537d7163e feat(pipeline): add Wowee Open Weather (.wow) zone schedule
8th open-format addition to the Wowee pipeline. Replaces
WoW's WeatherTypes.dbc / WeatherEffect logic with a single
binary file holding a list of weather states for one zone,
each tagged with intensity bounds, a probability weight,
and duration bounds. The renderer / runtime samples one
entry at a time using weighted-random selection, drives
it for a uniform-random duration in [min, max] sec, then
re-rolls.

  • Types: Clear / Rain / Snow / Storm / Sandstorm / Fog /
    Blizzard (extensible enum).

  • Binary format: magic "WOWA", version 1, name, N entries
    each storing (typeId, minIntensity, maxIntensity, weight,
    minDurationSec, maxDurationSec).

CLI:
  • --info-wow <wow-base> [--json] — inspect a WOW
  • --gen-weather-temperate — clear + rain + fog (forest)
  • --gen-weather-arctic    — snow + blizzard + fog (tundra)
  • --gen-weather-desert    — clear + sandstorm (dunes)
  • --gen-weather-stormy    — rain + storm + occasional clear

The 8th open format complementing the rest:
  M2 → WOM | WMO → WOB | WMO collision → WOC | ADT → WOT
  DBC → JsonDBC | BLP → PNG | Light.dbc → WOL | WeatherTypes.dbc → WOW

Smoke-tested all 4 presets + JSON output. Each preset reads
back identically with the expected entry count and weight
distribution.
2026-05-09 14:10:13 -07:00
Kelsi
8f16a27253 feat(pipeline): add WOL preset variants for cave/dungeon/night
Three new single-keyframe WOL presets complement the
existing 4-keyframe day/night cycle from --gen-light:

  • --gen-light-cave    — dim cool ambient (0.05, 0.05, 0.07)
                          + heavy short-range fog (15..80)
                          for cave / mine interiors
  • --gen-light-dungeon — warm torchlit ambient (0.18, 0.14,
                          0.10) + medium fog (25..200) for
                          dungeon / crypt interiors
  • --gen-light-night   — cold blue ambient (0.06, 0.07, 0.12)
                          + moonlit directional + far fog
                          (80..500) for always-night zones

Each preset emits a single-keyframe WOL since enclosed /
fixed-time scenes don't vary with time-of-day. All three
share an emitLightPreset helper so adding more presets
(e.g. --gen-light-tundra, --gen-light-volcanic) is one
line of registration + a maker function.

All four WOL outputs validate clean under --validate-wol
(1 or 4 keyframe(s) valid).
2026-05-09 14:01:26 -07:00
Kelsi
dc29f7f135 feat(pipeline): add WOL validation + time-of-day sampling
Three additions to the Wowee Open Light format that landed
last commit:

  • WoweeLightLoader::sampleAtTime(light, timeMin) returns
    the linearly-interpolated keyframe at any time-of-day,
    correctly handling wrap-around between the last keyframe
    and the first (e.g. 21:00 blends from dusk toward
    midnight by going forward through 00:00).

  • --validate-wol <wol-base> [--json] walks every keyframe
    and reports structural problems: time bounds (must be
    [0, 1440)), strict-ascending sort order, fogEnd >
    fogStart, finite color components. Exit code 0 PASS /
    1 FAIL — CI-friendly.

  • --info-wol-at <wol-base> <HH:MM|minutes> samples the
    interpolated state at a specific time of day. Useful
    for previewing what the renderer would feed in at a
    given moment, debugging keyframe gaps, or previewing
    a sub-range of the cycle.

Smoke-tested: dawn-to-midnight blend at 03:00 yields a
plausible mid-fade ambient (0.18, 0.16, 0.15) and dusk-to-
midnight wrap at 21:00 yields the symmetric (0.19, 0.145,
0.14). The default 4-keyframe day/night cycle from
makeDefaultDayNight passes --validate-wol cleanly.
2026-05-09 13:54:57 -07:00
Kelsi
d58ee0af7d feat(pipeline): add Wowee Open Light (.wol) atmosphere format
New open replacement for WoW's Light.dbc / LightParams.dbc /
LightIntBand.dbc / LightFloatBand.dbc stack — a single .wol
file holds a list of time-of-day keyframes for one zone,
each capturing the ambient + directional + fog state at that
moment. The renderer interpolates between adjacent keyframes
by time-of-day.

Binary layout:
  magic[4] = "WOLA", version (uint32),
  nameLen + name bytes,
  keyframeCount + keyframes (each 13 floats + 1 uint32 time)

Per keyframe:
  • timeOfDayMin (0..1439 = minutes since midnight)
  • ambientColor.rgb, directionalColor.rgb, directionalDir.xyz
  • fogColor.rgb, fogStart, fogEnd

CLI:
  • --gen-light <wol-base> [zoneName] — emit a starter file
    with 4-keyframe day/night cycle (midnight/dawn/noon/dusk)
    using reasonable outdoor defaults
  • --info-wol <wol-base> [--json] — inspect: zone name +
    per-keyframe time-of-day + colors + fog distances

The 7th open-format addition to the Wowee pipeline:
  M2  → WOM (model)
  WMO → WOB (building)
  WMO collision → WOC
  ADT → WOT (terrain)
  DBC → JsonDBC
  BLP → PNG
  Light.dbc family → WOL  ← new

Smoke-tested round-trip: gen → info shows correct 4 keyframes
at 00:00 / 06:00 / 12:00 / 18:00 with the canonical color
ramps. JSON output for tooling integration.
2026-05-09 13:52:07 -07:00
Kelsi
ea745005ce feat(runtime): pick up WHM/WOT/WOC sidecars from asset tree
Closes the loop on the asset_extract --emit-terrain pipeline. The
runtime terrain loader now probes for a .whm/.wot/.woc trio in the
same directory as the resolved ADT (e.g. <data>/world/maps/foo/
foo_30_30.{whm,wot,woc}) before falling back to ADTLoader.

Hits the open-format path when:
  custom_zones/<map>/<map>_X_Y.{whm,wot} exists  (zone author override)
  output/<map>/<map>_X_Y.{whm,wot} exists        (editor export)
  <data>/world/maps/<map>/<map>_X_Y.{whm,wot}    (asset extractor)
  -- otherwise falls through to ADTLoader::load(adtData)

Promotes AssetManager::resolveFile to public so callers (terrain
sidecar probe here, anything else later) can locate an extracted
file's directory without reading the bytes.

Servers/private servers continue to read .adt via manifest paths
unchanged. Runtime sidecar coverage now matches the extractor's
emit set across all five binary open formats.
2026-05-06 10:48:40 -07:00
Kelsi
e6ace7cce5 feat(extract): emit WOM and WOB side-files (M2/WMO → open formats)
Extends asset_extract with two more open-format emitters:
  --emit-wom  foo.m2 (+ foo00.skin) → foo.wom
  --emit-wob  foo.wmo (+ foo_NNN.wmo groups) → foo.wob
  --emit-open now also turns these on

Originals are preserved so private servers still load .m2/.wmo
through the manifest path; the wowee runtime/editor pick up the
.wom/.wob next to them via the existing open-format search rules.

Implementation:
- New WoweeModelLoader::fromM2Bytes(m2Data, skinData) shares the
  conversion body with fromM2(path, am) via a static helper
  (convertM2ToWom). Lets the extractor convert without standing
  up an AssetManager.
- fromM2(path, am) moved to a separate translation unit
  (wowee_model_fromm2.cpp) so asset_extract doesn't have to
  link the AssetManager dependency.
- WoweeBuildingLoader::fromWMO already takes a WMOModel directly,
  so emitWobFromWmo just needs to read root + group files and
  call save().
- Group sub-files (<base>_NNN.wmo) are skipped during the walk
  since they're merged into the root WMO.
2026-05-06 10:32:17 -07:00
Kelsi
500ad2e711 fix(camera): NaN/range guards on Camera setters
setPosition/setRotation/setAspectRatio/setFov now reject:
- NaN/inf inputs (would produce NaN view/proj matrix → frozen GPU
  on some drivers, garbage frustum culling everywhere)
- aspectRatio <= 0 (degenerate perspective)
- fov <= 0 or >= 180 (degenerate perspective)

Camera is constructed and set from many code paths; pushing the
guards into the setters means none of them need to remember.
2026-05-06 08:57:31 -07:00
Kelsi
db068d480b feat(wob): tryLoadByGamePath helper, used by editor + terrain_manager
Mirrors the WOM tryLoadByGamePath API: probes custom_zones/buildings/ +
output/buildings/ by default, with optional extraPrefixes (e.g. per-zone
output/<map>/buildings/) checked first. Both the editor and the main
game's terrain_manager now use the helper, removing duplicate inline
lookup loops in two more places.
2026-05-06 04:10:12 -07:00
Kelsi
f36309a96f feat(wom): tryLoadByGamePath accepts extraPrefixes for per-zone search roots
The shared helper only probed custom_zones/models/ + output/models/, but
the editor's exportZone writes to output/<map>/models/. Added an
extraPrefixes parameter that's tried before the defaults — main game's
terrain_manager now passes ['output/<map>/models/', 'custom_zones/<map>/
models/'] so per-zone WOM exports override globals. Also removes the
last duplicate WOM-loading code from terrain_manager.
2026-05-06 04:07:16 -07:00