Compare commits
1 Commits
main
...
d8c0db09f7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8c0db09f7 |
@@ -1 +1 @@
|
|||||||
nodejs 24.13.1
|
nodejs 24.14.0
|
||||||
|
|||||||
@@ -25,10 +25,6 @@ Saves are LZString `compressToBase64` of a JSON object (not plain base64). Uses
|
|||||||
|
|
||||||
Key top-level sections: `resource`, `race`, `tech`, `city`, `space`, `civic`, `genes`, `prestige`, `settings`, `arpa`.
|
Key top-level sections: `resource`, `race`, `tech`, `city`, `space`, `civic`, `genes`, `prestige`, `settings`, `arpa`.
|
||||||
|
|
||||||
### Prestige structure (`prestige.<name>`)
|
|
||||||
- `count`: current value
|
|
||||||
- Prestige currencies: `Plasmid`, `AntiPlasmid`, `Phage`, `Dark`, `Harmony`, `AICore`, `Artifact`, `Blood_Stone`, `Supercoiled`
|
|
||||||
|
|
||||||
### Resource structure (`resource.<name>`)
|
### Resource structure (`resource.<name>`)
|
||||||
- `amount`: current value
|
- `amount`: current value
|
||||||
- `max`: storage cap. `> 0` = capped, `-1` = unlimited (crafted), `-2` = special/uncapped, `0` = not unlocked
|
- `max`: storage cap. `> 0` = capped, `-1` = unlimited (crafted), `-2` = special/uncapped, `0` = not unlocked
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -31,17 +31,11 @@ node index.js --list
|
|||||||
# Max all capped resources
|
# Max all capped resources
|
||||||
node index.js --max-resources
|
node index.js --max-resources
|
||||||
|
|
||||||
# Set a specific resource or prestige currency to a value
|
|
||||||
node index.js --set=Plasmid=5000
|
|
||||||
|
|
||||||
# Set multiple at once
|
|
||||||
node index.js --set=Plasmid=5000 --set=Phage=3000
|
|
||||||
|
|
||||||
# Set all crafted (unlimited) resources to 100,000
|
# Set all crafted (unlimited) resources to 100,000
|
||||||
node index.js --set-crafted=100000
|
node index.js --set-crafted=100000
|
||||||
|
|
||||||
# Combine multiple actions
|
# Combine both
|
||||||
node index.js --max-resources --set-crafted=100000 --set=Plasmid=5000
|
node index.js --max-resources --set-crafted=100000
|
||||||
|
|
||||||
# Max only specific resources
|
# Max only specific resources
|
||||||
node index.js --max-resources --only=food,stone,iron
|
node index.js --max-resources --only=food,stone,iron
|
||||||
@@ -62,7 +56,6 @@ node index.js --max-resources --no-copy
|
|||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `--list` | List all resources in the save, grouped by type (capped, crafted, special, locked) |
|
| `--list` | List all resources in the save, grouped by type (capped, crafted, special, locked) |
|
||||||
| `--max-resources` | Set all capped resources (`max > 0`) to their max |
|
| `--max-resources` | Set all capped resources (`max > 0`) to their max |
|
||||||
| `--set=key=N` | Set a specific resource or prestige currency to N (e.g. `--set=Plasmid=100`). Case-insensitive. Can be used multiple times. |
|
|
||||||
| `--set-crafted=N` | Set all unlimited/crafted resources (`max == -1`) to N |
|
| `--set-crafted=N` | Set all unlimited/crafted resources (`max == -1`) to N |
|
||||||
| `--only=a,b,c` | Only affect listed resources (comma-separated, case-insensitive). Matches resource names (e.g. `Food`) or internal keys (e.g. `food`). Applies to `--max-resources` and `--set-crafted`. |
|
| `--only=a,b,c` | Only affect listed resources (comma-separated, case-insensitive). Matches resource names (e.g. `Food`) or internal keys (e.g. `food`). Applies to `--max-resources` and `--set-crafted`. |
|
||||||
| `--max-soldiers` | Fill garrison to max capacity and heal all wounded soldiers |
|
| `--max-soldiers` | Fill garrison to max capacity and heal all wounded soldiers |
|
||||||
|
|||||||
99
index.js
99
index.js
@@ -168,55 +168,16 @@ 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("--")) {
|
||||||
@@ -226,7 +187,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 || setValues.length > 0;
|
const hasAction = flags.has("--max-resources") || flags.has("--max-time") || flags.has("--max-geology") || flags.has("--max-soldiers") || flags.has("--list") || craftedValue !== null;
|
||||||
|
|
||||||
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]
|
||||||
@@ -236,7 +197,6 @@ 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
|
||||||
@@ -296,21 +256,6 @@ 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) {
|
||||||
@@ -400,7 +345,6 @@ 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" },
|
||||||
@@ -483,47 +427,6 @@ 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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user