From dbb9879a6ec8aa3315ad3274721eeff276568ebd Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 11:23:02 -0700 Subject: [PATCH] feat(editor): --json mode on remaining content inspectors Adds --json to --info-creatures, --info-objects, --info-quests so the per-content inspectors match the per-zone aggregator. Schemas match the existing --zone-summary --json sub-objects. Now 9 inspectors support --json: --info-extract --info-wcp --validate --info-creatures --info-objects --info-quests --zone-summary --list-zones --diff-wcp CI can now drill into per-content reports the same way as the top-level summary, e.g. fail a build if a zone's quests have any chain errors: wowee_editor --info-quests "$zone/quests.json" --json \ | jq -e '.chainErrors | length == 0' --- tools/editor/main.cpp | 64 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 13885b14..80295803 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -47,9 +47,12 @@ static void printUsage(const char* argv0) { std::printf(" --info-extract [--json]\n"); std::printf(" Walk extracted asset tree and report open-format coverage and exit\n"); std::printf(" --info-zone Print zone.json fields (manifest, tiles, audio, flags) and exit\n"); - std::printf(" --info-creatures

Print creatures.json summary (counts, behaviors) and exit\n"); - std::printf(" --info-objects

Print objects.json summary (counts, types, scale range) and exit\n"); - std::printf(" --info-quests

Print quests.json summary (counts, rewards, chain errors) and exit\n"); + std::printf(" --info-creatures

[--json]\n"); + std::printf(" Print creatures.json summary (counts, behaviors) and exit\n"); + std::printf(" --info-objects

[--json]\n"); + std::printf(" Print objects.json summary (counts, types, scale range) and exit\n"); + std::printf(" --info-quests

[--json]\n"); + std::printf(" Print quests.json summary (counts, rewards, chain errors) and exit\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"); @@ -153,6 +156,9 @@ int main(int argc, char* argv[]) { return 0; } else if (std::strcmp(argv[i], "--info-quests") == 0 && i + 1 < argc) { std::string path = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; wowee::editor::QuestEditor qe; if (!qe.loadFromFile(path)) { std::fprintf(stderr, "Failed to load quests.json: %s\n", path.c_str()); @@ -177,6 +183,23 @@ int main(int argc, char* argv[]) { } std::vector errors; qe.validateChains(errors); + if (jsonOut) { + nlohmann::json j; + j["file"] = path; + j["total"] = quests.size(); + j["chained"] = chained; + j["withReward"] = withReward; + j["withItems"] = withItems; + j["totalXp"] = totalXp; + j["avgXpPerQuest"] = quests.empty() ? 0.0 + : double(totalXp) / quests.size(); + j["objectives"] = {{"kill", objKill}, + {"collect", objCollect}, + {"talk", objTalk}}; + j["chainErrors"] = errors; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } std::printf("quests.json: %s\n", path.c_str()); std::printf(" total : %zu\n", quests.size()); std::printf(" chained : %d (have nextQuestId)\n", chained); @@ -193,6 +216,9 @@ int main(int argc, char* argv[]) { return 0; } else if (std::strcmp(argv[i], "--info-objects") == 0 && i + 1 < argc) { std::string path = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; wowee::editor::ObjectPlacer placer; if (!placer.loadFromFile(path)) { std::fprintf(stderr, "Failed to load objects.json: %s\n", path.c_str()); @@ -209,6 +235,20 @@ int main(int argc, char* argv[]) { if (o.scale < minScale) minScale = o.scale; if (o.scale > maxScale) maxScale = o.scale; } + if (jsonOut) { + nlohmann::json j; + j["file"] = path; + j["total"] = objs.size(); + j["m2"] = m2Count; + j["wmo"] = wmoCount; + j["uniquePaths"] = pathHist.size(); + if (!objs.empty()) { + j["scaleMin"] = minScale; + j["scaleMax"] = maxScale; + } + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } std::printf("objects.json: %s\n", path.c_str()); std::printf(" total : %zu\n", objs.size()); std::printf(" M2 doodads : %d\n", m2Count); @@ -379,6 +419,9 @@ int main(int argc, char* argv[]) { return 0; } else if (std::strcmp(argv[i], "--info-creatures") == 0 && i + 1 < argc) { std::string path = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; wowee::editor::NpcSpawner spawner; if (!spawner.loadFromFile(path)) { std::fprintf(stderr, "Failed to load creatures.json: %s\n", path.c_str()); @@ -399,6 +442,21 @@ int main(int argc, char* argv[]) { else if (s.behavior == B::Stationary) stationary++; displayIdHist[s.displayId]++; } + if (jsonOut) { + nlohmann::json j; + j["file"] = path; + j["total"] = spawns.size(); + j["hostile"] = hostile; + j["questgiver"] = questgiver; + j["vendor"] = vendor; + j["trainer"] = trainer; + j["behavior"] = {{"stationary", stationary}, + {"wander", wander}, + {"patrol", patrol}}; + j["uniqueDisplayIds"] = displayIdHist.size(); + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } std::printf("creatures.json: %s\n", path.c_str()); std::printf(" total : %zu\n", spawns.size()); std::printf(" hostile : %d\n", hostile);