diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 2dc7e4af..a05e7b20 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -502,6 +502,10 @@ static void printUsage(const char* argv0) { std::printf(" List every object with index, type, path, position\n"); std::printf(" --list-quests

[--json]\n"); std::printf(" List every quest with index, title, giver, XP\n"); + std::printf(" --list-quest-objectives

[--json]\n"); + std::printf(" List every objective on a quest (for --remove-quest-objective)\n"); + std::printf(" --list-quest-rewards

[--json]\n"); + std::printf(" List XP/coin/item rewards on a quest\n"); std::printf(" --info-wcp [--json]\n"); std::printf(" Print WCP archive metadata (name, files) and exit\n"); std::printf(" --list-wcp Print every file inside a WCP archive (sorted by path) and exit\n"); @@ -530,6 +534,7 @@ int main(int argc, char* argv[]) { "--info-png", "--info-jsondbc", "--info-zone", "--info-wcp", "--list-wcp", "--list-creatures", "--list-objects", "--list-quests", + "--list-quest-objectives", "--list-quest-rewards", "--unpack-wcp", "--pack-wcp", "--validate", "--validate-wom", "--validate-wob", "--validate-woc", "--validate-whm", "--validate-all", "--zone-summary", @@ -1414,6 +1419,124 @@ int main(int argc, char* argv[]) { q.nextQuestId ? " [chained]" : ""); } return 0; + } else if (std::strcmp(argv[i], "--list-quest-objectives") == 0 && i + 2 < argc) { + // Per-quest objective listing — pairs with --remove-quest-objective + // (which takes objIdx). Tabulates type, target, count, description. + std::string path = argv[++i]; + std::string idxStr = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; + int qIdx; + try { qIdx = std::stoi(idxStr); } + catch (...) { + std::fprintf(stderr, "list-quest-objectives: bad questIdx '%s'\n", idxStr.c_str()); + return 1; + } + wowee::editor::QuestEditor qe; + if (!qe.loadFromFile(path)) { + std::fprintf(stderr, "list-quest-objectives: failed to load %s\n", path.c_str()); + return 1; + } + if (qIdx < 0 || qIdx >= static_cast(qe.questCount())) { + std::fprintf(stderr, + "list-quest-objectives: questIdx %d out of range [0, %zu)\n", + qIdx, qe.questCount()); + return 1; + } + const auto& q = qe.getQuests()[qIdx]; + using OT = wowee::editor::QuestObjectiveType; + auto typeName = [](OT t) { + switch (t) { + case OT::KillCreature: return "kill"; + case OT::CollectItem: return "collect"; + case OT::TalkToNPC: return "talk"; + case OT::ExploreArea: return "explore"; + case OT::EscortNPC: return "escort"; + case OT::UseObject: return "use"; + } + return "?"; + }; + if (jsonOut) { + nlohmann::json j; + j["file"] = path; + j["questIdx"] = qIdx; + j["title"] = q.title; + j["count"] = q.objectives.size(); + nlohmann::json arr = nlohmann::json::array(); + for (size_t o = 0; o < q.objectives.size(); ++o) { + const auto& ob = q.objectives[o]; + arr.push_back({ + {"index", o}, + {"type", typeName(ob.type)}, + {"target", ob.targetName}, + {"count", ob.targetCount}, + {"description", ob.description}, + }); + } + j["objectives"] = arr; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("Quest %d ('%s'): %zu objective(s)\n", + qIdx, q.title.c_str(), q.objectives.size()); + std::printf(" idx type count target description\n"); + for (size_t o = 0; o < q.objectives.size(); ++o) { + const auto& ob = q.objectives[o]; + std::printf(" %3zu %-7s %5u %-18s %s\n", + o, typeName(ob.type), ob.targetCount, + ob.targetName.substr(0, 18).c_str(), + ob.description.c_str()); + } + return 0; + } else if (std::strcmp(argv[i], "--list-quest-rewards") == 0 && i + 2 < argc) { + // Per-quest reward listing. Shows XP/coin breakdown plus the + // full itemRewards list (which --info-quests only counts). + std::string path = argv[++i]; + std::string idxStr = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; + int qIdx; + try { qIdx = std::stoi(idxStr); } + catch (...) { + std::fprintf(stderr, "list-quest-rewards: bad questIdx '%s'\n", idxStr.c_str()); + return 1; + } + wowee::editor::QuestEditor qe; + if (!qe.loadFromFile(path)) { + std::fprintf(stderr, "list-quest-rewards: failed to load %s\n", path.c_str()); + return 1; + } + if (qIdx < 0 || qIdx >= static_cast(qe.questCount())) { + std::fprintf(stderr, + "list-quest-rewards: questIdx %d out of range [0, %zu)\n", + qIdx, qe.questCount()); + return 1; + } + const auto& q = qe.getQuests()[qIdx]; + const auto& r = q.reward; + if (jsonOut) { + nlohmann::json j; + j["file"] = path; + j["questIdx"] = qIdx; + j["title"] = q.title; + j["xp"] = r.xp; + j["gold"] = r.gold; + j["silver"] = r.silver; + j["copper"] = r.copper; + j["items"] = r.itemRewards; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("Quest %d ('%s') rewards:\n", qIdx, q.title.c_str()); + std::printf(" xp : %u\n", r.xp); + std::printf(" coin : %ug %us %uc\n", r.gold, r.silver, r.copper); + std::printf(" items : %zu\n", r.itemRewards.size()); + for (size_t k = 0; k < r.itemRewards.size(); ++k) { + std::printf(" [%zu] %s\n", k, r.itemRewards[k].c_str()); + } + return 0; } else if (std::strcmp(argv[i], "--diff-wcp") == 0 && i + 2 < argc) { // Print which files differ between two WCP archives. Useful // when verifying that an authoring tweak only changed what