mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-09 02:23:52 +00:00
feat(editor): add --copy-zone-items for cross-zone item reuse
Copies items from one zone to another. Two modes: Default (replace): destination items.json becomes a verbatim copy of the source. Useful when zoneB is a fresh copy of zoneA's loot table. --merge: appends source items to existing destination items, but preserves the destination's existing IDs by re-id'ing source entries on collision. The destination's items take precedence; the source's entries get fresh IDs picked from the smallest unused positive integer. Verified: merge with id-1 collision (dest has Boot id=1; source has Sword id=1, Helm id=2) → Sword becomes id=2, then Helm becomes id=3 because 2 was just claimed; "re-ided: 2 (id collisions)" reported. Replace mode wholesale-overwrites as expected. Brings command count to 223.
This commit is contained in:
parent
a3253eebc6
commit
05645b2a79
1 changed files with 101 additions and 0 deletions
|
|
@ -568,6 +568,8 @@ static void printUsage(const char* argv0) {
|
||||||
std::printf(" Edit fields on an existing item in place; only specified flags are changed\n");
|
std::printf(" Edit fields on an existing item in place; only specified flags are changed\n");
|
||||||
std::printf(" --remove-item <zoneDir> <index>\n");
|
std::printf(" --remove-item <zoneDir> <index>\n");
|
||||||
std::printf(" Remove item at given 0-based index from <zoneDir>/items.json\n");
|
std::printf(" Remove item at given 0-based index from <zoneDir>/items.json\n");
|
||||||
|
std::printf(" --copy-zone-items <fromZoneDir> <toZoneDir> [--merge]\n");
|
||||||
|
std::printf(" Copy items from one zone to another (default replaces; --merge appends with re-id)\n");
|
||||||
std::printf(" --clone-item <zoneDir> <index> [newName]\n");
|
std::printf(" --clone-item <zoneDir> <index> [newName]\n");
|
||||||
std::printf(" Duplicate the item at index, assign next free id (and optional name override)\n");
|
std::printf(" Duplicate the item at index, assign next free id (and optional name override)\n");
|
||||||
std::printf(" --validate-items <zoneDir>\n");
|
std::printf(" --validate-items <zoneDir>\n");
|
||||||
|
|
@ -1000,6 +1002,7 @@ int main(int argc, char* argv[]) {
|
||||||
"--info-project-items",
|
"--info-project-items",
|
||||||
"--clone-object",
|
"--clone-object",
|
||||||
"--remove-creature", "--remove-object", "--remove-quest", "--remove-item",
|
"--remove-creature", "--remove-object", "--remove-quest", "--remove-item",
|
||||||
|
"--copy-zone-items",
|
||||||
"--copy-zone", "--rename-zone", "--remove-zone",
|
"--copy-zone", "--rename-zone", "--remove-zone",
|
||||||
"--clear-zone-content", "--strip-zone", "--strip-project",
|
"--clear-zone-content", "--strip-zone", "--strip-project",
|
||||||
"--repair-zone", "--repair-project",
|
"--repair-zone", "--repair-project",
|
||||||
|
|
@ -13549,6 +13552,104 @@ int main(int argc, char* argv[]) {
|
||||||
removedName.c_str(), removedId,
|
removedName.c_str(), removedId,
|
||||||
path.c_str(), items.size());
|
path.c_str(), items.size());
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (std::strcmp(argv[i], "--copy-zone-items") == 0 && i + 2 < argc) {
|
||||||
|
// Copy items from one zone to another. Default mode
|
||||||
|
// replaces the destination items.json wholesale; --merge
|
||||||
|
// appends each source item to the existing destination
|
||||||
|
// list, re-id'ing on collision so the destination's
|
||||||
|
// existing IDs are preserved and the source's new
|
||||||
|
// entries get fresh ones.
|
||||||
|
std::string fromZone = argv[++i];
|
||||||
|
std::string toZone = argv[++i];
|
||||||
|
bool mergeMode = false;
|
||||||
|
if (i + 1 < argc && std::strcmp(argv[i + 1], "--merge") == 0) {
|
||||||
|
mergeMode = true; i++;
|
||||||
|
}
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
std::string srcPath = fromZone + "/items.json";
|
||||||
|
if (!fs::exists(srcPath)) {
|
||||||
|
std::fprintf(stderr,
|
||||||
|
"copy-zone-items: %s has no items.json\n", fromZone.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!fs::exists(toZone) || !fs::is_directory(toZone)) {
|
||||||
|
std::fprintf(stderr,
|
||||||
|
"copy-zone-items: dest %s is not a directory\n",
|
||||||
|
toZone.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
nlohmann::json src;
|
||||||
|
try {
|
||||||
|
std::ifstream in(srcPath);
|
||||||
|
in >> src;
|
||||||
|
} catch (...) {
|
||||||
|
std::fprintf(stderr,
|
||||||
|
"copy-zone-items: %s is not valid JSON\n", srcPath.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!src.contains("items") || !src["items"].is_array()) {
|
||||||
|
std::fprintf(stderr,
|
||||||
|
"copy-zone-items: %s has no 'items' array\n",
|
||||||
|
srcPath.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string dstPath = toZone + "/items.json";
|
||||||
|
nlohmann::json dst = nlohmann::json::object({{"items",
|
||||||
|
nlohmann::json::array()}});
|
||||||
|
int copied = 0, reIded = 0;
|
||||||
|
if (mergeMode && fs::exists(dstPath)) {
|
||||||
|
try {
|
||||||
|
std::ifstream in(dstPath);
|
||||||
|
in >> dst;
|
||||||
|
} catch (...) {}
|
||||||
|
if (!dst.contains("items") || !dst["items"].is_array()) {
|
||||||
|
dst["items"] = nlohmann::json::array();
|
||||||
|
}
|
||||||
|
std::set<uint32_t> usedIds;
|
||||||
|
for (const auto& it : dst["items"]) {
|
||||||
|
if (it.contains("id") && it["id"].is_number_unsigned()) {
|
||||||
|
usedIds.insert(it["id"].get<uint32_t>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& it : src["items"]) {
|
||||||
|
nlohmann::json newItem = it;
|
||||||
|
uint32_t srcId = it.value("id", 0u);
|
||||||
|
if (srcId == 0 || usedIds.count(srcId)) {
|
||||||
|
// Pick the next free id.
|
||||||
|
uint32_t fresh = 1;
|
||||||
|
while (usedIds.count(fresh)) ++fresh;
|
||||||
|
newItem["id"] = fresh;
|
||||||
|
usedIds.insert(fresh);
|
||||||
|
if (srcId != 0) reIded++;
|
||||||
|
} else {
|
||||||
|
usedIds.insert(srcId);
|
||||||
|
}
|
||||||
|
dst["items"].push_back(newItem);
|
||||||
|
copied++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Replace mode: destination becomes a verbatim copy of
|
||||||
|
// the source items array.
|
||||||
|
dst["items"] = src["items"];
|
||||||
|
copied = static_cast<int>(src["items"].size());
|
||||||
|
}
|
||||||
|
std::ofstream out(dstPath);
|
||||||
|
if (!out) {
|
||||||
|
std::fprintf(stderr,
|
||||||
|
"copy-zone-items: failed to write %s\n", dstPath.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
out << dst.dump(2);
|
||||||
|
out.close();
|
||||||
|
std::printf("Copied %d item(s) from %s to %s\n",
|
||||||
|
copied, fromZone.c_str(), toZone.c_str());
|
||||||
|
std::printf(" mode : %s\n",
|
||||||
|
mergeMode ? "merge (append + re-id)" : "replace");
|
||||||
|
std::printf(" dst total : %zu\n", dst["items"].size());
|
||||||
|
if (reIded > 0) {
|
||||||
|
std::printf(" re-ided : %d (id collisions)\n", reIded);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
} else if (std::strcmp(argv[i], "--clone-item") == 0 && i + 2 < argc) {
|
} else if (std::strcmp(argv[i], "--clone-item") == 0 && i + 2 < argc) {
|
||||||
// Duplicate the item at given 0-based index. Auto-assigns
|
// Duplicate the item at given 0-based index. Auto-assigns
|
||||||
// the smallest unused positive id; optional <newName>
|
// the smallest unused positive id; optional <newName>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue