Kelsidavis-WoWee/include/pipeline/wowee_talents.hpp
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

115 lines
3.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace wowee {
namespace pipeline {
// Wowee Open Talent catalog (.wtal) — novel 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: a per-class set
// of named tabs (Arms / Fury / Protection for warrior, Fire
// / Frost / Arcane for mage, etc.), each with up to N
// 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)
//
// Binary layout (little-endian):
// magic[4] = "WTAL"
// version (uint32) = current 1
// nameLen + name (catalog label)
// treeCount (uint32)
// trees (each):
// treeId (uint32)
// nameLen + name
// iconLen + iconPath
// requiredClassMask (uint32)
// talentCount (uint16) + pad[2]
// talents (talentCount × {
// talentId (uint32)
// row (uint8) / col (uint8) / maxRank (uint8) / pad[1]
// prereqTalentId (uint32) / prereqRank (uint8) / pad[3]
// rankSpellIds[5] (uint32 each, zero-padded for unused ranks)
// })
struct WoweeTalent {
static constexpr int kMaxRanks = 5;
// Class IDs follow Blizzard's CharClasses.dbc canonical
// bit positions (bit N corresponds to classId N+1):
// 1=Warrior 2=Paladin 3=Hunter 4=Rogue 5=Priest
// 6=DK 7=Shaman 8=Mage 9=Warlock 10=- 11=Druid
enum ClassMask : uint32_t {
ClassWarrior = 1u << 0,
ClassPaladin = 1u << 1,
ClassHunter = 1u << 2,
ClassRogue = 1u << 3,
ClassPriest = 1u << 4,
ClassDK = 1u << 5,
ClassShaman = 1u << 6,
ClassMage = 1u << 7,
ClassWarlock = 1u << 8,
ClassDruid = 1u << 10,
};
struct Talent {
uint32_t talentId = 0;
uint8_t row = 0;
uint8_t col = 0;
uint8_t maxRank = 1;
uint32_t prereqTalentId = 0;
uint8_t prereqRank = 0;
uint32_t rankSpellIds[kMaxRanks] = {0, 0, 0, 0, 0};
};
struct Tree {
uint32_t treeId = 0;
std::string name;
std::string iconPath;
uint32_t requiredClassMask = 0;
std::vector<Talent> talents;
};
std::string name;
std::vector<Tree> trees;
bool isValid() const { return !trees.empty(); }
const Tree* findTree(uint32_t treeId) const;
// Talent lookup is global across all trees (talentIds are
// expected to be unique within a single .wtal catalog).
const Talent* findTalent(uint32_t talentId) const;
};
class WoweeTalentLoader {
public:
static bool save(const WoweeTalent& cat,
const std::string& basePath);
static WoweeTalent load(const std::string& basePath);
static bool exists(const std::string& basePath);
// Preset emitters used by --gen-talents* variants.
//
// makeStarter — 1 small tree (3 talents, no prereqs).
// makeWarrior — 3 trees (Arms / Fury / Protection) each
// with a handful of talents, prerequisite
// chains, and rankSpellIds wired to WSPL
// warrior preset spells where applicable.
// makeMage — 3 trees (Arcane / Fire / Frost) with
// links to WSPL mage preset spell IDs.
static WoweeTalent makeStarter(const std::string& catalogName);
static WoweeTalent makeWarrior(const std::string& catalogName);
static WoweeTalent makeMage(const std::string& catalogName);
};
} // namespace pipeline
} // namespace wowee