diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 61f2d24f..b5c44889 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -455,6 +455,12 @@ static void printUsage(const char* argv0) { 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(" --list-creatures

[--json]\n"); + std::printf(" List every creature with index, name, position, level (for --remove-creature)\n"); + std::printf(" --list-objects

[--json]\n"); + 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(" --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"); @@ -479,6 +485,7 @@ int main(int argc, char* argv[]) { "--info-creatures", "--info-objects", "--info-quests", "--info-extract", "--list-missing-sidecars", "--info-zone", "--info-wcp", "--list-wcp", + "--list-creatures", "--list-objects", "--list-quests", "--unpack-wcp", "--pack-wcp", "--validate", "--validate-wom", "--validate-wob", "--validate-woc", "--validate-whm", "--validate-all", "--zone-summary", @@ -1056,6 +1063,135 @@ int main(int argc, char* argv[]) { stationary, wander, patrol); std::printf(" unique displayIds: %zu\n", displayIdHist.size()); return 0; + } else if (std::strcmp(argv[i], "--list-creatures") == 0 && i + 1 < argc) { + // Verbose enumeration of every spawn — needed because + // --remove-creature takes a 0-based index but --info-creatures + // only shows aggregate counts. + 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()); + return 1; + } + const auto& spawns = spawner.getSpawns(); + if (jsonOut) { + nlohmann::json j; + j["file"] = path; + j["total"] = spawns.size(); + nlohmann::json arr = nlohmann::json::array(); + for (size_t k = 0; k < spawns.size(); ++k) { + const auto& s = spawns[k]; + arr.push_back({ + {"index", k}, + {"name", s.name}, + {"displayId", s.displayId}, + {"level", s.level}, + {"position", {s.position.x, s.position.y, s.position.z}}, + {"hostile", s.hostile}, + }); + } + j["spawns"] = arr; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("creatures.json: %s (%zu total)\n", path.c_str(), spawns.size()); + std::printf(" idx name lvl display pos (x, y, z)\n"); + for (size_t k = 0; k < spawns.size(); ++k) { + const auto& s = spawns[k]; + std::printf(" %3zu %-30s %3u %7u (%.1f, %.1f, %.1f)%s\n", + k, s.name.substr(0, 30).c_str(), s.level, s.displayId, + s.position.x, s.position.y, s.position.z, + s.hostile ? " [hostile]" : ""); + } + return 0; + } else if (std::strcmp(argv[i], "--list-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(); + auto typeStr = [](wowee::editor::PlaceableType t) { + return t == wowee::editor::PlaceableType::M2 ? "m2" : "wmo"; + }; + if (jsonOut) { + nlohmann::json j; + j["file"] = path; + j["total"] = objs.size(); + nlohmann::json arr = nlohmann::json::array(); + for (size_t k = 0; k < objs.size(); ++k) { + const auto& o = objs[k]; + arr.push_back({ + {"index", k}, + {"type", typeStr(o.type)}, + {"path", o.path}, + {"position", {o.position.x, o.position.y, o.position.z}}, + {"scale", o.scale}, + }); + } + j["objects"] = arr; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("objects.json: %s (%zu total)\n", path.c_str(), objs.size()); + std::printf(" idx type scale path pos (x, y, z)\n"); + for (size_t k = 0; k < objs.size(); ++k) { + const auto& o = objs[k]; + std::printf(" %3zu %-4s %5.2f %-38s (%.1f, %.1f, %.1f)\n", + k, typeStr(o.type), o.scale, + o.path.substr(0, 38).c_str(), + o.position.x, o.position.y, o.position.z); + } + return 0; + } else if (std::strcmp(argv[i], "--list-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(); + if (jsonOut) { + nlohmann::json j; + j["file"] = path; + j["total"] = quests.size(); + nlohmann::json arr = nlohmann::json::array(); + for (size_t k = 0; k < quests.size(); ++k) { + const auto& q = quests[k]; + arr.push_back({ + {"index", k}, + {"title", q.title}, + {"giver", q.questGiverNpcId}, + {"turnIn", q.turnInNpcId}, + {"requiredLevel", q.requiredLevel}, + {"xp", q.reward.xp}, + {"nextQuestId", q.nextQuestId}, + }); + } + j["quests"] = arr; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("quests.json: %s (%zu total)\n", path.c_str(), quests.size()); + std::printf(" idx lvl giver turnIn xp title\n"); + for (size_t k = 0; k < quests.size(); ++k) { + const auto& q = quests[k]; + std::printf(" %3zu %3u %7u %7u %5u %s%s\n", + k, q.requiredLevel, q.questGiverNpcId, q.turnInNpcId, + q.reward.xp, q.title.c_str(), + q.nextQuestId ? " [chained]" : ""); + } + 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