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:
2026-10-23 16:05:31 -04:00
co-authored by Claude Opus 5.5
commit b270c4e818
97 changed files with 11710 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
// Events from the simulation (emit) turned into sounds, toasts and cards.
// ---------- Events from the simulation ----------
function drainEvents() {
let n = 0;
while (EV.length && n++ < 300) {
const e = EV.shift();
if (e.t === 'discover') {
discoveryCard(e); playSfx('discover');
if (!$('m-notebook').hidden) renderNotebook();
} else if (e.t === 'milestone') {
const m = MILESTONES.find(x => x.id === e.id);
if (m && !['bit1', 'bit2', 'bit3', 'tank'].includes(m.id)) { showCard({ kind: 'Milestone', title: m.title, body: [m.msg, m.reward], accent: 'milestone' }); playSfx('milestone'); }
} else if (e.t === 'cast_ready') {
const metal = MD[e.metal] ? MD[e.metal].name : 'Metal';
const body = e.kind === 'ingot' ? `${e.bars} ${e.bars === 1 ? 'bar' : 'bars'} worth has set. Press F on the mold to knock ${e.bars === 1 ? 'it' : 'them'} out.` : `The ${e.kind} casting has set. Press F on the mold to knock it out.`;
showCard({ kind: 'Casting ready', title: `${metal} ${e.kind === 'ingot' ? 'ingot' : e.kind}`, swatch: e.metal > 0 ? e.metal : 0, body: [body], accent: 'milestone' });
playSfx('cast');
} else if (e.t === 'upgrade') {
playSfx('upgrade'); if (SET.shake) R.shake = 1.4;
showCard({ kind: e.kind === 'bit' ? 'Drill bit upgraded' : 'Bag upgraded', title: e.kind === 'bit' ? BIT_TIERS[e.tier].name : TANK_TIERS[e.tier].name, accent: 'milestone',
body: [e.kind === 'bit' ? `Digs hardness ${BIT_TIERS[e.tier].hard} and below.` : `Holds ${TANK_TIERS[e.tier].cap.toLocaleString('en-US')} grains.`] });
hudDirty = true;
} else if (e.t === 'debris') spawnDebris(e.x, e.y, e.m);
else if (e.t === 'suck') spawnSuck(e.x, e.y, e.m);
else if (e.t === 'sold') spawnFloater(e.x, e.y, '+' + fmtMoney(e.v));
else if (e.t === 'order_new') {
const o = e.o;
showCard({ kind: 'New order', title: orderName(o), swatch: o.m, body: [`Within ${o.mins} minutes, for ${fmtMoney(o.pay)}: ${o.mult}× the market price.`, 'Goods dropped into the trade dock fill it first.'] });
playSfx('discover'); hudDirty = true;
}
else if (e.t === 'order_done') {
showCard({ kind: 'Order filled', title: '+' + fmtMoney(e.pay), swatch: e.m, accent: 'milestone', body: [`${orderName(e)} delivered.`, debt > 0 ? `${fmtMoney(debt)} still owed on the claim.` : ''] });
playSfx('sale'); hudDirty = true; if (!$('m-notebook').hidden) renderNotebook();
}
else if (e.t === 'order_expired') {
showCard({ kind: 'Order expired', title: orderName(e), swatch: e.m, body: [e.got ? `You delivered ${e.got} of ${e.qty}. They were paid at market price: ${fmtMoney(e.paid)}.` : 'Nothing was delivered. No harm done.'] });
hudDirty = true;
}
else if (e.t === 'claim_paid') { showCard({ kind: 'Claim', title: 'The land is yours', accent: 'milestone', hold: 1.5, body: ['You have paid off the claim on this land.', 'Keep filling orders: bigger buyers bring bigger ones. The ledger has bigger goals too.'] }); playSfx('milestone'); }
else if (e.t === 'sprout') { showCard({ kind: 'Discovery', title: 'A sapling', swatch: ACORN, body: ['An acorn on soil under open sky has sprouted.', 'It grows into a tree in about a minute and a half. Keep the space above it clear.'] }); playSfx('discover'); }
else if (e.t === 'sfx') { if (e.s) playSfx(e.s, e.a); }
else if (e.t === 'toast') { if (e.text) toast(e.text); }
}
}