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).
This commit is contained in:
Kelsi 2026-05-09 16:47:04 -07:00
parent 019104536f
commit e66601c208
8 changed files with 926 additions and 0 deletions

View file

@ -0,0 +1,147 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace wowee {
namespace pipeline {
// Wowee Open Character Classes/Races catalog (.wchc) —
// novel replacement for Blizzard's CharClasses.dbc +
// CharRaces.dbc + CharStartOutfit.dbc trio. The 27th open
// format added to the editor.
//
// Defines every player class, race, and the starting outfit
// (gear loadout) for each class+race+gender combination.
// One file holds three flat arrays: classes / races /
// outfits.
//
// 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
// WCHC.class.canTrainProfessions → WSKL.entry.skillId
// (bitset by category)
//
// Binary layout (little-endian):
// magic[4] = "WCHC"
// version (uint32) = current 1
// nameLen + name (catalog label)
// classCount (uint32) + classes[]
// raceCount (uint32) + races[]
// outfitCount (uint32) + outfits[]
struct WoweeChars {
enum PowerType : uint8_t {
Mana = 0,
Rage = 1,
Focus = 2,
Energy = 3,
RunicPower = 4,
Runes = 6, // DK only
};
enum FactionAvailability : uint8_t {
AvailableAlliance = 0x01,
AvailableHorde = 0x02,
};
enum RaceFaction : uint8_t {
Alliance = 0,
Horde = 1,
Neutral = 2, // Pandaren start neutral
};
enum Gender : uint8_t {
Male = 0,
Female = 1,
};
struct Class {
uint32_t classId = 0;
std::string name;
std::string iconPath;
uint8_t powerType = Mana;
uint8_t displayPower = Mana; // can differ from powerType (druid)
uint32_t baseHealth = 50;
uint16_t baseHealthPerLevel = 12;
uint32_t basePower = 100;
uint16_t basePowerPerLevel = 5;
uint8_t factionAvailability =
AvailableAlliance | AvailableHorde;
};
struct Race {
uint32_t raceId = 0;
std::string name;
std::string iconPath;
uint8_t factionId = Alliance;
uint32_t maleDisplayId = 0;
uint32_t femaleDisplayId = 0;
uint16_t baseStrength = 20;
uint16_t baseAgility = 20;
uint16_t baseStamina = 20;
uint16_t baseIntellect = 20;
uint16_t baseSpirit = 20;
uint32_t startingMapId = 0;
uint32_t startingZoneAreaId = 0;
uint32_t defaultLanguageSpellId = 0; // 0 = none
uint32_t mountSpellId = 0; // racial mount spell
};
struct OutfitItem {
uint32_t itemId = 0;
uint8_t displaySlot = 0; // matches WIT.inventoryType
};
struct Outfit {
uint32_t classId = 0;
uint32_t raceId = 0;
uint8_t gender = Male;
std::vector<OutfitItem> items;
};
std::string name;
std::vector<Class> classes;
std::vector<Race> races;
std::vector<Outfit> outfits;
bool isValid() const { return !classes.empty() || !races.empty(); }
const Class* findClass(uint32_t classId) const;
const Race* findRace(uint32_t raceId) const;
// First outfit matching the (class, race, gender) triple, or nullptr.
const Outfit* findOutfit(uint32_t classId, uint32_t raceId,
uint8_t gender) const;
static const char* powerTypeName(uint8_t p);
static const char* raceFactionName(uint8_t f);
};
class WoweeCharsLoader {
public:
static bool save(const WoweeChars& cat,
const std::string& basePath);
static WoweeChars load(const std::string& basePath);
static bool exists(const std::string& basePath);
// Preset emitters used by --gen-chars* variants.
//
// makeStarter — 2 classes (Warrior + Mage) + 2 races
// (Human Alliance + Orc Horde) + 4 outfits
// (2 classes × 2 races, male-only).
// makeAlliance — full Alliance faction: 4 classes + 4
// races + 8 outfits.
// makeAllRaces — 8 classic playable races (Human / Dwarf
// / NightElf / Gnome on Alliance side;
// Orc / Undead / Tauren / Troll on Horde)
// plus 9 classes (no DK).
static WoweeChars makeStarter(const std::string& catalogName);
static WoweeChars makeAlliance(const std::string& catalogName);
static WoweeChars makeAllRaces(const std::string& catalogName);
};
} // namespace pipeline
} // namespace wowee