Deepgrain Foundry: move from a claude.ai artifact to a local repository
The game as of artifact version 24, ready for local development with coding agents. - Source split from three large files into 52 small ones under src/sim, src/render and src/ui, joined in the order in src/manifest.json by build.py. The joined code is unchanged apart from comments; every headless suite gives the same output, and screenshots in both looks are pixel-identical to before. - Light tidying with no change in behaviour: one movement scan loop, shared hazard-stripe and belt shader helpers, unused code removed, a shared check helper for the test suites. - World files can be exported as an ordinary download outside claude.ai. - Tests: broken old scripts removed, experiments moved to tools/, test/run_all.py added. Tools: shot.py (repeatable screenshots), where.py, map.py. - Docs: AGENTS.md, CLAUDE.md, NOTES.md (the owner's to-do inbox), docs/PROJECT_MAP.md, DESIGN.md, TESTING.md, systems notes, the owner's requests and an agent log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9kLmYADKSEjuwUN3rFxDA
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
// The tool hotbar and what is selected.
|
||||
// ---------- HUD ----------
|
||||
function renderHotbar() {
|
||||
const hb = $('hotbar'); hb.innerHTML = '';
|
||||
TOOLS.forEach((t, k) => {
|
||||
const b = el('button', { class: 'slot' + (k === GAME.tool ? ' on' : ''), title: `${t.name} (${t.key})`, 'aria-label': `${t.name} tool, key ${t.key}`, 'aria-pressed': k === GAME.tool });
|
||||
b.innerHTML = `<span class="k">${t.key}</span>${ICONS[t.id]}`;
|
||||
b.onclick = () => { selectTool(k); };
|
||||
hb.appendChild(b);
|
||||
});
|
||||
}
|
||||
function toolId() { return TOOLS[GAME.tool].id; }
|
||||
// Shift does two jobs. While you are steering the bot it boosts; while you are not,
|
||||
// it is the tool modifier (vacuum everything, blow a mix, fill a rectangle).
|
||||
function steering() { return ['w', 'a', 's', 'd', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].some(k => keys.has(k)) || GAME.padRun > 0; }
|
||||
function modShift() { return keys.has('Shift') && !steering(); }
|
||||
// The loose material exactly under the cursor, or the nearest one touching it.
|
||||
function grainAt(ax, ay) {
|
||||
if (ax < 0 || ay < 0 || ax >= W || ay >= H) return 0;
|
||||
const i = ay * W + ax, m = mat[i];
|
||||
if (occ[i]) { const e = ents.find(e => ax >= e.ix && ax < e.ix + BAR_W && ay >= e.iy && ay < e.iy + BAR_H); if (e) return e.m; }
|
||||
if (PH[m] === PH_POWDER || (PH[m] === PH_LIQUID && player.flask)) return m;
|
||||
return vacuumTargetAt(ax, ay, 1);
|
||||
}
|
||||
function selectTool(k) {
|
||||
if (k === GAME.tool) return;
|
||||
GAME.tool = k; GAME.drag = null; hudDirty = true; renderHotbar(); playSfx('ui');
|
||||
if (toolId() === 'vacuum') ensureDropMat();
|
||||
}
|
||||
function bagMaterials() { return BAG_ORDER.filter(m => bag[m] > 0); }
|
||||
// Only auto-pick a material when the player has not chosen one. A chosen
|
||||
// material stays selected even after it runs out.
|
||||
function mixList() { return [...GAME.collect].filter(m => bag[m] > 0 && PH[m] !== PH_ITEM); }
|
||||
function ensureDropMat() {
|
||||
if (GAME.blowMix && mixList().length < 1) GAME.blowMix = false;
|
||||
if (bag[GAME.dropMat] > 0) return;
|
||||
if (GAME.matChosen && GAME.dropMat) return;
|
||||
const l = bagMaterials(); GAME.dropMat = l.length ? l[0] : 0;
|
||||
}
|
||||
function piece() { return PIECES[GAME.pieceIdx]; }
|
||||
Reference in New Issue
Block a user