Kelsidavis-WoWee/include/pipeline/wowee_achievements.hpp
Kelsi e5eaf13866 feat(pipeline): add WACH (Wowee Achievement Catalog) format
Novel open replacement for Blizzard's Achievement.dbc +
AchievementCriteria.dbc + AchievementCategory.dbc + the
AzerothCore-style character_achievement /
character_achievement_progress SQL tables. The 21st open
format added to the editor.

Each achievement carries display metadata (name, description,
icon, points, faction restriction) plus a list of criteria
the player must satisfy. Criteria mirror the WQT objective
model (kind + targetId + quantity), so the runtime can
reuse the same progress-tracking machinery for both quests
and achievements.

Cross-references with previously-added formats — every
criterion kind has a real format target:
  WACH.criteria.targetId (kind=KillCreature)    -> WCRT.creatureId
  WACH.criteria.targetId (kind=CompleteQuest)   -> WQT.questId
  WACH.criteria.targetId (kind=LootItem)        -> WIT.itemId
  WACH.criteria.targetId (kind=CastSpell)       -> WSPL.spellId
  WACH.criteria.targetId (kind=ReachSkillLevel) -> WSKL.skillId
  WACH.criteria.targetId (kind=EarnReputation)  -> WFAC.factionId
  WACH.criteria.targetId (kind=CompleteAchievement) -> WACH.achievementId
                                                       (meta-achievements)

Format:
  • magic "WACH", version 1, little-endian
  • per achievement: id / categoryId / name / description /
    iconPath / titleReward / points / minLevel / faction /
    flags / criteria[]
  • per criterion: criteriaId / kind / targetId / quantity /
    description

Enums:
  • CriteriaKind (9): KillCreature / CompleteQuest / LootItem /
                      ReachLevel / EarnReputation / CastSpell /
                      ReachSkillLevel / VisitArea /
                      CompleteAchievement
  • Faction:    Both / Alliance / Horde
  • Flags:      HiddenUntilEarned / ServerFirst / RealmFirst /
                 Tracking / Counter / Account

API: WoweeAchievementLoader::save / load / exists /
findById; presets makeStarter (3 simple kill/quest/level
demos), makeBandit (3 with WCRT/WGOT/WQT cross-refs),
makeMeta (3 base + 1 meta-achievement granting "the
Versatile" title, exercising CompleteAchievement criterion
kind that lets achievements depend on other achievements).

CLI added (5 flags, 542 documented total now):
  --gen-achievements / --gen-achievements-bandit / --gen-achievements-meta
  --info-wach / --validate-wach

Validator catches: achievementId=0 + duplicates, empty name,
faction out of range, no criteria (achievement can never
be earned), criterion quantity=0, unknown criterion kind,
targetId=0 on criterion kinds that need a real resource
reference (everything except ReachLevel which uses the
quantity field for the level number).

The bandit preset's cross-references close the gameplay
graph end-to-end: kill 50 creatureId=1000 (matches WCRT/
WSPN/WLOT bandit), loot objectId=2000 (matches WGOT bandit
strongbox), complete questId=1 (matches WQT Bandit Trouble).
The meta preset closes a separate loop: 3 sub-achievements
covering Mining (skillId=186), Lockpicking (skillId=633),
and Frostbolt cast count (spellId=116) — each pointing at
a real WSKL/WSPL entry that already exists in the demo
content stack.
2026-05-09 16:04:30 -07:00

