mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 02:53:51 +00:00
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.
This commit is contained in:
parent
89871c171c
commit
d2ca3ea22b
8 changed files with 698 additions and 0 deletions
120
include/pipeline/wowee_trainers.hpp
Normal file
120
include/pipeline/wowee_trainers.hpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
// Wowee Open Trainer / Vendor catalog (.wtrn) — novel
|
||||
// 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 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.
|
||||
//
|
||||
// Cross-references with previously-added formats:
|
||||
// WTRN.entry.npcId → WCRT.entry.creatureId
|
||||
// WTRN.spell.spellId → WSPL.entry.spellId
|
||||
// WTRN.spell.requiredSkillId → WSKL.entry.skillId
|
||||
// WTRN.item.itemId → WIT.entry.itemId
|
||||
//
|
||||
// Binary layout (little-endian):
|
||||
// magic[4] = "WTRN"
|
||||
// version (uint32) = current 1
|
||||
// nameLen + name (catalog label)
|
||||
// entryCount (uint32)
|
||||
// entries (each):
|
||||
// npcId (uint32)
|
||||
// kindMask (uint8) + pad[3]
|
||||
// greetingLen + greeting
|
||||
// spellCount (uint16) + itemCount (uint16)
|
||||
// spells (spellCount × {
|
||||
// spellId (uint32)
|
||||
// moneyCostCopper (uint32)
|
||||
// requiredSkillId (uint32)
|
||||
// requiredSkillRank (uint16)
|
||||
// requiredLevel (uint16)
|
||||
// })
|
||||
// items (itemCount × {
|
||||
// itemId (uint32)
|
||||
// stockCount (uint32) -- 0xFFFFFFFF = unlimited
|
||||
// restockSec (uint32)
|
||||
// extendedCost (uint32)
|
||||
// moneyCostCopper (uint32) -- 0 = use WIT.buyPrice
|
||||
// })
|
||||
struct WoweeTrainer {
|
||||
enum KindMask : uint8_t {
|
||||
Trainer = 0x01,
|
||||
Vendor = 0x02,
|
||||
};
|
||||
|
||||
static constexpr uint32_t kUnlimitedStock = 0xFFFFFFFFu;
|
||||
|
||||
struct SpellOffer {
|
||||
uint32_t spellId = 0;
|
||||
uint32_t moneyCostCopper = 0;
|
||||
uint32_t requiredSkillId = 0; // 0 = no skill prerequisite
|
||||
uint16_t requiredSkillRank = 0;
|
||||
uint16_t requiredLevel = 1;
|
||||
};
|
||||
|
||||
struct ItemOffer {
|
||||
uint32_t itemId = 0;
|
||||
uint32_t stockCount = kUnlimitedStock;
|
||||
uint32_t restockSec = 0;
|
||||
uint32_t extendedCost = 0; // 0 = copper-only
|
||||
uint32_t moneyCostCopper = 0; // 0 = inherit from WIT
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
uint32_t npcId = 0;
|
||||
uint8_t kindMask = 0;
|
||||
std::string greeting;
|
||||
std::vector<SpellOffer> spells;
|
||||
std::vector<ItemOffer> items;
|
||||
};
|
||||
|
||||
std::string name;
|
||||
std::vector<Entry> entries;
|
||||
|
||||
bool isValid() const { return !entries.empty(); }
|
||||
|
||||
// Lookup by npcId — nullptr if not present.
|
||||
const Entry* findByNpc(uint32_t npcId) const;
|
||||
|
||||
// Decode the kindMask into a short string (e.g.
|
||||
// "trainer+vendor" or just "vendor").
|
||||
static std::string kindMaskName(uint8_t k);
|
||||
};
|
||||
|
||||
class WoweeTrainerLoader {
|
||||
public:
|
||||
static bool save(const WoweeTrainer& cat,
|
||||
const std::string& basePath);
|
||||
static WoweeTrainer load(const std::string& basePath);
|
||||
static bool exists(const std::string& basePath);
|
||||
|
||||
// Preset emitters used by --gen-trainers* variants.
|
||||
//
|
||||
// makeStarter — 1 NPC (Bartleby innkeeper, npcId=4001)
|
||||
// acting as both vendor + trainer: sells
|
||||
// 3 starter items + teaches First Aid.
|
||||
// makeMageTrainer — npcId=4003 (alchemist) becomes a
|
||||
// mage trainer offering Frostbolt /
|
||||
// Fireball / Arcane Intellect / Blink
|
||||
// at appropriate ranks.
|
||||
// makeWeaponVendor — npcId=4002 (smith) sells 5 weapons
|
||||
// across the WIT weapon catalog.
|
||||
static WoweeTrainer makeStarter(const std::string& catalogName);
|
||||
static WoweeTrainer makeMageTrainer(const std::string& catalogName);
|
||||
static WoweeTrainer makeWeaponVendor(const std::string& catalogName);
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue