mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 02:53:51 +00:00
refactor(editor): extract zone-creation handlers into cli_zone_create.cpp
Moves the two zone-creation handlers (--scaffold-zone,
--mvp-zone) out of main.cpp into a new cli_zone_create.{hpp,cpp}
module. Both kickstart a new authoring session by generating a
new zone directory under custom_zones/ — empty for scaffold,
populated with one of each content type (creature + object +
quest with objective + reward) for mvp. Each goes terrain +
manifest, then mvp adds the demo content positioned at the
tile center.
main.cpp shrinks by 155 lines (4,771 to 4,616).
This commit is contained in:
parent
16f9c072c7
commit
dfe997c564
4 changed files with 227 additions and 160 deletions
203
tools/editor/cli_zone_create.cpp
Normal file
203
tools/editor/cli_zone_create.cpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
#include "cli_zone_create.hpp"
|
||||
|
||||
#include "zone_manifest.hpp"
|
||||
#include "npc_spawner.hpp"
|
||||
#include "object_placer.hpp"
|
||||
#include "quest_editor.hpp"
|
||||
#include "terrain_editor.hpp"
|
||||
#include "terrain_biomes.hpp"
|
||||
#include "wowee_terrain.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
namespace editor {
|
||||
namespace cli {
|
||||
|
||||
namespace {
|
||||
|
||||
int handleScaffoldZone(int& i, int argc, char** argv) {
|
||||
// Generate a minimal valid empty zone — useful for kickstarting
|
||||
// a new authoring session without needing to launch the GUI.
|
||||
std::string rawName = argv[++i];
|
||||
int sx = 32, sy = 32;
|
||||
if (i + 2 < argc) {
|
||||
int parsedX = std::atoi(argv[i + 1]);
|
||||
int parsedY = std::atoi(argv[i + 2]);
|
||||
if (parsedX >= 0 && parsedX <= 63 &&
|
||||
parsedY >= 0 && parsedY <= 63) {
|
||||
sx = parsedX; sy = parsedY;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
// Slugify name to match unpackZone / server module rules.
|
||||
std::string slug;
|
||||
for (char c : rawName) {
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '-') {
|
||||
slug += c;
|
||||
} else if (c == ' ') {
|
||||
slug += '_';
|
||||
}
|
||||
}
|
||||
if (slug.empty()) {
|
||||
std::fprintf(stderr, "--scaffold-zone: name '%s' has no valid characters\n",
|
||||
rawName.c_str());
|
||||
return 1;
|
||||
}
|
||||
namespace fs = std::filesystem;
|
||||
std::string dir = "custom_zones/" + slug;
|
||||
if (fs::exists(dir)) {
|
||||
std::fprintf(stderr, "--scaffold-zone: directory already exists: %s\n",
|
||||
dir.c_str());
|
||||
return 1;
|
||||
}
|
||||
fs::create_directories(dir);
|
||||
|
||||
// Blank flat terrain at the requested tile.
|
||||
auto terrain = wowee::editor::TerrainEditor::createBlankTerrain(
|
||||
sx, sy, 100.0f, wowee::editor::Biome::Grassland);
|
||||
std::string base = dir + "/" + slug + "_" +
|
||||
std::to_string(sx) + "_" + std::to_string(sy);
|
||||
wowee::editor::WoweeTerrain::exportOpen(terrain, base, sx, sy);
|
||||
|
||||
// Minimal zone.json
|
||||
wowee::editor::ZoneManifest manifest;
|
||||
manifest.mapName = slug;
|
||||
manifest.displayName = rawName;
|
||||
manifest.mapId = 9000;
|
||||
manifest.baseHeight = 100.0f;
|
||||
manifest.tiles.push_back({sx, sy});
|
||||
manifest.save(dir + "/zone.json");
|
||||
|
||||
std::printf("Scaffolded zone: %s\n", dir.c_str());
|
||||
std::printf(" tile : (%d, %d)\n", sx, sy);
|
||||
std::printf(" files : %s.wot, %s.whm, zone.json\n",
|
||||
slug.c_str(), slug.c_str());
|
||||
std::printf(" next step: run editor without args, then File → Open Zone\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleMvpZone(int& i, int argc, char** argv) {
|
||||
// Quick-start: scaffold + populate one of each content type
|
||||
// (1 creature, 1 object, 1 quest with objective + reward).
|
||||
// Useful for demos, screenshot bait, smoke tests of the
|
||||
// bake/validate pipeline. The zone goes from empty to
|
||||
// 'something to look at' in one command.
|
||||
std::string rawName = argv[++i];
|
||||
int sx = 32, sy = 32;
|
||||
if (i + 2 < argc) {
|
||||
int parsedX = std::atoi(argv[i + 1]);
|
||||
int parsedY = std::atoi(argv[i + 2]);
|
||||
if (parsedX >= 0 && parsedX <= 63 &&
|
||||
parsedY >= 0 && parsedY <= 63) {
|
||||
sx = parsedX; sy = parsedY;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
// Reuse scaffold-zone's slug logic.
|
||||
std::string slug;
|
||||
for (char c : rawName) {
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '-') slug += c;
|
||||
else if (c == ' ') slug += '_';
|
||||
}
|
||||
if (slug.empty()) {
|
||||
std::fprintf(stderr,
|
||||
"mvp-zone: name '%s' has no valid characters\n",
|
||||
rawName.c_str());
|
||||
return 1;
|
||||
}
|
||||
namespace fs = std::filesystem;
|
||||
std::string dir = "custom_zones/" + slug;
|
||||
if (fs::exists(dir)) {
|
||||
std::fprintf(stderr,
|
||||
"mvp-zone: directory already exists: %s\n", dir.c_str());
|
||||
return 1;
|
||||
}
|
||||
fs::create_directories(dir);
|
||||
// Scaffold terrain.
|
||||
auto terrain = wowee::editor::TerrainEditor::createBlankTerrain(
|
||||
sx, sy, 100.0f, wowee::editor::Biome::Grassland);
|
||||
std::string base = dir + "/" + slug + "_" +
|
||||
std::to_string(sx) + "_" + std::to_string(sy);
|
||||
wowee::editor::WoweeTerrain::exportOpen(terrain, base, sx, sy);
|
||||
// Manifest.
|
||||
wowee::editor::ZoneManifest zm;
|
||||
zm.mapName = slug;
|
||||
zm.displayName = rawName;
|
||||
zm.mapId = 9000;
|
||||
zm.baseHeight = 100.0f;
|
||||
zm.tiles.push_back({sx, sy});
|
||||
zm.hasCreatures = true;
|
||||
zm.save(dir + "/zone.json");
|
||||
// Position the demo content roughly centered in the tile.
|
||||
// Tile (32, 32) is the WoW map origin; tile centers are at
|
||||
// 533.33-yard intervals from there.
|
||||
float centerX = (32.0f - sy) * 533.33333f - 266.667f;
|
||||
float centerY = (32.0f - sx) * 533.33333f - 266.667f;
|
||||
float centerZ = 100.0f;
|
||||
// Demo creature.
|
||||
wowee::editor::NpcSpawner sp;
|
||||
wowee::editor::CreatureSpawn c;
|
||||
c.name = "Demo Wolf";
|
||||
c.position = {centerX, centerY, centerZ};
|
||||
c.level = 5;
|
||||
c.health = 100;
|
||||
c.minDamage = 5; c.maxDamage = 10;
|
||||
c.displayId = 11430; // any valid id; renderer falls back if absent
|
||||
sp.getSpawns().push_back(c);
|
||||
sp.saveToFile(dir + "/creatures.json");
|
||||
// Demo object — a tree placement near the creature.
|
||||
wowee::editor::ObjectPlacer op;
|
||||
wowee::editor::PlacedObject po;
|
||||
po.type = wowee::editor::PlaceableType::M2;
|
||||
po.path = "World/Generic/Tree.m2";
|
||||
po.position = {centerX + 5.0f, centerY, centerZ};
|
||||
po.scale = 1.0f;
|
||||
op.getObjects().push_back(po);
|
||||
op.saveToFile(dir + "/objects.json");
|
||||
// Demo quest with objective + XP reward.
|
||||
wowee::editor::QuestEditor qe;
|
||||
wowee::editor::Quest q;
|
||||
q.title = "Welcome to " + rawName;
|
||||
q.requiredLevel = 1;
|
||||
q.questGiverNpcId = c.id; // self-referential so refs check passes
|
||||
q.turnInNpcId = c.id;
|
||||
q.reward.xp = 100;
|
||||
wowee::editor::QuestObjective obj;
|
||||
obj.type = wowee::editor::QuestObjectiveType::KillCreature;
|
||||
obj.targetName = "Demo Wolf";
|
||||
obj.targetCount = 1;
|
||||
obj.description = "Slay the Demo Wolf";
|
||||
q.objectives.push_back(obj);
|
||||
qe.addQuest(q);
|
||||
qe.saveToFile(dir + "/quests.json");
|
||||
std::printf("Created demo zone: %s\n", dir.c_str());
|
||||
std::printf(" tile : (%d, %d)\n", sx, sy);
|
||||
std::printf(" contents : 1 creature, 1 object, 1 quest (with objective + reward)\n");
|
||||
std::printf(" next : wowee_editor --info-zone-tree %s\n", dir.c_str());
|
||||
(void)argc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool handleZoneCreate(int& i, int argc, char** argv, int& outRc) {
|
||||
if (std::strcmp(argv[i], "--scaffold-zone") == 0 && i + 1 < argc) {
|
||||
outRc = handleScaffoldZone(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--mvp-zone") == 0 && i + 1 < argc) {
|
||||
outRc = handleMvpZone(i, argc, argv); return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace cli
|
||||
} // namespace editor
|
||||
} // namespace wowee
|
||||
19
tools/editor/cli_zone_create.hpp
Normal file
19
tools/editor/cli_zone_create.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
namespace wowee {
|
||||
namespace editor {
|
||||
namespace cli {
|
||||
|
||||
// Dispatch the zone-creation handlers. Both kickstart a new
|
||||
// authoring session by generating a new zone directory under
|
||||
// custom_zones/ — empty for --scaffold-zone, populated with
|
||||
// one of each content type for --mvp-zone.
|
||||
// --scaffold-zone minimal valid empty zone (terrain + manifest)
|
||||
// --mvp-zone scaffold + 1 creature + 1 object + 1 quest
|
||||
//
|
||||
// Returns true if matched; outRc holds the exit code.
|
||||
bool handleZoneCreate(int& i, int argc, char** argv, int& outRc);
|
||||
|
||||
} // namespace cli
|
||||
} // namespace editor
|
||||
} // namespace wowee
|
||||
|
|
@ -45,6 +45,7 @@
|
|||
#include "cli_random.hpp"
|
||||
#include "cli_items_export.hpp"
|
||||
#include "cli_items_mutate.hpp"
|
||||
#include "cli_zone_create.hpp"
|
||||
#include "content_pack.hpp"
|
||||
#include "npc_spawner.hpp"
|
||||
#include "object_placer.hpp"
|
||||
|
|
@ -517,6 +518,9 @@ int main(int argc, char* argv[]) {
|
|||
if (wowee::editor::cli::handleItemsMutate(i, argc, argv, outRc)) {
|
||||
return outRc;
|
||||
}
|
||||
if (wowee::editor::cli::handleZoneCreate(i, argc, argv, outRc)) {
|
||||
return outRc;
|
||||
}
|
||||
}
|
||||
if (std::strcmp(argv[i], "--data") == 0 && i + 1 < argc) {
|
||||
dataPath = argv[++i];
|
||||
|
|
@ -1445,166 +1449,6 @@ int main(int argc, char* argv[]) {
|
|||
outPath.c_str(),
|
||||
col.triangles.size(), col.walkableCount(), col.steepCount());
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--scaffold-zone") == 0 && i + 1 < argc) {
|
||||
// Generate a minimal valid empty zone — useful for kickstarting
|
||||
// a new authoring session without needing to launch the GUI.
|
||||
std::string rawName = argv[++i];
|
||||
int sx = 32, sy = 32;
|
||||
if (i + 2 < argc) {
|
||||
int parsedX = std::atoi(argv[i + 1]);
|
||||
int parsedY = std::atoi(argv[i + 2]);
|
||||
if (parsedX >= 0 && parsedX <= 63 &&
|
||||
parsedY >= 0 && parsedY <= 63) {
|
||||
sx = parsedX; sy = parsedY;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
// Slugify name to match unpackZone / server module rules.
|
||||
std::string slug;
|
||||
for (char c : rawName) {
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '-') {
|
||||
slug += c;
|
||||
} else if (c == ' ') {
|
||||
slug += '_';
|
||||
}
|
||||
}
|
||||
if (slug.empty()) {
|
||||
std::fprintf(stderr, "--scaffold-zone: name '%s' has no valid characters\n",
|
||||
rawName.c_str());
|
||||
return 1;
|
||||
}
|
||||
namespace fs = std::filesystem;
|
||||
std::string dir = "custom_zones/" + slug;
|
||||
if (fs::exists(dir)) {
|
||||
std::fprintf(stderr, "--scaffold-zone: directory already exists: %s\n",
|
||||
dir.c_str());
|
||||
return 1;
|
||||
}
|
||||
fs::create_directories(dir);
|
||||
|
||||
// Blank flat terrain at the requested tile.
|
||||
auto terrain = wowee::editor::TerrainEditor::createBlankTerrain(
|
||||
sx, sy, 100.0f, wowee::editor::Biome::Grassland);
|
||||
std::string base = dir + "/" + slug + "_" +
|
||||
std::to_string(sx) + "_" + std::to_string(sy);
|
||||
wowee::editor::WoweeTerrain::exportOpen(terrain, base, sx, sy);
|
||||
|
||||
// Minimal zone.json
|
||||
wowee::editor::ZoneManifest manifest;
|
||||
manifest.mapName = slug;
|
||||
manifest.displayName = rawName;
|
||||
manifest.mapId = 9000;
|
||||
manifest.baseHeight = 100.0f;
|
||||
manifest.tiles.push_back({sx, sy});
|
||||
manifest.save(dir + "/zone.json");
|
||||
|
||||
std::printf("Scaffolded zone: %s\n", dir.c_str());
|
||||
std::printf(" tile : (%d, %d)\n", sx, sy);
|
||||
std::printf(" files : %s.wot, %s.whm, zone.json\n",
|
||||
slug.c_str(), slug.c_str());
|
||||
std::printf(" next step: run editor without args, then File → Open Zone\n");
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--mvp-zone") == 0 && i + 1 < argc) {
|
||||
// Quick-start: scaffold + populate one of each content type
|
||||
// (1 creature, 1 object, 1 quest with objective + reward).
|
||||
// Useful for demos, screenshot bait, smoke tests of the
|
||||
// bake/validate pipeline. The zone goes from empty to
|
||||
// 'something to look at' in one command.
|
||||
std::string rawName = argv[++i];
|
||||
int sx = 32, sy = 32;
|
||||
if (i + 2 < argc) {
|
||||
int parsedX = std::atoi(argv[i + 1]);
|
||||
int parsedY = std::atoi(argv[i + 2]);
|
||||
if (parsedX >= 0 && parsedX <= 63 &&
|
||||
parsedY >= 0 && parsedY <= 63) {
|
||||
sx = parsedX; sy = parsedY;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
// Reuse scaffold-zone's slug logic.
|
||||
std::string slug;
|
||||
for (char c : rawName) {
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '-') slug += c;
|
||||
else if (c == ' ') slug += '_';
|
||||
}
|
||||
if (slug.empty()) {
|
||||
std::fprintf(stderr,
|
||||
"mvp-zone: name '%s' has no valid characters\n",
|
||||
rawName.c_str());
|
||||
return 1;
|
||||
}
|
||||
namespace fs = std::filesystem;
|
||||
std::string dir = "custom_zones/" + slug;
|
||||
if (fs::exists(dir)) {
|
||||
std::fprintf(stderr,
|
||||
"mvp-zone: directory already exists: %s\n", dir.c_str());
|
||||
return 1;
|
||||
}
|
||||
fs::create_directories(dir);
|
||||
// Scaffold terrain.
|
||||
auto terrain = wowee::editor::TerrainEditor::createBlankTerrain(
|
||||
sx, sy, 100.0f, wowee::editor::Biome::Grassland);
|
||||
std::string base = dir + "/" + slug + "_" +
|
||||
std::to_string(sx) + "_" + std::to_string(sy);
|
||||
wowee::editor::WoweeTerrain::exportOpen(terrain, base, sx, sy);
|
||||
// Manifest.
|
||||
wowee::editor::ZoneManifest zm;
|
||||
zm.mapName = slug;
|
||||
zm.displayName = rawName;
|
||||
zm.mapId = 9000;
|
||||
zm.baseHeight = 100.0f;
|
||||
zm.tiles.push_back({sx, sy});
|
||||
zm.hasCreatures = true;
|
||||
zm.save(dir + "/zone.json");
|
||||
// Position the demo content roughly centered in the tile.
|
||||
// Tile (32, 32) is the WoW map origin; tile centers are at
|
||||
// 533.33-yard intervals from there.
|
||||
float centerX = (32.0f - sy) * 533.33333f - 266.667f;
|
||||
float centerY = (32.0f - sx) * 533.33333f - 266.667f;
|
||||
float centerZ = 100.0f;
|
||||
// Demo creature.
|
||||
wowee::editor::NpcSpawner sp;
|
||||
wowee::editor::CreatureSpawn c;
|
||||
c.name = "Demo Wolf";
|
||||
c.position = {centerX, centerY, centerZ};
|
||||
c.level = 5;
|
||||
c.health = 100;
|
||||
c.minDamage = 5; c.maxDamage = 10;
|
||||
c.displayId = 11430; // any valid id; renderer falls back if absent
|
||||
sp.getSpawns().push_back(c);
|
||||
sp.saveToFile(dir + "/creatures.json");
|
||||
// Demo object — a tree placement near the creature.
|
||||
wowee::editor::ObjectPlacer op;
|
||||
wowee::editor::PlacedObject po;
|
||||
po.type = wowee::editor::PlaceableType::M2;
|
||||
po.path = "World/Generic/Tree.m2";
|
||||
po.position = {centerX + 5.0f, centerY, centerZ};
|
||||
po.scale = 1.0f;
|
||||
op.getObjects().push_back(po);
|
||||
op.saveToFile(dir + "/objects.json");
|
||||
// Demo quest with objective + XP reward.
|
||||
wowee::editor::QuestEditor qe;
|
||||
wowee::editor::Quest q;
|
||||
q.title = "Welcome to " + rawName;
|
||||
q.requiredLevel = 1;
|
||||
q.questGiverNpcId = c.id; // self-referential so refs check passes
|
||||
q.turnInNpcId = c.id;
|
||||
q.reward.xp = 100;
|
||||
wowee::editor::QuestObjective obj;
|
||||
obj.type = wowee::editor::QuestObjectiveType::KillCreature;
|
||||
obj.targetName = "Demo Wolf";
|
||||
obj.targetCount = 1;
|
||||
obj.description = "Slay the Demo Wolf";
|
||||
q.objectives.push_back(obj);
|
||||
qe.addQuest(q);
|
||||
qe.saveToFile(dir + "/quests.json");
|
||||
std::printf("Created demo zone: %s\n", dir.c_str());
|
||||
std::printf(" tile : (%d, %d)\n", sx, sy);
|
||||
std::printf(" contents : 1 creature, 1 object, 1 quest (with objective + reward)\n");
|
||||
std::printf(" next : wowee_editor --info-zone-tree %s\n", dir.c_str());
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--add-tile") == 0 && i + 3 < argc) {
|
||||
// Extend an existing zone with another ADT tile. Zones can
|
||||
// span multiple tiles (e.g. a continent fragment), but
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue