diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 18c9848b..bfe172e4 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -413,6 +413,10 @@ static void printUsage(const char* argv0) { std::printf(" Append one quest to /quests.json and exit\n"); std::printf(" --add-quest-objective [count]\n"); std::printf(" Append one objective to a quest by index\n"); + std::printf(" --add-quest-reward-item [more...]\n"); + std::printf(" Append item reward(s) to a quest's reward.itemRewards list\n"); + std::printf(" --set-quest-reward [--xp N] [--gold N] [--silver N] [--copper N]\n"); + std::printf(" Update XP/coin reward fields on a quest by index\n"); std::printf(" --remove-creature \n"); std::printf(" Remove creature at given 0-based index from /creatures.json\n"); std::printf(" --remove-object \n"); @@ -507,7 +511,7 @@ int main(int argc, char* argv[]) { "--validate", "--validate-wom", "--validate-wob", "--validate-woc", "--validate-whm", "--validate-all", "--zone-summary", "--scaffold-zone", "--add-creature", "--add-object", "--add-quest", - "--add-quest-objective", + "--add-quest-objective", "--add-quest-reward-item", "--set-quest-reward", "--remove-creature", "--remove-object", "--remove-quest", "--copy-zone", "--build-woc", "--regen-collision", "--fix-zone", @@ -554,6 +558,16 @@ int main(int argc, char* argv[]) { "--add-quest-objective requires \n"); return 1; } + if (std::strcmp(argv[i], "--add-quest-reward-item") == 0 && i + 3 >= argc) { + std::fprintf(stderr, + "--add-quest-reward-item requires \n"); + return 1; + } + if (std::strcmp(argv[i], "--set-quest-reward") == 0 && i + 2 >= argc) { + std::fprintf(stderr, + "--set-quest-reward requires [--xp N] [--gold N] [--silver N] [--copper N]\n"); + return 1; + } if (std::strcmp(argv[i], "--copy-zone") == 0 && i + 2 >= argc) { std::fprintf(stderr, "--copy-zone requires \n"); @@ -2794,6 +2808,124 @@ int main(int argc, char* argv[]) { obj.description.c_str(), idx, q->title.c_str(), q->objectives.size()); return 0; + } else if (std::strcmp(argv[i], "--add-quest-reward-item") == 0 && i + 3 < argc) { + // Append one or more item rewards to a quest. Multiple paths + // can be passed in a single invocation: + // --add-quest-reward-item zone 0 'Item:Sword' 'Item:Shield' + std::string zoneDir = argv[++i]; + std::string idxStr = argv[++i]; + std::string path = zoneDir + "/quests.json"; + if (!std::filesystem::exists(path)) { + std::fprintf(stderr, "add-quest-reward-item: %s not found\n", path.c_str()); + return 1; + } + int idx; + try { idx = std::stoi(idxStr); } + catch (...) { + std::fprintf(stderr, "add-quest-reward-item: bad questIdx '%s'\n", idxStr.c_str()); + return 1; + } + wowee::editor::QuestEditor qe; + if (!qe.loadFromFile(path)) { + std::fprintf(stderr, "add-quest-reward-item: failed to load %s\n", path.c_str()); + return 1; + } + if (idx < 0 || idx >= static_cast(qe.questCount())) { + std::fprintf(stderr, + "add-quest-reward-item: questIdx %d out of range [0, %zu)\n", + idx, qe.questCount()); + return 1; + } + wowee::editor::Quest* q = qe.getQuest(idx); + if (!q) return 1; + int added = 0; + // Greedy-consume any remaining args that don't start with '-' + // so the caller can batch-add a whole loot table in one shot. + while (i + 1 < argc && argv[i + 1][0] != '-') { + q->reward.itemRewards.push_back(argv[++i]); + added++; + } + if (added == 0) { + std::fprintf(stderr, "add-quest-reward-item: need at least one itemPath\n"); + return 1; + } + if (!qe.saveToFile(path)) { + std::fprintf(stderr, "add-quest-reward-item: failed to write %s\n", path.c_str()); + return 1; + } + std::printf("Added %d item reward(s) to quest %d ('%s'), now %zu total\n", + added, idx, q->title.c_str(), q->reward.itemRewards.size()); + return 0; + } else if (std::strcmp(argv[i], "--set-quest-reward") == 0 && i + 2 < argc) { + // Update XP / coin reward fields on an existing quest. Each + // field is optional — only the ones explicitly passed are + // changed. This avoids the round-trip-and-clobber footgun of + // a "replace whole reward" command. + std::string zoneDir = argv[++i]; + std::string idxStr = argv[++i]; + std::string path = zoneDir + "/quests.json"; + if (!std::filesystem::exists(path)) { + std::fprintf(stderr, "set-quest-reward: %s not found\n", path.c_str()); + return 1; + } + int idx; + try { idx = std::stoi(idxStr); } + catch (...) { + std::fprintf(stderr, "set-quest-reward: bad questIdx '%s'\n", idxStr.c_str()); + return 1; + } + wowee::editor::QuestEditor qe; + if (!qe.loadFromFile(path)) { + std::fprintf(stderr, "set-quest-reward: failed to load %s\n", path.c_str()); + return 1; + } + if (idx < 0 || idx >= static_cast(qe.questCount())) { + std::fprintf(stderr, + "set-quest-reward: questIdx %d out of range [0, %zu)\n", + idx, qe.questCount()); + return 1; + } + wowee::editor::Quest* q = qe.getQuest(idx); + if (!q) return 1; + int changed = 0; + auto consumeUint = [&](const char* flag, uint32_t& target) { + if (i + 2 < argc && std::strcmp(argv[i + 1], flag) == 0) { + try { + target = static_cast(std::stoul(argv[i + 2])); + i += 2; + changed++; + return true; + } catch (...) { + std::fprintf(stderr, "set-quest-reward: bad %s value '%s'\n", + flag, argv[i + 2]); + } + } + return false; + }; + // Loop until no more recognised flags consume their value — + // order-independent, so callers can pass --gold then --xp. + bool any = true; + while (any) { + any = false; + if (consumeUint("--xp", q->reward.xp)) any = true; + if (consumeUint("--gold", q->reward.gold)) any = true; + if (consumeUint("--silver", q->reward.silver)) any = true; + if (consumeUint("--copper", q->reward.copper)) any = true; + } + if (changed == 0) { + std::fprintf(stderr, + "set-quest-reward: no fields changed — pass --xp / --gold / --silver / --copper\n"); + return 1; + } + if (!qe.saveToFile(path)) { + std::fprintf(stderr, "set-quest-reward: failed to write %s\n", path.c_str()); + return 1; + } + std::printf("Updated %d field(s) on quest %d ('%s'): xp=%u gold=%u silver=%u copper=%u\n", + changed, idx, q->title.c_str(), + q->reward.xp, q->reward.gold, + q->reward.silver, q->reward.copper); + return 0; } else if (std::strcmp(argv[i], "--remove-creature") == 0 && i + 2 < argc) { // Remove a creature spawn by 0-based index. Pair with // --info-creatures (or your editor) to find the right index