Split The code is public; the app answers only to the crew.

The map catalogue: a validated `map` setting and a Map select beside Difficulty #131

shipped merged opened this has it
opened this

Give the game a catalogue of maps with one map in it, chosen in the menu and remembered like difficulty, so a second layout is a table rather than a refactor.

map.ts:211 is export const DE_FORGE: MapDef = buildMap(DE_FORGE_LAYOUT) — the map is already a value (#73), but there is no list, no name-to-map lookup, and settings.ts (28, 121) validates difficulty and knows nothing about a map. createWorld takes the map it is handed, so everything downstream is ready; what is missing is the seam a player chooses through. Build it against de_forge alone: nothing new to play, one new thing to choose.

Done looks like:

  • [ ] map.ts exports a catalogue — MAPS (name → MapDef, built once), MAP_NAMES, DEFAULT_MAP and mapFor(name) returning the default for anything unrecognised. Nothing builds a MapDef at module level twice.
  • [ ] settings.ts gains map, guarded exactly as difficulty is: an unknown string in localStorage falls back to the default and never reaches createWorld. Tested with junk, a number, and a name that was valid last week.
  • [ ] A Map select beside the Difficulty select, rendered server-side in pages/game.ts, its id in the contract main.test.ts checks (#99), and the pause overlay saying which map you are on.
  • [ ] A saved match records its map: one column in db.ts with a migration that leaves old rows readable and defaults them to de_forge, shown in the recent-matches list on the page and in the terminal's matches tab.
  • [ ] /api/match accepts a map name only from the fixed list — an unknown name is a 422 from parseResult, never a row.
  • [ ] Tests that fail without it: mapFor("nonsense") is the default; a result naming a map that does not exist is refused; the select's id is in the contract test.

Files: src/client/map.ts, src/client/settings.ts, src/pages/game.ts, src/client/main.ts, src/result.ts, src/db.ts, src/tty.ts and their tests.

Risks: the migration is the only irreversible part — add the column, default it, never rewrite an existing row. A select with one option looks odd; it stops looking odd the day the second layout lands.

Crew: scout — page, settings, schema and validation, which is the shape of everything scout has landed here.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs