Bag: rock goes in as rubble, older saves convert, list shows everything held
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,16 @@ What agents have done, newest first. Add an entry for every piece of work: the d
|
|||||||
note or request it was for, what changed, the files touched, the tests run, and anything left
|
note or request it was for, what changed, the files touched, the tests run, and anything left
|
||||||
open or not verified. Keep each entry short; the git history has the details.
|
open or not verified. Keep each entry short; the git history has the details.
|
||||||
|
|
||||||
|
## 2026-10-27: bag counted things its list didn't show (Claude Code)
|
||||||
|
|
||||||
|
- Owner's exported save: bag total 1,522 but the list showed nothing for 1,000 basalt (from the
|
||||||
|
dev console's give), 200 clay (source not traced) and 322 molten slag (vacuumed with the Flask;
|
||||||
|
not in BAG_ORDER). `bagAdd` now stores solids as their drill yield; `deserializeWorld` converts
|
||||||
|
solids already in a saved bag; `bagMaterials` appends anything outside BAG_ORDER; dev `give`
|
||||||
|
reports what it added and refuses gases and solids with no yield.
|
||||||
|
- Files: `src/sim/items.js`, `src/sim/save.js`, `src/ui/hud.js`, `src/sim/devconsole.js`,
|
||||||
|
`test/t_basics.js`, `test/t_dev_console.js`. Tests: `python3 test/run_all.py --browser` all passed.
|
||||||
|
|
||||||
## 2026-10-27: t50 resonance ping (Claude Code)
|
## 2026-10-27: t50 resonance ping (Claude Code)
|
||||||
|
|
||||||
- Right click with the drill: `firePing` (`src/ui/actions.js`) calls `resonancePing` (`src/sim/drill.js`),
|
- Right click with the drill: `firePing` (`src/ui/actions.js`) calls `resonancePing` (`src/sim/drill.js`),
|
||||||
|
|||||||
@@ -57,9 +57,10 @@ function devCommand(text, cursor) {
|
|||||||
if (!args.length || !Number.isInteger(n) || n <= 0) return usage();
|
if (!args.length || !Number.isInteger(n) || n <= 0) return usage();
|
||||||
const m = devMaterial(args.join(' '));
|
const m = devMaterial(args.join(' '));
|
||||||
if (typeof m === 'string') return [m];
|
if (typeof m === 'string') return [m];
|
||||||
if (m === EMPTY) return ['Air cannot go in the bag.'];
|
if (m === EMPTY || PH[m] === PH_GAS || (PH[m] === PH_SOLID && !YLD[m])) return [`${MD[m].name} cannot go in the bag.`];
|
||||||
if (n > 2147483647 - bag[m]) return ['Too many: a material count cannot exceed 2147483647.'];
|
if (n > 2147483647 - bag[m]) return ['Too many: a material count cannot exceed 2147483647.'];
|
||||||
bagAdd(m, n, true); result = `Added ${n} ${MD[m].name}.`; break;
|
const into = PH[m] === PH_SOLID && YLD[m] ? YLD[m] : m;
|
||||||
|
bagAdd(m, n, true); result = `Added ${n} ${MD[into].name}${into !== m ? ` (as ${MD[m].name.toLowerCase()} breaks up)` : ''}.`; break;
|
||||||
}
|
}
|
||||||
case 'bit': case 'bag': {
|
case 'bit': case 'bag': {
|
||||||
const tiers = cmd === 'bit' ? BIT_TIERS : TANK_TIERS;
|
const tiers = cmd === 'bit' ? BIT_TIERS : TANK_TIERS;
|
||||||
|
|||||||
@@ -279,6 +279,7 @@ function entsInCircle(cx, cy, r) {
|
|||||||
|
|
||||||
// ---------- Bag ----------
|
// ---------- Bag ----------
|
||||||
function bagAdd(m, n, force) {
|
function bagAdd(m, n, force) {
|
||||||
|
if (PH[m] === PH_SOLID && YLD[m]) m = YLD[m]; // rock and metal go in as what drilling them gives
|
||||||
const room = force ? n : Math.max(0, bagCap() - bagTotal);
|
const room = force ? n : Math.max(0, bagCap() - bagTotal);
|
||||||
const k = Math.min(n, room);
|
const k = Math.min(n, room);
|
||||||
bag[m] += k; bagTotal += k;
|
bag[m] += k; bagTotal += k;
|
||||||
|
|||||||
@@ -117,6 +117,8 @@ function deserializeWorld(meta, buffers) {
|
|||||||
bot.x = meta.bot.x; bot.y = meta.bot.y; bot.lamp = meta.bot.lamp !== false; bot.vx = bot.vy = 0;
|
bot.x = meta.bot.x; bot.y = meta.bot.y; bot.lamp = meta.bot.lamp !== false; bot.vx = bot.vy = 0;
|
||||||
bag = new Int32Array(NMAT); meta.bag.forEach((v, k) => { if (k < NMAT) bag[k] = v; });
|
bag = new Int32Array(NMAT); meta.bag.forEach((v, k) => { if (k < NMAT) bag[k] = v; });
|
||||||
for (const m of meta.flyers || []) if (m < NMAT) bag[m]++; // grains that were mid-air when saved
|
for (const m of meta.flyers || []) if (m < NMAT) bag[m]++; // grains that were mid-air when saved
|
||||||
|
// solids saved in the bag (older saves) come back as what drilling them gives
|
||||||
|
for (let k = 0; k < NMAT; k++) if (bag[k] && PH[k] === PH_SOLID && YLD[k]) { bag[YLD[k]] += bag[k]; bag[k] = 0; }
|
||||||
bagTotal = 0; for (let k = 0; k < NMAT; k++) bagTotal += bag[k];
|
bagTotal = 0; for (let k = 0; k < NMAT; k++) bagTotal += bag[k];
|
||||||
Object.assign(player, meta.player);
|
Object.assign(player, meta.player);
|
||||||
seen = new Uint8Array(NMAT); meta.seen.forEach((v, k) => { if (k < NMAT) seen[k] = v; });
|
seen = new Uint8Array(NMAT); meta.seen.forEach((v, k) => { if (k < NMAT) seen[k] = v; });
|
||||||
|
|||||||
+7
-1
@@ -26,7 +26,13 @@ function selectTool(k) {
|
|||||||
if (k === GAME.tool) return;
|
if (k === GAME.tool) return;
|
||||||
GAME.tool = k; GAME.drag = null; hudDirty = true; renderHotbar(); playSfx('ui');
|
GAME.tool = k; GAME.drag = null; hudDirty = true; renderHotbar(); playSfx('ui');
|
||||||
}
|
}
|
||||||
function bagMaterials() { return BAG_ORDER.filter(m => bag[m] > 0); }
|
// The bag's usual order first, then anything else it holds, so nothing in it is ever hidden.
|
||||||
|
const IN_BAG_ORDER = new Uint8Array(NMAT); for (const m of BAG_ORDER) IN_BAG_ORDER[m] = 1;
|
||||||
|
function bagMaterials() {
|
||||||
|
const l = BAG_ORDER.filter(m => bag[m] > 0);
|
||||||
|
for (let m = 1; m < NMAT; m++) if (bag[m] > 0 && !IN_BAG_ORDER[m]) l.push(m);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
function blowList() { return [...GAME.blow].filter(m => bag[m] > 0); }
|
function blowList() { return [...GAME.blow].filter(m => bag[m] > 0); }
|
||||||
// Shift + right click: an even mix of everything loose in the bag (bars never mix)
|
// Shift + right click: an even mix of everything loose in the bag (bars never mix)
|
||||||
function blowAllList() { return bagMaterials().filter(m => PH[m] !== PH_ITEM && (PH[m] !== PH_LIQUID || player.flask)); }
|
function blowAllList() { return bagMaterials().filter(m => PH[m] !== PH_ITEM && (PH[m] !== PH_LIQUID || player.flask)); }
|
||||||
|
|||||||
@@ -148,4 +148,13 @@ const screwCatch = run(`(() => {
|
|||||||
return {airborne, caught, bag: bag[SAND]};
|
return {airborne, caught, bag: bag[SAND]};
|
||||||
})()`);
|
})()`);
|
||||||
check('vacuum held under a screw arc catches its launched sand', screwCatch.airborne === 16 && screwCatch.caught === 16 && screwCatch.bag === 16, JSON.stringify(screwCatch));
|
check('vacuum held under a screw arc catches its launched sand', screwCatch.airborne === 16 && screwCatch.caught === 16 && screwCatch.bag === 16, JSON.stringify(screwCatch));
|
||||||
|
// Rock in the bag: solids go in as what drilling them gives, and older saves holding solids load
|
||||||
|
// that way too, so the bag never holds something its list can't show.
|
||||||
|
flatWorld(run, 120, 80, 60);
|
||||||
|
check('putting rock in the bag stores its rubble', run('(() => { bag.fill(0); bagTotal = 0; bagAdd(BASALT, 10, true); bagAdd(CLAY, 5, true); return bag[BASALT] === 0 && bag[RBAS] === 10 && bag[CLAY] === 0 && bag[LCLAY] === 5 && bagTotal === 15; })()'));
|
||||||
|
check('a save holding solid rock loads it as rubble with the same total', run(`(() => {
|
||||||
|
bag.fill(0); bagTotal = 0; const s = serializeWorld(); s.meta.bag[BASALT] = 1000; s.meta.bag[CLAY] = 200; s.meta.bag[MSLAG] = 7;
|
||||||
|
deserializeWorld(s.meta, s.buffers);
|
||||||
|
return bag[BASALT] === 0 && bag[RBAS] === 1000 && bag[CLAY] === 0 && bag[LCLAY] === 200 && bag[MSLAG] === 7 && bagTotal === 1207;
|
||||||
|
})()`));
|
||||||
done();
|
done();
|
||||||
|
|||||||
@@ -70,4 +70,6 @@ check('dev mark and command log survive save round trip', run('devWorld && devSa
|
|||||||
check('load resets speed, free building, pause and pending steps', run('devSpeed === 1 && !devFree && !devPaused && devSteps === 0'));
|
check('load resets speed, free building, pause and pending steps', run('devSpeed === 1 && !devFree && !devPaused && devSteps === 0'));
|
||||||
run('alloc(240,160)');
|
run('alloc(240,160)');
|
||||||
check('a new world starts unmarked without a dev log', run('!devWorld && devLog.length === 0'));
|
check('a new world starts unmarked without a dev log', run('!devWorld && devLog.length === 0'));
|
||||||
|
check('give turns rock into its rubble and says so', run(`(() => { enableDevConsole(); const before = bag[RBAS]; const out = devCommand('give basalt 50').join(' '); return bag[RBAS] === before + 50 && bag[BASALT] === 0 && /Basalt rubble/.test(out); })()`));
|
||||||
|
check('give refuses things that cannot be in a bag', run(`(() => { enableDevConsole(); const out = devCommand('give steam 5').join(' '); return /cannot go in the bag/.test(out) && bag[STEAM] === 0; })()`));
|
||||||
done();
|
done();
|
||||||
|
|||||||
Reference in New Issue
Block a user