From 408d7a611a94b3bff47e7e7580fd72392f03171f Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 08:01:28 -0700 Subject: [PATCH] refactor(editor): extract quest-reward handlers into cli_quest_reward.cpp Moves the two quest-reward mutation handlers (--add-quest-reward-item, --set-quest-reward) out of main.cpp into a new cli_quest_reward.{hpp,cpp} module. Both operate on a quest's reward struct in zone.json: the first greedy-consumes multiple item paths in one invocation, the second uses order-independent flag/value pairs (--xp / --gold / --silver / --copper) with strict 'only changed fields are written' semantics so partial updates don't clobber unrelated fields. main.cpp shrinks by 114 lines (6,429 to 6,315). --- CMakeLists.txt | 1 + tools/editor/cli_quest_reward.cpp | 153 ++++++++++++++++++++++++++++++ tools/editor/cli_quest_reward.hpp | 17 ++++ tools/editor/main.cpp | 122 +----------------------- 4 files changed, 175 insertions(+), 118 deletions(-) create mode 100644 tools/editor/cli_quest_reward.cpp create mode 100644 tools/editor/cli_quest_reward.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index adb3fa3e..e649da92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1337,6 +1337,7 @@ add_executable(wowee_editor tools/editor/cli_info_audio.cpp tools/editor/cli_world_info.cpp tools/editor/cli_quest_objective.cpp + tools/editor/cli_quest_reward.cpp tools/editor/editor_app.cpp tools/editor/editor_camera.cpp tools/editor/editor_viewport.cpp diff --git a/tools/editor/cli_quest_reward.cpp b/tools/editor/cli_quest_reward.cpp new file mode 100644 index 00000000..43f16b76 --- /dev/null +++ b/tools/editor/cli_quest_reward.cpp @@ -0,0 +1,153 @@ +#include "cli_quest_reward.hpp" + +#include "quest_editor.hpp" + +#include +#include +#include +#include +#include + +namespace wowee { +namespace editor { +namespace cli { + +namespace { + +int handleAddQuestRewardItem(int& i, int argc, char** argv) { + // Append one or more item rewards to a quest. Multiple paths + // can be passed in a single invocation: + // --add-quest-reward-item zone 0 'Item:Sword' 'Item:Shield' + std::string zoneDir = argv[++i]; + std::string idxStr = argv[++i]; + std::string path = zoneDir + "/quests.json"; + if (!std::filesystem::exists(path)) { + std::fprintf(stderr, "add-quest-reward-item: %s not found\n", path.c_str()); + return 1; + } + int idx; + try { idx = std::stoi(idxStr); } + catch (...) { + std::fprintf(stderr, "add-quest-reward-item: bad questIdx '%s'\n", idxStr.c_str()); + return 1; + } + wowee::editor::QuestEditor qe; + if (!qe.loadFromFile(path)) { + std::fprintf(stderr, "add-quest-reward-item: failed to load %s\n", path.c_str()); + return 1; + } + if (idx < 0 || idx >= static_cast(qe.questCount())) { + std::fprintf(stderr, + "add-quest-reward-item: questIdx %d out of range [0, %zu)\n", + idx, qe.questCount()); + return 1; + } + wowee::editor::Quest* q = qe.getQuest(idx); + if (!q) return 1; + int added = 0; + // Greedy-consume any remaining args that don't start with '-' + // so the caller can batch-add a whole loot table in one shot. + while (i + 1 < argc && argv[i + 1][0] != '-') { + q->reward.itemRewards.push_back(argv[++i]); + added++; + } + if (added == 0) { + std::fprintf(stderr, "add-quest-reward-item: need at least one itemPath\n"); + return 1; + } + if (!qe.saveToFile(path)) { + std::fprintf(stderr, "add-quest-reward-item: failed to write %s\n", path.c_str()); + return 1; + } + std::printf("Added %d item reward(s) to quest %d ('%s'), now %zu total\n", + added, idx, q->title.c_str(), q->reward.itemRewards.size()); + return 0; +} + +int handleSetQuestReward(int& i, int argc, char** argv) { + // Update XP / coin reward fields on an existing quest. Each + // field is optional — only the ones explicitly passed are + // changed. This avoids the round-trip-and-clobber footgun of + // a "replace whole reward" command. + std::string zoneDir = argv[++i]; + std::string idxStr = argv[++i]; + std::string path = zoneDir + "/quests.json"; + if (!std::filesystem::exists(path)) { + std::fprintf(stderr, "set-quest-reward: %s not found\n", path.c_str()); + return 1; + } + int idx; + try { idx = std::stoi(idxStr); } + catch (...) { + std::fprintf(stderr, "set-quest-reward: bad questIdx '%s'\n", idxStr.c_str()); + return 1; + } + wowee::editor::QuestEditor qe; + if (!qe.loadFromFile(path)) { + std::fprintf(stderr, "set-quest-reward: failed to load %s\n", path.c_str()); + return 1; + } + if (idx < 0 || idx >= static_cast(qe.questCount())) { + std::fprintf(stderr, + "set-quest-reward: questIdx %d out of range [0, %zu)\n", + idx, qe.questCount()); + return 1; + } + wowee::editor::Quest* q = qe.getQuest(idx); + if (!q) return 1; + int changed = 0; + auto consumeUint = [&](const char* flag, uint32_t& target) { + if (i + 2 < argc && std::strcmp(argv[i + 1], flag) == 0) { + try { + target = static_cast(std::stoul(argv[i + 2])); + i += 2; + changed++; + return true; + } catch (...) { + std::fprintf(stderr, "set-quest-reward: bad %s value '%s'\n", + flag, argv[i + 2]); + } + } + return false; + }; + // Loop until no more recognised flags consume their value — + // order-independent, so callers can pass --gold then --xp. + bool any = true; + while (any) { + any = false; + if (consumeUint("--xp", q->reward.xp)) any = true; + if (consumeUint("--gold", q->reward.gold)) any = true; + if (consumeUint("--silver", q->reward.silver)) any = true; + if (consumeUint("--copper", q->reward.copper)) any = true; + } + if (changed == 0) { + std::fprintf(stderr, + "set-quest-reward: no fields changed — pass --xp / --gold / --silver / --copper\n"); + return 1; + } + if (!qe.saveToFile(path)) { + std::fprintf(stderr, "set-quest-reward: failed to write %s\n", path.c_str()); + return 1; + } + std::printf("Updated %d field(s) on quest %d ('%s'): xp=%u gold=%u silver=%u copper=%u\n", + changed, idx, q->title.c_str(), + q->reward.xp, q->reward.gold, + q->reward.silver, q->reward.copper); + return 0; +} + +} // namespace + +bool handleQuestReward(int& i, int argc, char** argv, int& outRc) { + if (std::strcmp(argv[i], "--add-quest-reward-item") == 0 && i + 3 < argc) { + outRc = handleAddQuestRewardItem(i, argc, argv); return true; + } + if (std::strcmp(argv[i], "--set-quest-reward") == 0 && i + 2 < argc) { + outRc = handleSetQuestReward(i, argc, argv); return true; + } + return false; +} + +} // namespace cli +} // namespace editor +} // namespace wowee diff --git a/tools/editor/cli_quest_reward.hpp b/tools/editor/cli_quest_reward.hpp new file mode 100644 index 00000000..14bcd45f --- /dev/null +++ b/tools/editor/cli_quest_reward.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace wowee { +namespace editor { +namespace cli { + +// Dispatch the quest-reward mutation handlers — both operate +// on a quest's reward struct in zone.json. +// --add-quest-reward-item append item rewards (greedy multi-arg) +// --set-quest-reward update XP / gold / silver / copper +// +// Returns true if matched; outRc holds the exit code. +bool handleQuestReward(int& i, int argc, char** argv, int& outRc); + +} // namespace cli +} // namespace editor +} // namespace wowee diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 64bb1cdc..1d4203e1 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -38,6 +38,7 @@ #include "cli_info_audio.hpp" #include "cli_world_info.hpp" #include "cli_quest_objective.hpp" +#include "cli_quest_reward.hpp" #include "content_pack.hpp" #include "npc_spawner.hpp" #include "object_placer.hpp" @@ -486,6 +487,9 @@ int main(int argc, char* argv[]) { if (wowee::editor::cli::handleQuestObjective(i, argc, argv, outRc)) { return outRc; } + if (wowee::editor::cli::handleQuestReward(i, argc, argv, outRc)) { + return outRc; + } } if (std::strcmp(argv[i], "--data") == 0 && i + 1 < argc) { dataPath = argv[++i]; @@ -1602,124 +1606,6 @@ int main(int argc, char* argv[]) { clone.position.x, clone.position.y, clone.position.z, objs.size()); return 0; - } else if (std::strcmp(argv[i], "--add-quest-reward-item") == 0 && i + 3 < argc) { - // Append one or more item rewards to a quest. Multiple paths - // can be passed in a single invocation: - // --add-quest-reward-item zone 0 'Item:Sword' 'Item:Shield' - std::string zoneDir = argv[++i]; - std::string idxStr = argv[++i]; - std::string path = zoneDir + "/quests.json"; - if (!std::filesystem::exists(path)) { - std::fprintf(stderr, "add-quest-reward-item: %s not found\n", path.c_str()); - return 1; - } - int idx; - try { idx = std::stoi(idxStr); } - catch (...) { - std::fprintf(stderr, "add-quest-reward-item: bad questIdx '%s'\n", idxStr.c_str()); - return 1; - } - wowee::editor::QuestEditor qe; - if (!qe.loadFromFile(path)) { - std::fprintf(stderr, "add-quest-reward-item: failed to load %s\n", path.c_str()); - return 1; - } - if (idx < 0 || idx >= static_cast(qe.questCount())) { - std::fprintf(stderr, - "add-quest-reward-item: questIdx %d out of range [0, %zu)\n", - idx, qe.questCount()); - return 1; - } - wowee::editor::Quest* q = qe.getQuest(idx); - if (!q) return 1; - int added = 0; - // Greedy-consume any remaining args that don't start with '-' - // so the caller can batch-add a whole loot table in one shot. - while (i + 1 < argc && argv[i + 1][0] != '-') { - q->reward.itemRewards.push_back(argv[++i]); - added++; - } - if (added == 0) { - std::fprintf(stderr, "add-quest-reward-item: need at least one itemPath\n"); - return 1; - } - if (!qe.saveToFile(path)) { - std::fprintf(stderr, "add-quest-reward-item: failed to write %s\n", path.c_str()); - return 1; - } - std::printf("Added %d item reward(s) to quest %d ('%s'), now %zu total\n", - added, idx, q->title.c_str(), q->reward.itemRewards.size()); - return 0; - } else if (std::strcmp(argv[i], "--set-quest-reward") == 0 && i + 2 < argc) { - // Update XP / coin reward fields on an existing quest. Each - // field is optional — only the ones explicitly passed are - // changed. This avoids the round-trip-and-clobber footgun of - // a "replace whole reward" command. - std::string zoneDir = argv[++i]; - std::string idxStr = argv[++i]; - std::string path = zoneDir + "/quests.json"; - if (!std::filesystem::exists(path)) { - std::fprintf(stderr, "set-quest-reward: %s not found\n", path.c_str()); - return 1; - } - int idx; - try { idx = std::stoi(idxStr); } - catch (...) { - std::fprintf(stderr, "set-quest-reward: bad questIdx '%s'\n", idxStr.c_str()); - return 1; - } - wowee::editor::QuestEditor qe; - if (!qe.loadFromFile(path)) { - std::fprintf(stderr, "set-quest-reward: failed to load %s\n", path.c_str()); - return 1; - } - if (idx < 0 || idx >= static_cast(qe.questCount())) { - std::fprintf(stderr, - "set-quest-reward: questIdx %d out of range [0, %zu)\n", - idx, qe.questCount()); - return 1; - } - wowee::editor::Quest* q = qe.getQuest(idx); - if (!q) return 1; - int changed = 0; - auto consumeUint = [&](const char* flag, uint32_t& target) { - if (i + 2 < argc && std::strcmp(argv[i + 1], flag) == 0) { - try { - target = static_cast(std::stoul(argv[i + 2])); - i += 2; - changed++; - return true; - } catch (...) { - std::fprintf(stderr, "set-quest-reward: bad %s value '%s'\n", - flag, argv[i + 2]); - } - } - return false; - }; - // Loop until no more recognised flags consume their value — - // order-independent, so callers can pass --gold then --xp. - bool any = true; - while (any) { - any = false; - if (consumeUint("--xp", q->reward.xp)) any = true; - if (consumeUint("--gold", q->reward.gold)) any = true; - if (consumeUint("--silver", q->reward.silver)) any = true; - if (consumeUint("--copper", q->reward.copper)) any = true; - } - if (changed == 0) { - std::fprintf(stderr, - "set-quest-reward: no fields changed — pass --xp / --gold / --silver / --copper\n"); - return 1; - } - if (!qe.saveToFile(path)) { - std::fprintf(stderr, "set-quest-reward: failed to write %s\n", path.c_str()); - return 1; - } - std::printf("Updated %d field(s) on quest %d ('%s'): xp=%u gold=%u silver=%u copper=%u\n", - changed, idx, q->title.c_str(), - q->reward.xp, q->reward.gold, - q->reward.silver, q->reward.copper); - return 0; } else if (std::strcmp(argv[i], "--remove-creature") == 0 && i + 2 < argc) { // Remove a creature spawn by 0-based index. Pair with // --info-creatures (or your editor) to find the right index