diff --git a/tools/editor/cli_content_info.cpp b/tools/editor/cli_content_info.cpp index 100d1c09..9f508a46 100644 --- a/tools/editor/cli_content_info.cpp +++ b/tools/editor/cli_content_info.cpp @@ -1002,6 +1002,114 @@ int handleInfoObject(int& i, int argc, char** argv) { } +int handleInfoQuests(int& i, int argc, char** argv) { + 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()); + return 1; + } + const auto& quests = qe.getQuests(); + int chained = 0, withReward = 0, withItems = 0; + int objKill = 0, objCollect = 0, objTalk = 0; + uint32_t totalXp = 0; + for (const auto& q : quests) { + if (q.nextQuestId != 0) chained++; + if (q.reward.xp > 0 || q.reward.gold > 0 || + q.reward.silver > 0 || q.reward.copper > 0) withReward++; + if (!q.reward.itemRewards.empty()) withItems++; + totalXp += q.reward.xp; + using OT = wowee::editor::QuestObjectiveType; + for (const auto& obj : q.objectives) { + if (obj.type == OT::KillCreature) objKill++; + else if (obj.type == OT::CollectItem) objCollect++; + else if (obj.type == OT::TalkToNPC) objTalk++; + } + } + 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); + std::printf(" with reward : %d\n", withReward); + std::printf(" with items : %d\n", withItems); + std::printf(" total XP : %u (avg %.0f per quest)\n", totalXp, + quests.empty() ? 0.0 : double(totalXp) / quests.size()); + std::printf(" objectives : %d kill, %d collect, %d talk\n", + objKill, objCollect, objTalk); + if (!errors.empty()) { + std::printf(" chain errors: %zu\n", errors.size()); + for (const auto& e : errors) std::printf(" - %s\n", e.c_str()); + } + return 0; +} + +int handleInfoObjects(int& i, int argc, char** argv) { + 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()); + return 1; + } + const auto& objs = placer.getObjects(); + int m2Count = 0, wmoCount = 0; + std::unordered_map pathHist; + float minScale = 1e30f, maxScale = -1e30f; + for (const auto& o : objs) { + if (o.type == wowee::editor::PlaceableType::M2) m2Count++; + else if (o.type == wowee::editor::PlaceableType::WMO) wmoCount++; + pathHist[o.path]++; + 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); + std::printf(" WMO buildings: %d\n", wmoCount); + std::printf(" unique paths: %zu\n", pathHist.size()); + if (!objs.empty()) { + std::printf(" scale range : [%.2f, %.2f]\n", minScale, maxScale); + } + return 0; +} + } // namespace bool handleContentInfo(int& i, int argc, char** argv, int& outRc) { @@ -1053,6 +1161,12 @@ bool handleContentInfo(int& i, int argc, char** argv, int& outRc) { if (std::strcmp(argv[i], "--info-object") == 0 && i + 2 < argc) { outRc = handleInfoObject(i, argc, argv); return true; } + if (std::strcmp(argv[i], "--info-quests") == 0 && i + 1 < argc) { + outRc = handleInfoQuests(i, argc, argv); return true; + } + if (std::strcmp(argv[i], "--info-objects") == 0 && i + 1 < argc) { + outRc = handleInfoObjects(i, argc, argv); return true; + } return false; } diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 7d365cb7..c4eb61f5 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -1076,110 +1076,6 @@ int main(int argc, char* argv[]) { std::printf(" total tris : %zu\n", totalIdx / 3); std::printf(" total mats : %zu (across all groups)\n", totalMats); 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()); - return 1; - } - const auto& quests = qe.getQuests(); - int chained = 0, withReward = 0, withItems = 0; - int objKill = 0, objCollect = 0, objTalk = 0; - uint32_t totalXp = 0; - for (const auto& q : quests) { - if (q.nextQuestId != 0) chained++; - if (q.reward.xp > 0 || q.reward.gold > 0 || - q.reward.silver > 0 || q.reward.copper > 0) withReward++; - if (!q.reward.itemRewards.empty()) withItems++; - totalXp += q.reward.xp; - using OT = wowee::editor::QuestObjectiveType; - for (const auto& obj : q.objectives) { - if (obj.type == OT::KillCreature) objKill++; - else if (obj.type == OT::CollectItem) objCollect++; - else if (obj.type == OT::TalkToNPC) objTalk++; - } - } - 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); - std::printf(" with reward : %d\n", withReward); - std::printf(" with items : %d\n", withItems); - std::printf(" total XP : %u (avg %.0f per quest)\n", totalXp, - quests.empty() ? 0.0 : double(totalXp) / quests.size()); - std::printf(" objectives : %d kill, %d collect, %d talk\n", - objKill, objCollect, objTalk); - if (!errors.empty()) { - std::printf(" chain errors: %zu\n", errors.size()); - for (const auto& e : errors) std::printf(" - %s\n", e.c_str()); - } - 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()); - return 1; - } - const auto& objs = placer.getObjects(); - int m2Count = 0, wmoCount = 0; - std::unordered_map pathHist; - float minScale = 1e30f, maxScale = -1e30f; - for (const auto& o : objs) { - if (o.type == wowee::editor::PlaceableType::M2) m2Count++; - else if (o.type == wowee::editor::PlaceableType::WMO) wmoCount++; - pathHist[o.path]++; - 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); - std::printf(" WMO buildings: %d\n", wmoCount); - std::printf(" unique paths: %zu\n", pathHist.size()); - if (!objs.empty()) { - std::printf(" scale range : [%.2f, %.2f]\n", minScale, maxScale); - } - return 0; } else if (std::strcmp(argv[i], "--info-extract") == 0 && i + 1 < argc) { // Walk an extracted-asset directory and report counts by // extension + open-format coverage. Useful for seeing whether