140 lines
4.9 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 Achievement Catalog (.wach) — novel replacement
// for Blizzard's Achievement.dbc + AchievementCriteria.dbc +
// AchievementCategory.dbc + the AzerothCore-style
// character_achievement / character_achievement_progress
// SQL tables. The 21st open format added to the editor.
//
// Each achievement carries display metadata (name, description,
// icon, points, faction restriction) plus a list of criteria
// the player must satisfy. Criteria mirror the WQT objective
// model (kind + targetId + quantity), which means the runtime
// can reuse the same progress-tracking machinery for both.
//
// Cross-references with previously-added formats:
// WACH.criteria.targetId (kind=KillCreature) → WCRT.creatureId
// WACH.criteria.targetId (kind=CompleteQuest) → WQT.questId
// WACH.criteria.targetId (kind=LootItem) → WIT.itemId
// WACH.criteria.targetId (kind=CastSpell) → WSPL.spellId
// WACH.criteria.targetId (kind=ReachSkillLevel) → WSKL.skillId
// WACH.criteria.targetId (kind=EarnReputation) → WFAC.factionId
// WACH.entry.categoryId → WACH.entry.achievementId (for header
// rows; achievements can be parented
// to other achievements as headers)
//
// Binary layout (little-endian):
// magic[4] = "WACH"
// version (uint32) = current 1
// nameLen + name (catalog label)
// entryCount (uint32)
// entries (each):
// achievementId (uint32)
// categoryId (uint32)
// nameLen + name
// descLen + description
// iconLen + iconPath
// titleLen + titleReward
// points (uint32)
// minLevel (uint16) / faction (uint8) / criteriaCount (uint8)
// flags (uint32)
// criteria (criteriaCount × {
// criteriaId (uint32)
// kind (uint8) + pad[3]
// targetId (uint32)
// quantity (uint32)
// descLen + description
// })
struct WoweeAchievement {
enum CriteriaKind : uint8_t {
KillCreature = 0,
CompleteQuest = 1,
LootItem = 2,
ReachLevel = 3,
EarnReputation = 4,
CastSpell = 5,
ReachSkillLevel = 6,
VisitArea = 7,
CompleteAchievement = 8, // meta-achievements
};
enum Faction : uint8_t {
FactionBoth = 0,
FactionAlliance = 1,
FactionHorde = 2,
};
enum Flags : uint32_t {
HiddenUntilEarned = 0x01, // not shown in panel until completed
ServerFirst = 0x02, // first-on-server rewards a global tag
RealmFirst = 0x04,
Tracking = 0x08, // shows progress UI in the panel
Counter = 0x10, // counts up forever (Pet Battles wins)
Account = 0x20, // account-wide, not per-character
};
struct Criterion {
uint32_t criteriaId = 0;
uint8_t kind = KillCreature;
uint32_t targetId = 0;
uint32_t quantity = 1;
std::string description;
};
struct Entry {
uint32_t achievementId = 0;
uint32_t categoryId = 0; // 0 = top-level
std::string name;
std::string description;
std::string iconPath;
std::string titleReward; // empty = no title
uint32_t points = 10;
uint16_t minLevel = 1;
uint8_t faction = FactionBoth;
uint32_t flags = 0;
std::vector<Criterion> criteria;
};
std::string name;
std::vector<Entry> entries;
bool isValid() const { return !entries.empty(); }
// Lookup by achievementId — nullptr if not present.
const Entry* findById(uint32_t achievementId) const;
static const char* criteriaKindName(uint8_t k);
static const char* factionName(uint8_t f);
};
class WoweeAchievementLoader {
public:
static bool save(const WoweeAchievement& cat,
const std::string& basePath);
static WoweeAchievement load(const std::string& basePath);
static bool exists(const std::string& basePath);
// Preset emitters used by --gen-achievements* variants.
//
// makeStarter — 3 demo achievements covering kill / quest
// completion / level reached criteria.
// makeBandit — bandit-themed: "Slay 50 Defias Bandits"
// + "Loot the Bandit Strongbox" + "Complete
// Bandit Trouble" — all referencing the
// WCRT/WGOT/WQT/WIT cross-referenced demo IDs.
// makeMeta — 3 base achievements + 1 meta-achievement
// that requires completing the others.
static WoweeAchievement makeStarter(const std::string& catalogName);
static WoweeAchievement makeBandit(const std::string& catalogName);
static WoweeAchievement makeMeta(const std::string& catalogName);
};
} // namespace pipeline
} // namespace wowee