From fca17592b16c233c27e65f8e2e753432c94a62bb Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 09:44:52 -0700 Subject: [PATCH] refactor(editor): extract zone-data maintenance into cli_zone_data.cpp Moves three derived-data maintenance handlers (--fix-zone, --regen-collision, --build-woc) out of main.cpp into a new cli_zone_data.{hpp,cpp} module. Distinct from cli_repair (which fixes manifest-vs-disk drift): these rebuild derived terrain data and clean up JSON files via load+save: - fix-zone re-parses + re-saves every JSON sidecar to apply load-time scrubs and save-time caps - regen-collision rebuilds WOC for every WHM/WOT in a zone - build-woc handles single-tile WOC generation main.cpp shrinks by 104 lines (1,541 to 1,437). --- CMakeLists.txt | 1 + tools/editor/cli_zone_data.cpp | 155 +++++++++++++++++++++++++++++++++ tools/editor/cli_zone_data.hpp | 21 +++++ tools/editor/main.cpp | 112 +----------------------- 4 files changed, 181 insertions(+), 108 deletions(-) create mode 100644 tools/editor/cli_zone_data.cpp create mode 100644 tools/editor/cli_zone_data.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1249e62d..13e06541 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1358,6 +1358,7 @@ add_executable(wowee_editor tools/editor/cli_introspect.cpp tools/editor/cli_texture_helpers.cpp tools/editor/cli_mesh_info.cpp + tools/editor/cli_zone_data.cpp tools/editor/editor_app.cpp tools/editor/editor_camera.cpp tools/editor/editor_viewport.cpp diff --git a/tools/editor/cli_zone_data.cpp b/tools/editor/cli_zone_data.cpp new file mode 100644 index 00000000..335f19f8 --- /dev/null +++ b/tools/editor/cli_zone_data.cpp @@ -0,0 +1,155 @@ +#include "cli_zone_data.hpp" + +#include "zone_manifest.hpp" +#include "npc_spawner.hpp" +#include "object_placer.hpp" +#include "quest_editor.hpp" +#include "pipeline/wowee_terrain_loader.hpp" +#include "pipeline/wowee_collision.hpp" +#include "pipeline/adt_loader.hpp" + +#include +#include +#include +#include +#include + +namespace wowee { +namespace editor { +namespace cli { + +namespace { + +int handleFixZone(int& i, int argc, char** argv) { + // Re-parse + re-save every JSON/binary file in a zone to apply + // the editor's load-time scrubs and save-time caps. Useful when + // an old zone was created before recent hardening — running + // this once cleans up NaN/oversize fields without touching + // the editor GUI. + std::string zoneDir = argv[++i]; + namespace fs = std::filesystem; + if (!fs::exists(zoneDir)) { + std::fprintf(stderr, "fix-zone: %s does not exist\n", zoneDir.c_str()); + return 1; + } + int touched = 0; + // zone.json + { + wowee::editor::ZoneManifest m; + std::string p = zoneDir + "/zone.json"; + if (fs::exists(p) && m.load(p) && m.save(p)) touched++; + } + // creatures.json + { + wowee::editor::NpcSpawner sp; + std::string p = zoneDir + "/creatures.json"; + if (fs::exists(p) && sp.loadFromFile(p) && sp.saveToFile(p)) touched++; + } + // objects.json + { + wowee::editor::ObjectPlacer op; + std::string p = zoneDir + "/objects.json"; + if (fs::exists(p) && op.loadFromFile(p) && op.saveToFile(p)) touched++; + } + // quests.json + { + wowee::editor::QuestEditor qe; + std::string p = zoneDir + "/quests.json"; + if (fs::exists(p) && qe.loadFromFile(p) && qe.saveToFile(p)) touched++; + } + // WHM/WOT pairs and WoB files would need full pipeline access; + // skip them — the editor opens them on next zone load anyway, + // and the load-time scrubs run then. + std::printf("fix-zone: cleaned %d files in %s\n", touched, zoneDir.c_str()); + return 0; +} + +int handleRegenCollision(int& i, int argc, char** argv) { + // Find all WHM/WOT pairs under a zone dir and rebuild WOC for each. + // Useful after sculpting changes when you want to re-derive + // collision in batch instead of one tile at a time. + std::string zoneDir = argv[++i]; + namespace fs = std::filesystem; + if (!fs::exists(zoneDir)) { + std::fprintf(stderr, "regen-collision: %s does not exist\n", + zoneDir.c_str()); + return 1; + } + int rebuilt = 0, failed = 0; + for (auto& entry : fs::recursive_directory_iterator(zoneDir)) { + if (!entry.is_regular_file()) continue; + if (entry.path().extension() != ".whm") continue; + std::string base = entry.path().string(); + base = base.substr(0, base.size() - 4); // strip .whm + wowee::pipeline::ADTTerrain terrain; + if (!wowee::pipeline::WoweeTerrainLoader::load(base, terrain)) { + std::fprintf(stderr, " FAILED to load: %s\n", base.c_str()); + failed++; + continue; + } + auto col = wowee::pipeline::WoweeCollisionBuilder::fromTerrain(terrain); + std::string outPath = base + ".woc"; + if (wowee::pipeline::WoweeCollisionBuilder::save(col, outPath)) { + std::printf(" WOC rebuilt: %s (%zu triangles)\n", + outPath.c_str(), col.triangles.size()); + rebuilt++; + } else { + std::fprintf(stderr, " FAILED to save: %s\n", outPath.c_str()); + failed++; + } + } + std::printf("regen-collision: %d rebuilt, %d failed\n", rebuilt, failed); + return failed > 0 ? 1 : 0; +} + +int handleBuildWoc(int& i, int argc, char** argv) { + // Generate a WOC collision mesh from a WHM/WOT terrain pair. + // Uses terrain triangles only (no WMO overlays); useful as a + // first-pass collision build before the editor adds buildings. + std::string base = argv[++i]; + for (const char* ext : {".wot", ".whm", ".woc"}) { + if (base.size() >= 4 && base.substr(base.size() - 4) == ext) { + base = base.substr(0, base.size() - 4); + break; + } + } + if (!wowee::pipeline::WoweeTerrainLoader::exists(base)) { + std::fprintf(stderr, "WOT/WHM not found at base: %s\n", base.c_str()); + return 1; + } + wowee::pipeline::ADTTerrain terrain; + if (!wowee::pipeline::WoweeTerrainLoader::load(base, terrain)) { + std::fprintf(stderr, "Failed to load terrain: %s\n", base.c_str()); + return 1; + } + auto col = wowee::pipeline::WoweeCollisionBuilder::fromTerrain(terrain); + std::string outPath = base + ".woc"; + if (!wowee::pipeline::WoweeCollisionBuilder::save(col, outPath)) { + std::fprintf(stderr, "WOC save failed: %s\n", outPath.c_str()); + return 1; + } + std::printf("WOC built: %s (%zu triangles, %zu walkable, %zu steep)\n", + outPath.c_str(), + col.triangles.size(), col.walkableCount(), col.steepCount()); + return 0; +} + + +} // namespace + +bool handleZoneData(int& i, int argc, char** argv, int& outRc) { + if (std::strcmp(argv[i], "--fix-zone") == 0 && i + 1 < argc) { + outRc = handleFixZone(i, argc, argv); return true; + } + if (std::strcmp(argv[i], "--regen-collision") == 0 && i + 1 < argc) { + outRc = handleRegenCollision(i, argc, argv); return true; + } + if (std::strcmp(argv[i], "--build-woc") == 0 && i + 1 < argc) { + outRc = handleBuildWoc(i, argc, argv); return true; + } + return false; +} + +} // namespace cli +} // namespace editor +} // namespace wowee diff --git a/tools/editor/cli_zone_data.hpp b/tools/editor/cli_zone_data.hpp new file mode 100644 index 00000000..4b3ed154 --- /dev/null +++ b/tools/editor/cli_zone_data.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace wowee { +namespace editor { +namespace cli { + +// Dispatch the zone-data maintenance handlers — re-derive +// stored data files (collision, JSON sidecars) from sources +// after authoring changes. Distinct from cli_repair (which +// fixes manifest-vs-disk drift): these rebuild derived +// terrain data and clean up JSON files via load+save. +// --fix-zone re-parse + re-save every zone JSON to apply scrubs +// --regen-collision rebuild WOC for every WHM/WOT in a zone +// --build-woc single-tile WOC build from a WHM/WOT base +// +// Returns true if matched; outRc holds the exit code. +bool handleZoneData(int& i, int argc, char** argv, int& outRc); + +} // namespace cli +} // namespace editor +} // namespace wowee diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index ddc475f5..24a8f99b 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -59,6 +59,7 @@ #include "cli_introspect.hpp" #include "cli_texture_helpers.hpp" #include "cli_mesh_info.hpp" +#include "cli_zone_data.hpp" #include "content_pack.hpp" #include "npc_spawner.hpp" #include "object_placer.hpp" @@ -577,6 +578,9 @@ int main(int argc, char* argv[]) { if (wowee::editor::cli::handleMeshInfo(i, argc, argv, outRc)) { return outRc; } + if (wowee::editor::cli::handleZoneData(i, argc, argv, outRc)) { + return outRc; + } } if (std::strcmp(argv[i], "--data") == 0 && i + 1 < argc) { dataPath = argv[++i]; @@ -1108,114 +1112,6 @@ int main(int argc, char* argv[]) { wowee::editor::WoweeTerrain::exportZoneMap(terrain, base + "_zone.png", 512); std::printf("Exported PNGs: %s_{heightmap,normals,zone}.png\n", base.c_str()); return 0; - } else if (std::strcmp(argv[i], "--fix-zone") == 0 && i + 1 < argc) { - // Re-parse + re-save every JSON/binary file in a zone to apply - // the editor's load-time scrubs and save-time caps. Useful when - // an old zone was created before recent hardening — running - // this once cleans up NaN/oversize fields without touching - // the editor GUI. - std::string zoneDir = argv[++i]; - namespace fs = std::filesystem; - if (!fs::exists(zoneDir)) { - std::fprintf(stderr, "fix-zone: %s does not exist\n", zoneDir.c_str()); - return 1; - } - int touched = 0; - // zone.json - { - wowee::editor::ZoneManifest m; - std::string p = zoneDir + "/zone.json"; - if (fs::exists(p) && m.load(p) && m.save(p)) touched++; - } - // creatures.json - { - wowee::editor::NpcSpawner sp; - std::string p = zoneDir + "/creatures.json"; - if (fs::exists(p) && sp.loadFromFile(p) && sp.saveToFile(p)) touched++; - } - // objects.json - { - wowee::editor::ObjectPlacer op; - std::string p = zoneDir + "/objects.json"; - if (fs::exists(p) && op.loadFromFile(p) && op.saveToFile(p)) touched++; - } - // quests.json - { - wowee::editor::QuestEditor qe; - std::string p = zoneDir + "/quests.json"; - if (fs::exists(p) && qe.loadFromFile(p) && qe.saveToFile(p)) touched++; - } - // WHM/WOT pairs and WoB files would need full pipeline access; - // skip them — the editor opens them on next zone load anyway, - // and the load-time scrubs run then. - std::printf("fix-zone: cleaned %d files in %s\n", touched, zoneDir.c_str()); - return 0; - } else if (std::strcmp(argv[i], "--regen-collision") == 0 && i + 1 < argc) { - // Find all WHM/WOT pairs under a zone dir and rebuild WOC for each. - // Useful after sculpting changes when you want to re-derive - // collision in batch instead of one tile at a time. - std::string zoneDir = argv[++i]; - namespace fs = std::filesystem; - if (!fs::exists(zoneDir)) { - std::fprintf(stderr, "regen-collision: %s does not exist\n", - zoneDir.c_str()); - return 1; - } - int rebuilt = 0, failed = 0; - for (auto& entry : fs::recursive_directory_iterator(zoneDir)) { - if (!entry.is_regular_file()) continue; - if (entry.path().extension() != ".whm") continue; - std::string base = entry.path().string(); - base = base.substr(0, base.size() - 4); // strip .whm - wowee::pipeline::ADTTerrain terrain; - if (!wowee::pipeline::WoweeTerrainLoader::load(base, terrain)) { - std::fprintf(stderr, " FAILED to load: %s\n", base.c_str()); - failed++; - continue; - } - auto col = wowee::pipeline::WoweeCollisionBuilder::fromTerrain(terrain); - std::string outPath = base + ".woc"; - if (wowee::pipeline::WoweeCollisionBuilder::save(col, outPath)) { - std::printf(" WOC rebuilt: %s (%zu triangles)\n", - outPath.c_str(), col.triangles.size()); - rebuilt++; - } else { - std::fprintf(stderr, " FAILED to save: %s\n", outPath.c_str()); - failed++; - } - } - std::printf("regen-collision: %d rebuilt, %d failed\n", rebuilt, failed); - return failed > 0 ? 1 : 0; - } else if (std::strcmp(argv[i], "--build-woc") == 0 && i + 1 < argc) { - // Generate a WOC collision mesh from a WHM/WOT terrain pair. - // Uses terrain triangles only (no WMO overlays); useful as a - // first-pass collision build before the editor adds buildings. - std::string base = argv[++i]; - for (const char* ext : {".wot", ".whm", ".woc"}) { - if (base.size() >= 4 && base.substr(base.size() - 4) == ext) { - base = base.substr(0, base.size() - 4); - break; - } - } - if (!wowee::pipeline::WoweeTerrainLoader::exists(base)) { - std::fprintf(stderr, "WOT/WHM not found at base: %s\n", base.c_str()); - return 1; - } - wowee::pipeline::ADTTerrain terrain; - if (!wowee::pipeline::WoweeTerrainLoader::load(base, terrain)) { - std::fprintf(stderr, "Failed to load terrain: %s\n", base.c_str()); - return 1; - } - auto col = wowee::pipeline::WoweeCollisionBuilder::fromTerrain(terrain); - std::string outPath = base + ".woc"; - if (!wowee::pipeline::WoweeCollisionBuilder::save(col, outPath)) { - std::fprintf(stderr, "WOC save failed: %s\n", outPath.c_str()); - return 1; - } - std::printf("WOC built: %s (%zu triangles, %zu walkable, %zu steep)\n", - outPath.c_str(), - col.triangles.size(), col.walkableCount(), col.steepCount()); - return 0; } else if (std::strcmp(argv[i], "--export-zone-deps-md") == 0 && i + 1 < argc) { // Markdown counterpart to --list-zone-deps. Writes a sortable // GitHub-rendered table of every external model the zone