New worlds get generated camp names like new claims (t3)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 19:00:22 -04:00
co-authored by Claude Opus 5.5
parent 66543bbd4b
commit 945e5205b8
5 changed files with 34 additions and 11 deletions
+1 -1
View File
@@ -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. Events from the simulation (emit) turned into sounds, toasts and cards.
- functions: drainEvents - functions: drainEvents
### src/ui/loop.js (127 lines) ### src/ui/loop.js (133 lines)
The frame loop and boot. The frame loop and boot.
- functions: scanVisible, frame, boot - functions: scanVisible, frame, boot
- variables: lastT, acc, lastRender, frameMs, simMs, fpsSm, ambientT - variables: lastT, acc, lastRender, frameMs, simMs, fpsSm, ambientT
+6
View File
@@ -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 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-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) ## 2026-10-24: t30: progress while generating, loading and saving (Claude Code, Codex)
- `worldgen.js`: `worldSteps` generator yields `[fraction, label]`; `generateWorld` (sync, tests) - `worldgen.js`: `worldSteps` generator yields `[fraction, label]`; `generateWorld` (sync, tests)
+9 -3
View File
@@ -98,16 +98,22 @@ function boot(hotData) {
buildTitleCells(); drawTitle(); buildTitleCells(); drawTitle();
$('nw-seed').value = String(Math.floor(Math.random() * 900000) + 100000); $('nw-seed').value = String(Math.floor(Math.random() * 900000) + 100000);
$('wallet').onclick = () => { playSfx('ui'); openNotebook('Market'); }; $('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 () => { $('btn-new').onclick = async () => {
auInit(); playSfx('ui'); $('newworld').hidden = !$('newworld').hidden; auInit(); playSfx('ui'); $('newworld').hidden = !$('newworld').hidden;
if ($('newworld').hidden) return; if ($('newworld').hidden) return;
try { $('nw-name').value = `World ${(await listWorlds()).length + 1}`; } try { worldNames = (await listWorlds()).map(w => w.name); }
catch (e) { $('nw-name').value = 'World 1'; } catch (e) { worldNames = []; }
$('nw-name').value = autoName = claimName(newWorldSeed(), worldNames);
$('nw-name').focus(); $('nw-name').select(); $('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(); }; for (const id of ['nw-name', 'nw-seed']) $(id).onkeydown = e => { if (e.key === 'Enter') $('btn-generate').click(); };
$('btn-generate').onclick = () => { $('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); const [w, h] = $('nw-size').value.split('x').map(Number);
startNewWorld(seed, w, h, $('nw-name').value); startNewWorld(seed, w, h, $('nw-name').value);
}; };
+1 -1
View File
@@ -33,7 +33,7 @@ async function setupNewWorld(seed, w, h, name, carried, title = 'Generating worl
async function startNewWorld(seed, w, h, name = '') { async function startNewWorld(seed, w, h, name = '') {
try { try {
const worlds = await listWorlds(); 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 }; career = { id: newWorldId(), claims: [GAME.worldId], claimNo: 1 };
GAME.careerId = career.id; GAME.claimNo = 1; GAME.careerId = career.id; GAME.claimNo = 1;
showLoading(true, 0.9, 'Saving'); showLoading(true, 0.9, 'Saving');
+17 -6
View File
@@ -1,7 +1,7 @@
"""Play-test in a real browser: mouse, keyboard and a mocked gamepad drive the game; save and continue. """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 Screenshots go to shots/play/ (or the folder given as the first argument). Pixel look unless the
second argument is 'smooth'.""" 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')) sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent / 'tools'))
from use_venv import ensure_playwright; ensure_playwright() from use_venv import ensure_playwright; ensure_playwright()
from playwright.sync_api import sync_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') if pg.is_visible('#nw-seed'): pg.click('#btn-new')
pg.click('#btn-new') pg.click('#btn-new')
pg.wait_for_function("document.activeElement === $('nw-name')") pg.wait_for_function("document.activeElement === $('nw-name')")
names[seed] = 'Copper Valley' if seed == 222 else pg.input_value('#nw-name') auto_name = 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}') 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: if seed == 222:
pg.fill('#nw-name', names[seed]) pg.fill('#nw-name', names[seed])
pg.screenshot(path=str(out / 'new-world.png')) pg.fill('#nw-seed', '42')
pg.fill('#nw-seed', str(seed)); pg.press('#nw-name' if seed == 222 else '#nw-seed', 'Enter') 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) 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} 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'))")) 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) check('Continue chooses the most recently saved world', pg.evaluate('worldSeed') == 333)
pg.evaluate('quitToTitle()') pg.evaluate('quitToTitle()')
if not pg.is_visible('#nw-seed'): pg.click('#btn-new') 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-name', ' ')
pg.fill('#nw-seed', '777'); pg.click('#btn-generate') pg.fill('#nw-seed', '777'); pg.click('#btn-generate')
pg.wait_for_function('GAME.running === true', timeout=20000) pg.wait_for_function('GAME.running === true', timeout=20000)