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

147 lines
4.7 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 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