Kelsidavis-WoWee/include/pipeline/wowee_quests.hpp
Kelsi 02ae17740e feat(pipeline): add WQT (Wowee Quest Template) format
Novel open replacement for AzerothCore-style quest_template
SQL tables PLUS the Blizzard Quest.dbc / QuestObjective.dbc
trio. The 15th open format added to the editor — and the
last gameplay-graph piece the catalog needed.

Cross-references with previously-added formats:
  WQT.giverCreatureId    -> WCRT.entry.creatureId
  WQT.turninCreatureId   -> WCRT.entry.creatureId
  WQT.objective.targetId -> WCRT (kill) / WIT (collect) /
                             WOB (interact)
  WQT.rewardItem.itemId  -> WIT.entry.itemId
  WQT.prevQuestId        -> WQT.entry.questId (intra-format)
  WQT.nextQuestId        -> WQT.entry.questId

Together with WIT / WCRT / WLOT / WSPN / WOMX / WOL / WOW /
WSND, a content pack can now ship a complete RPG zone
(terrain + props + atmosphere + sounds + creatures + items
+ loot + spawns + quests) entirely in open formats with no
SQL or .dbc dependencies. 15 of 15 expected slots filled.

Format:
  • magic "WQTM", version 1, little-endian
  • per quest: questId / title / objective / description /
    minLevel..maxLevel + questLevel / requiredClass+RaceMask /
    prev+nextQuestId / giver+turninCreatureId /
    objectives[] / xpReward + moneyCopperReward /
    rewardItems[] / flags

Per-objective:
  kind (kill/collect/interact/visit/escort/cast),
  targetId, quantity

Per-reward:
  itemId, qty, pickFlags (AutoGiven / PlayerChoice)

Quest flags: Daily / Weekly / Raid / Group / AutoComplete /
              AutoAccept / Repeatable / ClassQuest / Pvp

API: WoweeQuestLoader::save / load / exists / findById;
presets makeStarter (1 simple kill quest, references the
bandit creatureId=1000), makeChain (3-quest chain with
prev/next links + AutoComplete bridge + player-choice
rewards), makeDaily (Daily+Repeatable+AutoAccept combo).

CLI added (5 flags, 500 documented total — round milestone):
  --gen-quests / --gen-quests-chain / --gen-quests-daily
  --info-wqt / --validate-wqt

