Kelsidavis-WoWee/include/pipeline/wowee_buff_book.hpp
Kelsi abf264abfe feat(editor): add WBAB (Buff & Aura Book) — 102nd open format
Novel replacement for the implicit rank-chain
relationships that vanilla WoW encoded by burying
nextRank/prevRank pointers inside Spell.dbc with no
explicit graph structure. Each WBAB entry is one long-
duration class buff at one specific rank, with explicit
edges to adjacent ranks via previousRankId and
nextRankId fields. The graph-shaped data is novel among
the 100+ catalog set: most catalogs have flat rows; WBAB
is genuinely a graph where rows are nodes and the rank
fields are edges.

Both directions are stored explicitly so the spellbook
UI's "upgrade to next rank" button can traverse without
scanning the full table. Helper methods walkChainBack-
ToRoot() returns the full chain root->tip for the rank-
picker widget; findChainTip() returns the highest rank
for auto-cast logic.

Three preset emitters demonstrating the pattern:
makeMage (Arcane Intellect ranks 1-4 with chain edges),
makeDruid (Mark of the Wild ranks 1-5 with chain edges),
makeRaidMax (6 max-rank standalone raid buffs — one per
buffing class — with no chain edges to show the
standalone case).

Validator catches several rank-chain-specific bugs:
self-referencing edges (entry.next == entry.id would
create a 1-element cycle), missing referenced entries
(next/prev pointing to non-existent ids), and most
importantly back-edge symmetry — if A.nextRankId=B then
B.previousRankId MUST equal A.buffId or the spellbook
upgrade traversal will derail. Symmetric back-edge check
is unique to graph-shaped catalogs.

Also fixed a crash in --catalog-find where the recursive
directory iterator threw on permission-denied subdirs
(common when walking /tmp). Now uses the
skip_permission_denied directory_options + per-step
error_code clearing for defensive resumption.

Format count 101 -> 102. CLI flag count 1134 -> 1139.
2026-05-10 01:13:42 -07:00

154 lines
5.6 KiB
C++

#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace wowee {
namespace pipeline {
// Wowee Open Buff & Aura Book catalog (.wbab) — novel
// replacement for the implicit rank-chain relationships
// that vanilla WoW encoded by burying nextRank/prevRank
// pointers inside Spell.dbc. Each entry is one long-
// duration class buff (Mark of the Wild, Arcane
// Intellect, Power Word: Fortitude, Battle Shout, etc.)
// at one specific rank, with explicit edges to the
// previous and next ranks via previousRankId /
// nextRankId.
//
// The rank-chain pattern is novel among the catalog set:
// most catalogs have flat entries (one row per concept);
// WBAB is a graph where rows are nodes connected by edge
// fields. Both directions are stored explicitly so the
// rank-step UI ("upgrade to next rank" button) can
// traverse without scanning the full table.
//
// Cross-references with previously-added formats:
// WSPL: spellId references the WSPL spell catalog (the
// actual spell that gets cast).
// WCHC: castClassMask uses the WCHC class-bit
// convention.
// WBAB: previousRankId / nextRankId reference OTHER
// entries in the same WBAB catalog — internal
// self-reference for the rank chain. Validator
// can check the back-edges (if A.next=B then
// B.prev should = A).
//
// Binary layout (little-endian):
// magic[4] = "WBAB"
// version (uint32) = current 1
// nameLen + name (catalog label)
// entryCount (uint32)
// entries (each):
// buffId (uint32)
// nameLen + name
// descLen + description
// spellId (uint32)
// castClassMask (uint32)
// targetTypeMask (uint8) — Self / Party / Raid /
// Friendly bitmask
// statBonusKind (uint8) — Stamina / Intellect /
// Spirit / AllStats /
// Armor / SpellPower /
// AttackPower / Crit /
// Haste / Mastery
// rank (uint8) — 1-based rank number
// maxStackCount (uint8) — typically 1
// statBonusAmount (int32) — signed magnitude
// (negative = debuff)
// duration (uint32) — seconds (0 = until
// cancel / log out)
// previousRankId (uint32) — 0 if rank 1
// nextRankId (uint32) — 0 if max rank
// iconColorRGBA (uint32)
struct WoweeBuffBook {
enum TargetTypeBit : uint8_t {
TargetSelf = 0x01,
TargetParty = 0x02,
TargetRaid = 0x04,
TargetFriendly = 0x08, // any friendly target
// including outside party
};
enum StatBonusKind : uint8_t {
Stamina = 0,
Intellect = 1,
Spirit = 2,
AllStats = 3,
Armor = 4,
SpellPower = 5,
AttackPower = 6,
CritRating = 7,
HasteRating = 8,
ManaRegen = 9,
Other = 255, // not statted (e.g.
// Trueshot Aura is %DPS,
// not a flat stat)
};
struct Entry {
uint32_t buffId = 0;
std::string name;
std::string description;
uint32_t spellId = 0;
uint32_t castClassMask = 0;
uint8_t targetTypeMask = TargetSelf | TargetParty;
uint8_t statBonusKind = Other;
uint8_t rank = 1;
uint8_t maxStackCount = 1;
int32_t statBonusAmount = 0;
uint32_t duration = 0;
uint32_t previousRankId = 0;
uint32_t nextRankId = 0;
uint32_t iconColorRGBA = 0xFFFFFFFFu;
};
std::string name;
std::vector<Entry> entries;
bool isValid() const { return !entries.empty(); }
const Entry* findById(uint32_t buffId) const;
// Walks the rank chain from buffId all the way to
// rank 1, returning entries from first to last. Used
// by the spellbook UI's "rank picker" widget which
// shows all ranks the player can currently train.
std::vector<const Entry*>
walkChainBackToRoot(uint32_t buffId) const;
// Returns the highest rank in the chain starting from
// buffId (the entry with nextRankId=0 reachable from
// here). Used by the auto-cast logic to always apply
// the highest rank the caster knows.
const Entry* findChainTip(uint32_t buffId) const;
};
class WoweeBuffBookLoader {
public:
static bool save(const WoweeBuffBook& cat,
const std::string& basePath);
static WoweeBuffBook load(const std::string& basePath);
static bool exists(const std::string& basePath);
// Preset emitters used by --gen-bab* variants.
//
// makeMage — 4 entries: Arcane Intellect ranks
// 1-4 with explicit rank chain.
// makeDruid — 5 entries: Mark of the Wild ranks
// 1-5 with explicit rank chain.
// makeRaidMax — 6 entries: one max-rank buff per
// buffing class (Mark of the Wild
// R7, Power Word: Fortitude R8,
// Arcane Intellect R6, Blessing
// of Kings R1, Battle Shout R9,
// Trueshot Aura R3) — no chain
// edges since each is standalone.
static WoweeBuffBook makeMage(const std::string& catalogName);
static WoweeBuffBook makeDruid(const std::string& catalogName);
static WoweeBuffBook makeRaidMax(const std::string& catalogName);
};
} // namespace pipeline
} // namespace wowee