diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index f058d5e0..61f2d24f 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -409,6 +409,12 @@ static void printUsage(const char* argv0) { std::printf(" Append one object placement to /objects.json and exit\n"); std::printf(" --add-quest [giverId] [turnInId] [xp] [level]\n"); std::printf(" Append one quest to <zoneDir>/quests.json and exit\n"); + std::printf(" --remove-creature <zoneDir> <index>\n"); + std::printf(" Remove creature at given 0-based index from <zoneDir>/creatures.json\n"); + std::printf(" --remove-object <zoneDir> <index>\n"); + std::printf(" Remove object at given 0-based index from <zoneDir>/objects.json\n"); + std::printf(" --remove-quest <zoneDir> <index>\n"); + std::printf(" Remove quest at given 0-based index from <zoneDir>/quests.json\n"); std::printf(" --copy-zone <srcDir> <newName>\n"); std::printf(" Duplicate a zone to custom_zones/<slug>/ with renamed slug-prefixed files\n"); std::printf(" --build-woc <wot-base> Generate a WOC collision mesh from WHM/WOT and exit\n"); @@ -477,6 +483,7 @@ int main(int argc, char* argv[]) { "--validate", "--validate-wom", "--validate-wob", "--validate-woc", "--validate-whm", "--validate-all", "--zone-summary", "--scaffold-zone", "--add-creature", "--add-object", "--add-quest", + "--remove-creature", "--remove-object", "--remove-quest", "--copy-zone", "--build-woc", "--regen-collision", "--fix-zone", "--export-png", @@ -517,6 +524,13 @@ int main(int argc, char* argv[]) { "--copy-zone requires <srcDir> <newName>\n"); return 1; } + for (const char* opt : {"--remove-creature", "--remove-object", + "--remove-quest"}) { + if (std::strcmp(argv[i], opt) == 0 && i + 2 >= argc) { + std::fprintf(stderr, "%s requires <zoneDir> <index>\n", opt); + return 1; + } + } } for (int i = 1; i < argc; i++) { @@ -1881,6 +1895,100 @@ int main(int argc, char* argv[]) { std::printf("Added quest '%s' to %s (now %zu total)\n", title.c_str(), path.c_str(), qe.questCount()); 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 + // first; nothing identifies entries reliably across reloads. + std::string zoneDir = argv[++i]; + std::string idxStr = argv[++i]; + std::string path = zoneDir + "/creatures.json"; + if (!std::filesystem::exists(path)) { + std::fprintf(stderr, "remove-creature: %s not found\n", path.c_str()); + return 1; + } + int idx; + try { idx = std::stoi(idxStr); } + catch (...) { + std::fprintf(stderr, "remove-creature: bad index '%s'\n", idxStr.c_str()); + return 1; + } + wowee::editor::NpcSpawner sp; + sp.loadFromFile(path); + if (idx < 0 || idx >= static_cast<int>(sp.spawnCount())) { + std::fprintf(stderr, "remove-creature: index %d out of range [0, %zu)\n", + idx, sp.spawnCount()); + return 1; + } + std::string removedName = sp.getSpawns()[idx].name; + sp.removeCreature(idx); + if (!sp.saveToFile(path)) { + std::fprintf(stderr, "remove-creature: failed to write %s\n", path.c_str()); + return 1; + } + std::printf("Removed creature '%s' (was index %d) from %s (now %zu total)\n", + removedName.c_str(), idx, path.c_str(), sp.spawnCount()); + return 0; + } else if (std::strcmp(argv[i], "--remove-object") == 0 && i + 2 < argc) { + std::string zoneDir = argv[++i]; + std::string idxStr = argv[++i]; + std::string path = zoneDir + "/objects.json"; + if (!std::filesystem::exists(path)) { + std::fprintf(stderr, "remove-object: %s not found\n", path.c_str()); + return 1; + } + int idx; + try { idx = std::stoi(idxStr); } + catch (...) { + std::fprintf(stderr, "remove-object: bad index '%s'\n", idxStr.c_str()); + return 1; + } + wowee::editor::ObjectPlacer placer; + placer.loadFromFile(path); + auto& objs = placer.getObjects(); + if (idx < 0 || idx >= static_cast<int>(objs.size())) { + std::fprintf(stderr, "remove-object: index %d out of range [0, %zu)\n", + idx, objs.size()); + return 1; + } + std::string removedPath = objs[idx].path; + objs.erase(objs.begin() + idx); + if (!placer.saveToFile(path)) { + std::fprintf(stderr, "remove-object: failed to write %s\n", path.c_str()); + return 1; + } + std::printf("Removed object '%s' (was index %d) from %s (now %zu total)\n", + removedPath.c_str(), idx, path.c_str(), objs.size()); + return 0; + } else if (std::strcmp(argv[i], "--remove-quest") == 0 && i + 2 < argc) { + std::string zoneDir = argv[++i]; + std::string idxStr = argv[++i]; + std::string path = zoneDir + "/quests.json"; + if (!std::filesystem::exists(path)) { + std::fprintf(stderr, "remove-quest: %s not found\n", path.c_str()); + return 1; + } + int idx; + try { idx = std::stoi(idxStr); } + catch (...) { + std::fprintf(stderr, "remove-quest: bad index '%s'\n", idxStr.c_str()); + return 1; + } + wowee::editor::QuestEditor qe; + qe.loadFromFile(path); + if (idx < 0 || idx >= static_cast<int>(qe.questCount())) { + std::fprintf(stderr, "remove-quest: index %d out of range [0, %zu)\n", + idx, qe.questCount()); + return 1; + } + std::string removedTitle = qe.getQuests()[idx].title; + qe.removeQuest(idx); + if (!qe.saveToFile(path)) { + std::fprintf(stderr, "remove-quest: failed to write %s\n", path.c_str()); + return 1; + } + std::printf("Removed quest '%s' (was index %d) from %s (now %zu total)\n", + removedTitle.c_str(), idx, path.c_str(), qe.questCount()); + return 0; } else if (std::strcmp(argv[i], "--add-object") == 0 && i + 5 < argc) { // Append a single object placement to a zone's objects.json. // Args: <zoneDir> <m2|wmo> <gamePath> <x> <y> <z> [scale]