From 945e5205b813f13f149eba19e555b457226a7673 Mon Sep 17 00:00:00 2001 From: 44r0n7 <44r0n7+gitea@pm.me> Date: Sat, 24 Oct 2026 19:00:22 -0400 Subject: [PATCH] New worlds get generated camp names like new claims (t3) Co-Authored-By: Claude Opus 5.5 --- docs/PROJECT_MAP.md | 2 +- docs/agent-log.md | 6 ++++++ src/ui/loop.js | 12 +++++++++--- src/ui/session.js | 2 +- test/play.py | 23 +++++++++++++++++------ 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/docs/PROJECT_MAP.md b/docs/PROJECT_MAP.md index cc33159..6e2c7cf 100644 --- a/docs/PROJECT_MAP.md +++ b/docs/PROJECT_MAP.md @@ -442,7 +442,7 @@ Previews drawn under the cursor: build ghosts, drill aim and pulses, selections, Events from the simulation (emit) turned into sounds, toasts and cards. - functions: drainEvents -### src/ui/loop.js (127 lines) +### src/ui/loop.js (133 lines) The frame loop and boot. - functions: scanVisible, frame, boot - variables: lastT, acc, lastRender, frameMs, simMs, fpsSm, ambientT diff --git a/docs/agent-log.md b/docs/agent-log.md index 733249e..2e50024 100644 --- a/docs/agent-log.md +++ b/docs/agent-log.md @@ -4,6 +4,12 @@ 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 open or not verified. Keep each entry short; the git history has the details. +## 2026-10-24: New worlds get camp names too (Claude Code, Codex) + +- The new-world dialog prefills `claimName(seed, existing names)` instead of "World N", and + follows seed edits until the player types their own name; a blank name falls back to it too + (`src/ui/loop.js`, `session.js`). play.py checks updated. All tests pass. + ## 2026-10-24: t30: progress while generating, loading and saving (Claude Code, Codex) - `worldgen.js`: `worldSteps` generator yields `[fraction, label]`; `generateWorld` (sync, tests) diff --git a/src/ui/loop.js b/src/ui/loop.js index f49ccef..2148d98 100644 --- a/src/ui/loop.js +++ b/src/ui/loop.js @@ -98,16 +98,22 @@ function boot(hotData) { buildTitleCells(); drawTitle(); $('nw-seed').value = String(Math.floor(Math.random() * 900000) + 100000); $('wallet').onclick = () => { playSfx('ui'); openNotebook('Market'); }; + let autoName = '', worldNames = []; + const newWorldSeed = () => parseInt(String($('nw-seed').value).replace(/\D/g, ''), 10) || Math.floor(Math.random() * 1e6); $('btn-new').onclick = async () => { auInit(); playSfx('ui'); $('newworld').hidden = !$('newworld').hidden; if ($('newworld').hidden) return; - try { $('nw-name').value = `World ${(await listWorlds()).length + 1}`; } - catch (e) { $('nw-name').value = 'World 1'; } + try { worldNames = (await listWorlds()).map(w => w.name); } + catch (e) { worldNames = []; } + $('nw-name').value = autoName = claimName(newWorldSeed(), worldNames); $('nw-name').focus(); $('nw-name').select(); }; + $('nw-seed').oninput = () => { + if ($('nw-name').value === autoName) $('nw-name').value = autoName = claimName(newWorldSeed(), worldNames); + }; for (const id of ['nw-name', 'nw-seed']) $(id).onkeydown = e => { if (e.key === 'Enter') $('btn-generate').click(); }; $('btn-generate').onclick = () => { - const seed = parseInt(String($('nw-seed').value).replace(/\D/g, ''), 10) || Math.floor(Math.random() * 1e6); + const seed = newWorldSeed(); const [w, h] = $('nw-size').value.split('x').map(Number); startNewWorld(seed, w, h, $('nw-name').value); }; diff --git a/src/ui/session.js b/src/ui/session.js index ff46c1c..e4646ed 100644 --- a/src/ui/session.js +++ b/src/ui/session.js @@ -33,7 +33,7 @@ async function setupNewWorld(seed, w, h, name, carried, title = 'Generating worl async function startNewWorld(seed, w, h, name = '') { try { const worlds = await listWorlds(); - await setupNewWorld(seed, w, h, cleanWorldName(name, `World ${worlds.length + 1}`)); + await setupNewWorld(seed, w, h, cleanWorldName(name, claimName(seed, worlds.map(w => w.name)))); career = { id: newWorldId(), claims: [GAME.worldId], claimNo: 1 }; GAME.careerId = career.id; GAME.claimNo = 1; showLoading(true, 0.9, 'Saving'); diff --git a/test/play.py b/test/play.py index c11c5ee..cc8f293 100644 --- a/test/play.py +++ b/test/play.py @@ -1,7 +1,7 @@ """Play-test in a real browser: mouse, keyboard and a mocked gamepad drive the game; save and continue. Screenshots go to shots/play/ (or the folder given as the first argument). Pixel look unless the second argument is 'smooth'.""" -import json, sys, pathlib +import json, sys, pathlib, re sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent / 'tools')) from use_venv import ensure_playwright; ensure_playwright() from playwright.sync_api import sync_playwright @@ -75,12 +75,23 @@ with sync_playwright() as p: if pg.is_visible('#nw-seed'): pg.click('#btn-new') pg.click('#btn-new') pg.wait_for_function("document.activeElement === $('nw-name')") - names[seed] = 'Copper Valley' if seed == 222 else pg.input_value('#nw-name') - check(f'new world {seed} offers the next default name', pg.input_value('#nw-name') == f'World {2 if seed == 222 else 3}') + auto_name = pg.input_value('#nw-name') + check(f'new world {seed} offers a two-word camp name', bool(re.fullmatch(r'[A-Z][a-z]+ [A-Z][a-z]+', auto_name))) + check(f'new world {seed} default matches its seed', auto_name == pg.evaluate("async () => claimName(parseInt($('nw-seed').value, 10), (await listWorlds()).map(w => w.name))")) + check('default name is selected for replacement', pg.evaluate("$('nw-name').selectionStart === 0 && $('nw-name').selectionEnd === $('nw-name').value.length")) + pg.fill('#nw-seed', '42') + first_name = pg.input_value('#nw-name') + pg.fill('#nw-seed', str(seed)) + expected = pg.evaluate("async seed => claimName(seed, (await listWorlds()).map(w => w.name))", seed) + check(f'changing seed to {seed} updates the camp name', pg.input_value('#nw-name') == expected and expected != first_name) + names[seed] = 'Copper Valley' if seed == 222 else expected if seed == 222: pg.fill('#nw-name', names[seed]) - pg.screenshot(path=str(out / 'new-world.png')) - pg.fill('#nw-seed', str(seed)); pg.press('#nw-name' if seed == 222 else '#nw-seed', 'Enter') + pg.fill('#nw-seed', '42') + check('changing the seed keeps a typed world name', pg.input_value('#nw-name') == names[seed]) + pg.fill('#nw-seed', str(seed)) + pg.screenshot(path=str(out / 'new-world.png')) + pg.press('#nw-name' if seed == 222 else '#nw-seed', 'Enter') pg.wait_for_function('GAME.running', timeout=20000) check(f'new world {seed} paints a moving generation bar', pg.evaluate("__loads.some(([t, s, w]) => t === 'Generating world' && parseFloat(w) > 0 && parseFloat(w) < 90) && !GAME.loading")) check(f'new world {seed} shows the Welcome card', pg.evaluate("[...$('cards').children].some(c => c.textContent.includes('Welcome'))")) @@ -133,7 +144,7 @@ with sync_playwright() as p: check('Continue chooses the most recently saved world', pg.evaluate('worldSeed') == 333) pg.evaluate('quitToTitle()') if not pg.is_visible('#nw-seed'): pg.click('#btn-new') - blank_default = pg.evaluate("listWorlds().then(w => `World ${w.length + 1}`)") + blank_default = pg.evaluate("listWorlds().then(w => claimName(777, w.map(w => w.name)))") pg.fill('#nw-name', ' ') pg.fill('#nw-seed', '777'); pg.click('#btn-generate') pg.wait_for_function('GAME.running === true', timeout=20000)