Add --set flag to edit any resource or prestige currency directly

Supports prestige currencies (Plasmid, Phage, Dark, etc.) which live
outside the resource section. Also adds interactive menu option.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Julian Tabel
2026-05-18 13:59:33 +02:00
parent a75fdd72f5
commit cd58c0db9d

View File

@@ -168,16 +168,55 @@ function copyToClipboard(text) {
} }
} }
function setPrestige(data, key, value) {
for (const [k, entry] of Object.entries(data.prestige || {})) {
if (k.toLowerCase() === key.toLowerCase()) {
const old = entry.count ?? 0;
entry.count = value;
return ` ${k}: ${old} -> ${value}`;
}
}
return null;
}
function setResource(data, key, value) {
// Check prestige currencies first
const prestigeResult = setPrestige(data, key, value);
if (prestigeResult) return prestigeResult;
const res = (data.resource || {})[key];
if (!res) {
for (const [k, r] of Object.entries(data.resource || {})) {
if (k.toLowerCase() === key.toLowerCase() || (r.name || "").toLowerCase() === key.toLowerCase()) {
const old = r.amount ?? 0;
r.amount = value;
return ` ${r.name || k}: ${Math.floor(old)} -> ${value}`;
}
}
return null;
}
const old = res.amount ?? 0;
res.amount = value;
return ` ${res.name || key}: ${Math.floor(old)} -> ${value}`;
}
function main() { function main() {
const args = process.argv.slice(2); const args = process.argv.slice(2);
const flags = new Set(); const flags = new Set();
const positional = []; const positional = [];
let craftedValue = null; let craftedValue = null;
let onlyFilter = null; let onlyFilter = null;
const setValues = []; // [{key, value}]
for (const arg of args) { for (const arg of args) {
if (arg.startsWith("--set-crafted=")) { if (arg.startsWith("--set-crafted=")) {
craftedValue = Number(arg.split("=")[1]); craftedValue = Number(arg.split("=")[1]);
} else if (arg.startsWith("--set=")) {
const parts = arg.slice("--set=".length);
const eqIdx = parts.lastIndexOf("=");
if (eqIdx > 0) {
setValues.push({ key: parts.slice(0, eqIdx), value: Number(parts.slice(eqIdx + 1)) });
}
} else if (arg.startsWith("--only=")) { } else if (arg.startsWith("--only=")) {
onlyFilter = new Set(arg.slice("--only=".length).split(",").map(s => s.trim().toLowerCase())); onlyFilter = new Set(arg.slice("--only=".length).split(",").map(s => s.trim().toLowerCase()));
} else if (arg.startsWith("--")) { } else if (arg.startsWith("--")) {
@@ -187,7 +226,7 @@ function main() {
} }
} }
const hasAction = flags.has("--max-resources") || flags.has("--max-time") || flags.has("--max-geology") || flags.has("--max-soldiers") || flags.has("--list") || craftedValue !== null; const hasAction = flags.has("--max-resources") || flags.has("--max-time") || flags.has("--max-geology") || flags.has("--max-soldiers") || flags.has("--list") || craftedValue !== null || setValues.length > 0;
if (flags.has("--help")) { if (flags.has("--help")) {
console.log(`Usage: node index.js [options] [save-string] console.log(`Usage: node index.js [options] [save-string]
@@ -197,6 +236,7 @@ Running with no flags launches interactive mode.
Options: Options:
--list List all resources in the save with current values --list List all resources in the save with current values
--max-resources Set all capped resources to their max --max-resources Set all capped resources to their max
--set=key=N Set a specific resource to N (e.g. --set=Plasmid=100)
--set-crafted=N Set all unlimited (crafted) resources to N --set-crafted=N Set all unlimited (crafted) resources to N
--only=a,b,c Only affect listed resources (comma-separated names or keys) --only=a,b,c Only affect listed resources (comma-separated names or keys)
--max-soldiers Fill garrison to max and heal all wounded --max-soldiers Fill garrison to max and heal all wounded
@@ -256,6 +296,21 @@ Save string can be passed as argument, piped via stdin, or read from clipboard.`
} }
} }
if (setValues.length > 0) {
for (const { key, value } of setValues) {
if (isNaN(value)) {
console.error(`Invalid value for ${key}. Must be a number.`);
continue;
}
const result = setResource(data, key, value);
if (result) {
console.log(`Set resource:\n${result}`);
} else {
console.error(`Resource "${key}" not found in save.`);
}
}
}
if (craftedValue !== null) { if (craftedValue !== null) {
const changed = setCraftedResources(data, craftedValue, onlyFilter); const changed = setCraftedResources(data, craftedValue, onlyFilter);
if (changed.length > 0) { if (changed.length > 0) {
@@ -345,6 +400,7 @@ async function interactiveMode() {
message: "What would you like to do?", message: "What would you like to do?",
choices: [ choices: [
{ value: "max-resources", name: "Max capped resources" }, { value: "max-resources", name: "Max capped resources" },
{ value: "set-resource", name: "Set a specific resource" },
{ value: "set-crafted", name: "Set crafted resources" }, { value: "set-crafted", name: "Set crafted resources" },
{ value: "max-soldiers", name: "Max soldiers & heal wounded" }, { value: "max-soldiers", name: "Max soldiers & heal wounded" },
{ value: "max-geology", name: "Max geology bonuses" }, { value: "max-geology", name: "Max geology bonuses" },
@@ -427,6 +483,47 @@ async function interactiveMode() {
continue; continue;
} }
if (action === "set-resource") {
const prestige = Object.entries(data.prestige || {})
.filter(([, p]) => (p.count ?? 0) > 0)
.map(([key, p]) => ({
value: `prestige:${key}`,
name: `${key} (${p.count}) [prestige]`,
}));
const resources = Object.entries(data.resource || {})
.filter(([, r]) => (r.amount ?? 0) > 0 || (r.max ?? 0) !== 0)
.map(([key, r]) => ({
value: `resource:${key}`,
name: `${r.name || key} (${Math.floor(r.amount ?? 0)}${r.max > 0 ? ` / ${r.max}` : ""})`,
}));
const choices = [...prestige, ...resources];
if (choices.length === 0) {
console.log("No resources found.");
continue;
}
const selected = await select({
message: "Which resource?",
choices,
});
const [type, resKey] = selected.split(":");
const label = type === "prestige" ? resKey : (data.resource[resKey].name || resKey);
const value = await input({
message: `Set ${label} to what value?`,
validate: (v) => {
const n = Number(v);
return !isNaN(n) && n >= 0 ? true : "Enter a non-negative number";
},
});
const result = type === "prestige"
? setPrestige(data, resKey, Number(value))
: setResource(data, resKey, Number(value));
if (result) {
console.log(`Set resource:\n${result}`);
modified = true;
}
continue;
}
if (action === "set-crafted") { if (action === "set-crafted") {
const choices = getResourceChoices(data, "crafted"); const choices = getResourceChoices(data, "crafted");
if (choices.length === 0) { if (choices.length === 0) {