From 1017ab47519f73d52d2bceff8ccbf122444e1382 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 11:37:10 -0700 Subject: [PATCH] feat(editor): add --add-object CLI for headless M2/WMO placement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors --add-creature for the object placer: wowee_editor --add-object [scale] Lets shell scripts populate objects/buildings without the GUI: for tree in 100,200 150,250 200,300; do x=${tree%,*}; y=${tree#*,} wowee_editor --add-object "$zone" m2 'World/Doodad/Tree.m2' $x $y 0 done Loads existing objects.json first then appends, so multiple invocations build up. Optional scale slot lets the caller set non-default size (clamped to >0 by the load-time guard). Verified end-to-end: scaffolded zone → added m2 + wmo → info-objects reports total=2 with the correct scale range. --- tools/editor/main.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 2e4bf14b..17f9f7b2 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -34,6 +34,8 @@ static void printUsage(const char* argv0) { std::printf(" --scaffold-zone [tx ty] Create a blank zone in custom_zones// and exit\n"); std::printf(" --add-creature [displayId] [level]\n"); std::printf(" Append one creature spawn to /creatures.json and exit\n"); + std::printf(" --add-object [scale]\n"); + std::printf(" Append one object placement to /objects.json and exit\n"); std::printf(" --build-woc Generate a WOC collision mesh from WHM/WOT and exit\n"); std::printf(" --regen-collision Rebuild every WOC under a zone dir and exit\n"); std::printf(" --fix-zone Re-parse + re-save zone JSONs to apply latest scrubs/caps and exit\n"); @@ -85,7 +87,7 @@ int main(int argc, char* argv[]) { "--info-extract", "--info-zone", "--info-wcp", "--list-wcp", "--unpack-wcp", "--pack-wcp", "--validate", "--zone-summary", - "--scaffold-zone", "--add-creature", + "--scaffold-zone", "--add-creature", "--add-object", "--build-woc", "--regen-collision", "--fix-zone", "--export-png", "--convert-m2", "--convert-wmo", @@ -110,6 +112,11 @@ int main(int argc, char* argv[]) { "--add-creature requires \n"); return 1; } + if (std::strcmp(argv[i], "--add-object") == 0 && i + 6 >= argc) { + std::fprintf(stderr, + "--add-object requires \n"); + return 1; + } } for (int i = 1; i < argc; i++) { @@ -1101,6 +1108,57 @@ int main(int argc, char* argv[]) { outPath.c_str(), col.triangles.size(), col.walkableCount(), col.steepCount()); 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: [scale] + std::string zoneDir = argv[++i]; + std::string typeStr = argv[++i]; + std::string gamePath = argv[++i]; + namespace fs = std::filesystem; + if (!fs::exists(zoneDir)) { + std::fprintf(stderr, "add-object: zone '%s' does not exist\n", + zoneDir.c_str()); + return 1; + } + wowee::editor::PlaceableType ptype; + if (typeStr == "m2") ptype = wowee::editor::PlaceableType::M2; + else if (typeStr == "wmo") ptype = wowee::editor::PlaceableType::WMO; + else { + std::fprintf(stderr, "add-object: type must be 'm2' or 'wmo'\n"); + return 1; + } + glm::vec3 pos; + try { + pos.x = std::stof(argv[++i]); + pos.y = std::stof(argv[++i]); + pos.z = std::stof(argv[++i]); + } catch (const std::exception& e) { + std::fprintf(stderr, "add-object: bad coordinate (%s)\n", e.what()); + return 1; + } + wowee::editor::ObjectPlacer placer; + std::string path = zoneDir + "/objects.json"; + if (fs::exists(path)) placer.loadFromFile(path); + placer.setActivePath(gamePath, ptype); + placer.placeObject(pos); + // Optional scale after coordinates. + if (i + 1 < argc && argv[i + 1][0] != '-') { + try { + float scale = std::stof(argv[++i]); + if (std::isfinite(scale) && scale > 0.0f) { + // Set scale on the just-placed object (last in list). + placer.getObjects().back().scale = scale; + } + } catch (...) {} + } + if (!placer.saveToFile(path)) { + std::fprintf(stderr, "add-object: failed to write %s\n", path.c_str()); + return 1; + } + std::printf("Added %s '%s' to %s (now %zu total)\n", + typeStr.c_str(), gamePath.c_str(), path.c_str(), + placer.getObjects().size()); + return 0; } else if (std::strcmp(argv[i], "--add-creature") == 0 && i + 4 < argc) { // Append a single creature spawn to a zone's creatures.json. // Args: [displayId] [level]