Validator catches: questId=0+duplicates, level=0,
maxLevel<minLevel, empty title, no objectives without
AutoComplete (player can't finish), no rewards at all,
Daily without Repeatable (incoherent), targetId=0,
quantity=0, unknown objective kind, reward itemId=0 or qty=0.

The 3-quest chain demo exercises every major feature:
  • multiple objective kinds (visit / collect / kill)
  • prev/next chain links
  • AutoComplete dialogue-bridge quest
  • PlayerChoice reward (1 of 2 weapons)
2026-05-09 15:25:02 -07:00

153 lines
5.1 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 Quest Template (.wqt) — novel replacement for
// AzerothCore-style quest_template SQL tables PLUS the
// Blizzard Quest.dbc / QuestObjective.dbc trio. The 15th
// open format added to the editor.
//
// Cross-references with previously-added formats:
// WQT.giverCreatureId → WCRT.entry.creatureId
// WQT.turninCreatureId → WCRT.entry.creatureId
// WQT.objective.targetId → WCRT (kill) / WIT (collect) / WOB (interact)
// WQT.rewardItem.itemId → WIT.entry.itemId
// WQT.prevQuestId → WQT.entry.questId (intra-format chain)
// WQT.nextQuestId → WQT.entry.questId
//
// Together with WIT / WCRT / WLOT / WSPN this completes the
// gameplay graph: a content pack can ship items + creatures
// + spawns + loot tables + quests with no SQL or .dbc files.
//
// Binary layout (little-endian):
// magic[4] = "WQTM"
// version (uint32) = current 1
// nameLen + name (catalog label)
// entryCount (uint32)
// entries (each):
// questId (uint32)
// titleLen + title
// objectiveLen + objective -- 1-line "Kill 10 wolves"
// descriptionLen + description -- flavor text
// minLevel (uint16) / questLevel (uint16) / maxLevel (uint16) / pad
// requiredClassMask (uint32) -- bitmask of class IDs; 0 = any
// requiredRaceMask (uint32)
// prevQuestId (uint32) -- 0 = chain start
// nextQuestId (uint32)
// giverCreatureId (uint32)
// turninCreatureId (uint32)
// objectiveCount (uint8) + pad[3]
// objectives (objectiveCount × {
// kind (uint8) + pad[3]
// targetId (uint32)
// quantity (uint16) + pad[2]
// })
// xpReward (uint32)
// moneyCopperReward (uint32)
// rewardItemCount (uint8) + pad[3]
// rewardItems (rewardItemCount × {
// itemId (uint32)
// qty (uint8) + pickFlags (uint8) + pad[2]
// })
// flags (uint32)
struct WoweeQuest {
enum ObjectiveKind : uint8_t {
KillCreature = 0,
CollectItem = 1,
InteractObject = 2,
VisitArea = 3,
EscortNpc = 4,
SpellCast = 5,
};
enum Flags : uint32_t {
Daily = 0x01,
Weekly = 0x02,
Raid = 0x04,
Group = 0x08, // requires party / not solo-completable
AutoComplete = 0x10, // turns in immediately on objective met
AutoAccept = 0x20, // accepts on giver-NPC interaction
Repeatable = 0x40,
ClassQuest = 0x80,
Pvp = 0x100,
};
enum RewardPickFlags : uint8_t {
AutoGiven = 0x01, // always handed out on turn-in
PlayerChoice = 0x02, // player picks from a list at turn-in
};
struct Objective {
uint8_t kind = KillCreature;
uint32_t targetId = 0;
uint16_t quantity = 1;
};
struct RewardItem {
uint32_t itemId = 0;
uint8_t qty = 1;
uint8_t pickFlags = AutoGiven;
};
struct Entry {
uint32_t questId = 0;
std::string title;
std::string objective;
std::string description;
uint16_t minLevel = 1;
uint16_t questLevel = 1;
uint16_t maxLevel = 0; // 0 = no upper cap
uint32_t requiredClassMask = 0;
uint32_t requiredRaceMask = 0;
uint32_t prevQuestId = 0;
uint32_t nextQuestId = 0;
uint32_t giverCreatureId = 0;
uint32_t turninCreatureId = 0;
std::vector<Objective> objectives;
uint32_t xpReward = 0;
uint32_t moneyCopperReward = 0;
std::vector<RewardItem> rewardItems;
uint32_t flags = 0;
};
std::string name;
std::vector<Entry> entries;
bool isValid() const { return !entries.empty(); }
// Lookup by questId — nullptr if not present.
const Entry* findById(uint32_t questId) const;
static const char* objectiveKindName(uint8_t k);
};
class WoweeQuestLoader {
public:
static bool save(const WoweeQuest& cat,
const std::string& basePath);
static WoweeQuest load(const std::string& basePath);
static bool exists(const std::string& basePath);
// Preset emitters used by --gen-quests* variants.
//
// makeStarter — 1 short kill quest: "Kill 10 bandits" with
// XP + small money reward. Cross-references
// WCRT bandit (creatureId=1000) + WCRT
// village innkeeper (giver/turnin=4001).
// makeChain — 3-quest chain: "Investigate" -> "Recover"
// -> "Report Back". Each quest's nextQuestId
// points to the next; the third closes the loop.
// makeDaily — 1 daily repeatable quest with the Daily +
// Repeatable + AutoAccept flag combo.
static WoweeQuest makeStarter(const std::string& catalogName);
static WoweeQuest makeChain(const std::string& catalogName);
static WoweeQuest makeDaily(const std::string& catalogName);
};
} // namespace pipeline
} // namespace wowee