mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 02:53:51 +00:00
feat(editor): add WTAX JSON round-trip authoring workflow
Closes the WTAX open-format loop with --export-wtax-json /
--import-wtax-json, mirroring the JSON pairs added for
every other novel binary format. All 18 binary formats
added since WOL now have full JSON round-trip authoring.
Two top-level arrays mirror the binary layout:
• nodes[] — nodeId / mapId / name / iconPath / position[3] /
factionAlliance + factionHorde restrictions
• paths[] — pathId / fromNodeId / toNodeId / moneyCostCopper /
waypoints[] each with position[3] + delaySec
Vec3 fields become 3-element JSON arrays for natural
hand-edit. The intra-format graph (paths reference nodeIds)
round-trips exactly so the catalog's connectivity is
preserved.
Verified byte-identical round-trip on the continent preset
(6 nodes + 8 paths covering hub-and-spoke + 3 perimeter
shortcuts, 16 total waypoints).
Adds 2 flags (573 documented total now).
This commit is contained in:
parent
99baf2d0c4
commit
cc4b9a6fad
3 changed files with 157 additions and 0 deletions
|
|
@ -70,6 +70,7 @@ const char* const kArgRequired[] = {
|
|||
"--export-wgsp-json", "--import-wgsp-json",
|
||||
"--gen-taxi", "--gen-taxi-region", "--gen-taxi-continent",
|
||||
"--info-wtax", "--validate-wtax",
|
||||
"--export-wtax-json", "--import-wtax-json",
|
||||
"--gen-talents", "--gen-talents-warrior", "--gen-talents-mage",
|
||||
"--info-wtal", "--validate-wtal",
|
||||
"--gen-weather-temperate", "--gen-weather-arctic",
|
||||
|
|
|
|||
|
|
@ -1047,6 +1047,10 @@ void printUsage(const char* argv0) {
|
|||
std::printf(" Print WTAX nodes (id / map / position / name) + paths (id / from->to / cost / waypoint count)\n");
|
||||
std::printf(" --validate-wtax <wtax-base> [--json]\n");
|
||||
std::printf(" Static checks: ids>0+unique, finite positions, paths reference real nodes, no self-loop, non-negative delays\n");
|
||||
std::printf(" --export-wtax-json <wtax-base> [out.json]\n");
|
||||
std::printf(" Export binary .wtax to a human-editable JSON sidecar (defaults to <base>.wtax.json)\n");
|
||||
std::printf(" --import-wtax-json <json-path> [out-base]\n");
|
||||
std::printf(" Import a .wtax.json sidecar back into binary .wtax (round-trip with --export-wtax-json)\n");
|
||||
std::printf(" --gen-talents <wtal-base> [name]\n");
|
||||
std::printf(" Emit .wtal starter: 1 small tree (3 talents in chain) for class warrior\n");
|
||||
std::printf(" --gen-talents-warrior <wtal-base> [name]\n");
|
||||
|
|
|
|||
|
|
@ -159,6 +159,152 @@ int handleInfo(int& i, int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int handleExportJson(int& i, int argc, char** argv) {
|
||||
// Mirrors the JSON pairs added for every other novel
|
||||
// open format. Two top-level arrays (nodes / paths)
|
||||
// mirroring the binary layout. Vec3 fields become
|
||||
// 3-element JSON arrays.
|
||||
std::string base = argv[++i];
|
||||
std::string outPath;
|
||||
if (parseOptArg(i, argc, argv)) outPath = argv[++i];
|
||||
base = stripWtaxExt(base);
|
||||
if (outPath.empty()) outPath = base + ".wtax.json";
|
||||
if (!wowee::pipeline::WoweeTaxiLoader::exists(base)) {
|
||||
std::fprintf(stderr,
|
||||
"export-wtax-json: WTAX not found: %s.wtax\n", base.c_str());
|
||||
return 1;
|
||||
}
|
||||
auto c = wowee::pipeline::WoweeTaxiLoader::load(base);
|
||||
nlohmann::json j;
|
||||
j["name"] = c.name;
|
||||
nlohmann::json na = nlohmann::json::array();
|
||||
for (const auto& n : c.nodes) {
|
||||
na.push_back({
|
||||
{"nodeId", n.nodeId},
|
||||
{"mapId", n.mapId},
|
||||
{"name", n.name},
|
||||
{"iconPath", n.iconPath},
|
||||
{"position", {n.position.x, n.position.y, n.position.z}},
|
||||
{"factionAlliance", n.factionAlliance},
|
||||
{"factionHorde", n.factionHorde},
|
||||
});
|
||||
}
|
||||
j["nodes"] = na;
|
||||
nlohmann::json pa = nlohmann::json::array();
|
||||
for (const auto& p : c.paths) {
|
||||
nlohmann::json wpa = nlohmann::json::array();
|
||||
for (const auto& w : p.waypoints) {
|
||||
wpa.push_back({
|
||||
{"position", {w.position.x, w.position.y, w.position.z}},
|
||||
{"delaySec", w.delaySec},
|
||||
});
|
||||
}
|
||||
pa.push_back({
|
||||
{"pathId", p.pathId},
|
||||
{"fromNodeId", p.fromNodeId},
|
||||
{"toNodeId", p.toNodeId},
|
||||
{"moneyCostCopper", p.moneyCostCopper},
|
||||
{"waypoints", wpa},
|
||||
});
|
||||
}
|
||||
j["paths"] = pa;
|
||||
std::ofstream out(outPath);
|
||||
if (!out) {
|
||||
std::fprintf(stderr,
|
||||
"export-wtax-json: cannot write %s\n", outPath.c_str());
|
||||
return 1;
|
||||
}
|
||||
out << j.dump(2) << "\n";
|
||||
out.close();
|
||||
std::printf("Wrote %s\n", outPath.c_str());
|
||||
std::printf(" source : %s.wtax\n", base.c_str());
|
||||
std::printf(" nodes : %zu paths : %zu\n",
|
||||
c.nodes.size(), c.paths.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleImportJson(int& i, int argc, char** argv) {
|
||||
std::string jsonPath = argv[++i];
|
||||
std::string outBase;
|
||||
if (parseOptArg(i, argc, argv)) outBase = argv[++i];
|
||||
if (outBase.empty()) {
|
||||
outBase = jsonPath;
|
||||
std::string suffix = ".wtax.json";
|
||||
if (outBase.size() > suffix.size() &&
|
||||
outBase.substr(outBase.size() - suffix.size()) == suffix) {
|
||||
outBase = outBase.substr(0, outBase.size() - suffix.size());
|
||||
} else if (outBase.size() > 5 &&
|
||||
outBase.substr(outBase.size() - 5) == ".json") {
|
||||
outBase = outBase.substr(0, outBase.size() - 5);
|
||||
}
|
||||
}
|
||||
outBase = stripWtaxExt(outBase);
|
||||
std::ifstream in(jsonPath);
|
||||
if (!in) {
|
||||
std::fprintf(stderr,
|
||||
"import-wtax-json: cannot read %s\n", jsonPath.c_str());
|
||||
return 1;
|
||||
}
|
||||
nlohmann::json j;
|
||||
try { in >> j; }
|
||||
catch (const std::exception& e) {
|
||||
std::fprintf(stderr,
|
||||
"import-wtax-json: bad JSON in %s: %s\n",
|
||||
jsonPath.c_str(), e.what());
|
||||
return 1;
|
||||
}
|
||||
auto readVec3 = [](const nlohmann::json& jv, glm::vec3& v) {
|
||||
if (jv.is_array() && jv.size() >= 3) {
|
||||
v.x = jv[0].get<float>();
|
||||
v.y = jv[1].get<float>();
|
||||
v.z = jv[2].get<float>();
|
||||
}
|
||||
};
|
||||
wowee::pipeline::WoweeTaxi c;
|
||||
c.name = j.value("name", std::string{});
|
||||
if (j.contains("nodes") && j["nodes"].is_array()) {
|
||||
for (const auto& jn : j["nodes"]) {
|
||||
wowee::pipeline::WoweeTaxi::Node n;
|
||||
n.nodeId = jn.value("nodeId", 0u);
|
||||
n.mapId = jn.value("mapId", 0u);
|
||||
n.name = jn.value("name", std::string{});
|
||||
n.iconPath = jn.value("iconPath", std::string{});
|
||||
if (jn.contains("position")) readVec3(jn["position"], n.position);
|
||||
n.factionAlliance = jn.value("factionAlliance", 0u);
|
||||
n.factionHorde = jn.value("factionHorde", 0u);
|
||||
c.nodes.push_back(n);
|
||||
}
|
||||
}
|
||||
if (j.contains("paths") && j["paths"].is_array()) {
|
||||
for (const auto& jp : j["paths"]) {
|
||||
wowee::pipeline::WoweeTaxi::Path p;
|
||||
p.pathId = jp.value("pathId", 0u);
|
||||
p.fromNodeId = jp.value("fromNodeId", 0u);
|
||||
p.toNodeId = jp.value("toNodeId", 0u);
|
||||
p.moneyCostCopper = jp.value("moneyCostCopper", 0u);
|
||||
if (jp.contains("waypoints") && jp["waypoints"].is_array()) {
|
||||
for (const auto& jw : jp["waypoints"]) {
|
||||
wowee::pipeline::WoweeTaxi::Waypoint w;
|
||||
if (jw.contains("position")) readVec3(jw["position"], w.position);
|
||||
w.delaySec = jw.value("delaySec", 0.0f);
|
||||
p.waypoints.push_back(w);
|
||||
}
|
||||
}
|
||||
c.paths.push_back(p);
|
||||
}
|
||||
}
|
||||
if (!wowee::pipeline::WoweeTaxiLoader::save(c, outBase)) {
|
||||
std::fprintf(stderr,
|
||||
"import-wtax-json: failed to save %s.wtax\n", outBase.c_str());
|
||||
return 1;
|
||||
}
|
||||
std::printf("Wrote %s.wtax\n", outBase.c_str());
|
||||
std::printf(" source : %s\n", jsonPath.c_str());
|
||||
std::printf(" nodes : %zu paths : %zu\n",
|
||||
c.nodes.size(), c.paths.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleValidate(int& i, int argc, char** argv) {
|
||||
std::string base = argv[++i];
|
||||
bool jsonOut = consumeJsonFlag(i, argc, argv);
|
||||
|
|
@ -293,6 +439,12 @@ bool handleTaxiCatalog(int& i, int argc, char** argv, int& outRc) {
|
|||
if (std::strcmp(argv[i], "--validate-wtax") == 0 && i + 1 < argc) {
|
||||
outRc = handleValidate(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--export-wtax-json") == 0 && i + 1 < argc) {
|
||||
outRc = handleExportJson(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--import-wtax-json") == 0 && i + 1 < argc) {
|
||||
outRc = handleImportJson(i, argc, argv); return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue