feat(editor): add WSCS (Skill Cost) open catalog format

Open replacement for Blizzard's SkillCostsData.dbc plus the
per-rank training cost tables. Defines the tiered progression of
trainable skills: each rank unlocks a skill range, requires a
minimum character level, and costs a fixed amount of gold to
learn.

The canonical 6-tier profession progression captured by the
default preset:
  Apprentice    skill 0-75     lvl 5    1s
  Journeyman    skill 50-150   lvl 10   5s
  Expert        skill 125-225  lvl 20   1g
  Artisan       skill 200-300  lvl 35   5g
  Master        skill 275-375  lvl 50   10g
  Grand Master  skill 350-450  lvl 65   25g

Same shape applies to weapon skills (free, level-gated, capped at
5x char level) and riding skills (canonical Vanilla / TBC / WotLK
gold costs from 90g Apprentice through 5000g Artisan flying down
to 1000g Cold Weather Flying).

Five costKind values cover the full training-skill space
(Profession / WeaponSkill / RidingSkill / ClassSkill / Misc).
Each entry's copperCost stores the cost in copper (1g = 10000c)
which the info renderer pretty-prints as "25g 0s 0c".

Cross-references back to WSKL — skill entries reference costId
here for the tiered training schedule. nextTrainable(currentSkill,
characterLevel) is the engine helper that returns the lowest-rank
tier a character qualifies for and hasn't capped yet — used by
trainer NPCs to populate their offered-skill list.

Three preset emitters: --gen-scs (6 profession tiers), --gen-scs-
weapon (5 weapon skill tiers), --gen-scs-riding (5 riding tiers
with canonical gold costs).

Validation enforces id+name presence, costKind 0..4, no duplicate
ids, min<max range; warns on:
  - requiredLevel > 80 (unreachable at WotLK cap)
  - RidingSkill with requiredLevel < 20 (Apprentice canonically
    unlocks at 20)
  - Profession kind with copperCost=0 (every standard tier costs
    at least a copper — usually a config bug)

Wired through the cross-format table; WSCS appears automatically
in all 15 cross-format utilities. Format count 84 -> 85; CLI
flag count 1010 -> 1015.
This commit is contained in:
Kelsi 2026-05-09 23:04:02 -07:00
parent a7057393eb
commit b10bc2be5d
10 changed files with 689 additions and 0 deletions

View file

@ -0,0 +1,120 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace wowee {
namespace pipeline {
// Wowee Open Skill Cost catalog (.wscs) — novel
// replacement for Blizzard's SkillCostsData.dbc plus the
// per-rank training cost tables. Defines the tiered
// progression of trainable skills: each rank unlocks a
// skill range, requires a minimum character level, and
// costs a fixed amount of gold to learn.
//
// The canonical 6-tier profession progression:
// Apprentice skill 0-75 lvl 5 1s
// Journeyman skill 50-150 lvl 10 5s
// Expert skill 125-225 lvl 20 1g
// Artisan skill 200-300 lvl 35 5g
// Master skill 275-375 lvl 50 10g
// Grand Master skill 350-450 lvl 65 25g
//
// Same shape applies to weapon skills (with different
// caps), riding skills (with level gates per mount tier),
// and class secondary skills (Lockpicking for Rogues,
// First Aid for everyone).
//
// Cross-references with previously-added formats:
// WSKL: skill entries reference costId here for the
// tiered training schedule.
//
// Binary layout (little-endian):
// magic[4] = "WSCS"
// version (uint32) = current 1
// nameLen + name (catalog label)
// entryCount (uint32)
// entries (each):
// costId (uint32)
// nameLen + name
// descLen + description
// skillRankIndex (uint32)
// minSkillToLearn (uint16) / maxSkillUnlocked (uint16)
// requiredLevel (uint8) / costKind (uint8) / pad[2]
// copperCost (uint32)
// iconColorRGBA (uint32)
struct WoweeSkillCost {
enum CostKind : uint8_t {
Profession = 0, // primary/secondary profession rank
WeaponSkill = 1, // weapon skill cap (Sword / Mace / Axe / etc)
RidingSkill = 2, // mount riding skill (60% / 100% / 150% / 280% / Cold)
ClassSkill = 3, // class-specific (Lockpicking / Poisons)
Misc = 4, // catch-all
};
struct Entry {
uint32_t costId = 0;
std::string name;
std::string description;
uint32_t skillRankIndex = 0;
uint16_t minSkillToLearn = 0;
uint16_t maxSkillUnlocked = 75;
uint8_t requiredLevel = 1;
uint8_t costKind = Profession;
uint8_t pad0 = 0;
uint8_t pad1 = 0;
uint32_t copperCost = 0; // 1g = 10000c
uint32_t iconColorRGBA = 0xFFFFFFFFu;
};
std::string name;
std::vector<Entry> entries;
bool isValid() const { return !entries.empty(); }
const Entry* findById(uint32_t costId) const;
// Returns the entry that would be next-trainable for a
// character with the given current skill points and
// character level — i.e. the lowest-rank entry the
// character qualifies for and hasn't already maxed out.
// Returns nullptr if every entry is either capped or
// gated by level.
const Entry* nextTrainable(uint16_t currentSkill,
uint8_t characterLevel) const;
static const char* costKindName(uint8_t k);
};
class WoweeSkillCostLoader {
public:
static bool save(const WoweeSkillCost& cat,
const std::string& basePath);
static WoweeSkillCost load(const std::string& basePath);
static bool exists(const std::string& basePath);
// Preset emitters used by --gen-scs* variants.
//
// makeProfession — 6 canonical profession tiers
// (Apprentice through Grand Master)
// with the standard skill ranges and
// gold costs from a Vanilla / TBC /
// WotLK-era server.
// makeWeapon — 5 weapon skill tiers (Beginner /
// Trained / Skilled / Expert /
// Master) for free-to-train weapon
// skills capped at 5x character lvl.
// makeRiding — 5 riding skill tiers (Apprentice
// 60% / Journeyman 100% / Expert
// 150% / Artisan 280% / Cold Weather
// Flying) with the canonical Vanilla
// /TBC / WotLK gold costs.
static WoweeSkillCost makeProfession(const std::string& catalogName);
static WoweeSkillCost makeWeapon(const std::string& catalogName);
static WoweeSkillCost makeRiding(const std::string& catalogName);
};
} // namespace pipeline
} // namespace wowee