diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index fca92c4f..8219eeea 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -477,6 +477,8 @@ static void printUsage(const char* argv0) { std::printf(" Wipe one or more content files (terrain + manifest preserved)\n"); std::printf(" --strip-zone [--dry-run]\n"); std::printf(" Remove derived outputs (.glb/.obj/.stl/.html/.dot/.csv/ZONE.md/DEPS.md)\n"); + std::printf(" --gen-makefile [out.mk]\n"); + std::printf(" Generate a Makefile that rebuilds every derived output for a zone\n"); std::printf(" --repair-zone [--dry-run]\n"); std::printf(" Auto-fix manifest/disk drift (missing tiles in manifest, hasCreatures flag)\n"); std::printf(" --build-woc Generate a WOC collision mesh from WHM/WOT and exit\n"); @@ -688,7 +690,7 @@ int main(int argc, char* argv[]) { "--clone-object", "--remove-creature", "--remove-object", "--remove-quest", "--copy-zone", "--rename-zone", "--clear-zone-content", "--strip-zone", - "--repair-zone", + "--repair-zone", "--gen-makefile", "--build-woc", "--regen-collision", "--fix-zone", "--export-png", "--export-obj", "--import-obj", "--export-wob-obj", "--import-wob-obj", @@ -9588,6 +9590,98 @@ int main(int argc, char* argv[]) { std::printf(" re-run without --dry-run to apply\n"); } return 0; + } else if (std::strcmp(argv[i], "--gen-makefile") == 0 && i + 1 < argc) { + // Generate a Makefile that rebuilds every derived output for + // a zone. With this in place, designers can `make` to refresh + // glb/obj/stl/html/csv/md from sources after editing + // creatures.json or terrain — without remembering which + // wowee_editor flag does what. The Makefile uses dependency + // tracking so only stale outputs get rebuilt. + std::string zoneDir = argv[++i]; + std::string outPath; + if (i + 1 < argc && argv[i + 1][0] != '-') outPath = argv[++i]; + namespace fs = std::filesystem; + std::string manifestPath = zoneDir + "/zone.json"; + if (!fs::exists(manifestPath)) { + std::fprintf(stderr, + "gen-makefile: %s has no zone.json\n", zoneDir.c_str()); + return 1; + } + wowee::editor::ZoneManifest zm; + if (!zm.load(manifestPath)) { + std::fprintf(stderr, "gen-makefile: parse failed\n"); + return 1; + } + if (outPath.empty()) outPath = zoneDir + "/Makefile"; + std::ofstream out(outPath); + if (!out) { + std::fprintf(stderr, + "gen-makefile: cannot write %s\n", outPath.c_str()); + return 1; + } + // Use a single absolute editor path so the Makefile works + // from any cwd (running `make -C custom_zones/MyZone` etc.). + std::error_code ec; + std::string editorBin = fs::canonical("/proc/self/exe", ec).string(); + if (ec || editorBin.empty()) editorBin = "wowee_editor"; + // Per-tile WHM/WOT inputs feed the bake targets. Compose the + // list once so all targets share the same dep set. + std::string tileWhmDeps; + for (const auto& [tx, ty] : zm.tiles) { + tileWhmDeps += " " + zm.mapName + "_" + + std::to_string(tx) + "_" + std::to_string(ty) + + ".whm"; + } + std::string slug = zm.mapName; + out << "# Generated by wowee_editor --gen-makefile\n" + "# Zone: " << slug << "\n" + "# Run from this directory: `make` to rebuild all\n" + "# derived outputs from sources, `make clean` to wipe.\n\n"; + out << "EDITOR := " << editorBin << "\n"; + out << "ZONE := .\n\n"; + // Source dep aggregations for content-derived outputs. + out << "CONTENT_SRCS := zone.json $(wildcard creatures.json) " + "$(wildcard objects.json) $(wildcard quests.json)\n"; + out << "TERRAIN_SRCS := zone.json" << tileWhmDeps << "\n\n"; + out << ".PHONY: all clean glb obj stl html docs csv graph\n\n"; + out << "all: glb obj stl html docs csv graph\n\n"; + // Each target lists its dependencies so make can skip already- + // up-to-date outputs. + out << "glb: " << slug << ".glb\n" + << slug << ".glb: $(TERRAIN_SRCS)\n" + << "\t$(EDITOR) --bake-zone-glb $(ZONE)\n\n"; + out << "obj: " << slug << ".obj\n" + << slug << ".obj: $(TERRAIN_SRCS)\n" + << "\t$(EDITOR) --bake-zone-obj $(ZONE)\n\n"; + out << "stl: " << slug << ".stl\n" + << slug << ".stl: $(TERRAIN_SRCS)\n" + << "\t$(EDITOR) --bake-zone-stl $(ZONE)\n\n"; + out << "html: " << slug << ".html\n" + << slug << ".html: " << slug << ".glb\n" + << "\t$(EDITOR) --export-zone-html $(ZONE)\n\n"; + out << "docs: ZONE.md DEPS.md\n"; + out << "ZONE.md: $(CONTENT_SRCS)\n" + << "\t$(EDITOR) --export-zone-summary-md $(ZONE)\n"; + out << "DEPS.md: zone.json $(wildcard objects.json) $(wildcard *.wob)\n" + << "\t$(EDITOR) --export-zone-deps-md $(ZONE)\n\n"; + // CSV + graph targets use '-' prefix so missing-content + // (zone without creatures/quests) doesn't fail the whole + // 'make all'. The editor prints the error to stderr; make + // continues with the next target. + out << "csv:\n" + << "\t-$(EDITOR) --export-zone-csv $(ZONE)\n\n"; + out << "graph:\n" + << "\t-$(EDITOR) --export-quest-graph $(ZONE)\n\n"; + out << "clean:\n" + << "\t$(EDITOR) --strip-zone $(ZONE)\n\n"; + out << "validate:\n" + << "\t$(EDITOR) --validate-all $(ZONE)\n"; + out.close(); + std::printf("Wrote %s\n", outPath.c_str()); + std::printf(" zone : %s\n", slug.c_str()); + std::printf(" tiles : %zu (terrain dep)\n", zm.tiles.size()); + std::printf(" next : cd %s && make\n", zoneDir.c_str()); + return 0; } else if (std::strcmp(argv[i], "--pack-wcp") == 0 && i + 1 < argc) { // Pack a zone directory into a .wcp archive. // Usage: --pack-wcp [destPath]