# Adding a city

Each city is one folder in `src/cities/<id>/`. Nothing shared lists the cities: they are found automatically
(`src/cities/browser.ts` with `import.meta.glob` in the browser, `src/cities/node.ts` with `readdir` for bots and tests).
A city is built in its own session without touching shared files: everything below is a hook the shared code reads.

| file | side | what it does |
|---|---|---|
| `city.ts` | simulation (no Phaser) | `registerCity({...})`: everything about the place (see the table below) |
| `room.ts` | drawing | the room as a `RoomArt`: `drawRoom()` (640 x 300 static shell drawn with `Pix`), `drawNoren(frame)`, the window, door and menu-board boxes, lanterns, steam spots, the cat, and optionally a living layer (`extend`) |
| `index.ts` | drawing | `registerRoom(...)`, `CITY_MUSIC[id] = {...}` (tempo, key, instruments, ambience beds), local guest looks (`registerLook`), icons (`registerIcon`), and `import './city.ts'` |
| `food.ts` | drawing | the local cards' art (below), imported from `index.ts` |
| anything else | either | a city may add files (Osaka has `belt.ts` for its rules and `view.ts` for its drawing) |

## What `registerCity` takes (src/sim/city.ts)

| field | what it is |
|---|---|
| `id`, `name`, `style`, `order` | `order` is the place on the route: 1 Tokyo, 2 Osaka, 3 Toyama, 4 Kanazawa, 5 Sapporo, 6 Fukuoka. The city with order 6 ends the game with credits. |
| `lat`, `lon`, `blurb` | the pin on the map of Japan and one line for the map card |
| `layout` | stool x positions in unlock order, the aisle line, the door, the queue spots, the chef line. A different floor plan moves these. |
| `seatsStart`, `arrival` | stools at the start, guests per second before upgrades |
| `cards` | local ingredients (`CardRow`s); they only drop in packs while you are in this city |
| `guests`, `wishes` | local guest types (with a `look` drawn by `registerLook` in index.ts) and local wishes (a `test` on the recipe is allowed; choice and pay always go through `src/sim/wishes.ts`) |
| `rules` | local classics: flavour rules only this city's cards fire (Osaka: corn mayo, torched mayo). A classic sets `dish` to its real name (`'MASU-ZUSHI'`, `'GOMA-SABA {F}'`; `{F}` the form, `{T}` the topping); the dish is then called that, plus "WITH ..." for accents the rule does not use. `npm run craft` lints every card and rule together. |
| `twist` | a price rule (`price`), a signature mark on the menu (`signature(s, g)`; `g` is the shop) and the words under a signature dish's price on the prep board (`mark(s, g)`; default "NAME: X" + the `price` multiplier, so a city whose bonus lives in its service, like Toyama's press or Sapporo's seasons, says it there) |
| `service` | optional: a different way of serving (below) |
| `goal`, `costScale`, `priceScale` | the size of the city's numbers. `priceScale` multiplies every dish and pack price, `costScale` every upgrade. The chain arrives with Tokyo's full collection and good dishes, so `costScale` is larger than `priceScale` (Osaka: 20 and 150). Tune them with `npm run campaign` (below). |
| `startCoins` | the till when the city opens (each shop has its own till; this is the fast start) |
| `welcome` | the welcome pack: exactly these cards, free, opened on arrival (local fish first) |
| `intro` | first-visit hints, one at a time, each pointing at a named target (`room:belt`, `room:menuboard`, `btn:shop`...) |
| `upNames` | local names for the upgrades (Osaka: BELT SEAT, NEON SIGN, SECOND CHEF) |
| `menuExtra` | menu slots beyond the usual (a belt carries more dishes); the room's `boardSlots` should match |
| `firstWish`, `startMenu` | only the first city uses these for the tutorial; later cities open with the best menu the chain's dishes make |

## A different way of serving: `service` (src/sim/city.ts, `Service`)

Every hook is optional; anything not given keeps the counter rules in `src/sim/game.ts`.

| hook | replaces |
|---|---|
| `arrivals(g, dt)` | the shared door once the shop is open: spawn guests with `g.spawn(false)` (Fukuoka's quiet stretches and rushes). The city's `arrival` then only feeds `Game.rates()` if the city has no `rates` |
| `init(g)` | sets up `g.ext` (rebuilt on load, never saved) and `g.saved` (plain JSON, saved with the shop) |
| `guest(g, guest, dt)` | a seated guest's `order` / `wait` / `eat` states. Use `g.bill(guest, key)` for each serving and `g.settle(guest, amount, verdict)` to pay and leave, `g.leave(guest, true)` for an angry exit |
| `chefs(g, dt)` | the chefs' work (set `c.x`, `c.state`, `c.t`, `c.dur` so the shared scene draws them) |
| `rates(g, levels, menu)` | the income estimate used by the bots, the managers and the upgrade panel. Check it against measured income (Osaka's is within about 25%) |
| `price(g, key)` | an extra per-dish price multiplier in this shop (Osaka's plate colour) |
| `left(g, guest)` | cleanup when a guest leaves |
| `dishOption` | a per-dish choice shown on the menu board (a stripe, tap to change) and in the menu panel (a button with a note) |
| `bot(g)` | how the bots play the twist, once per simulated second (Osaka: picks plate colours by `rates`) |

City events for the room are `{ k: 'city', what, x?, key?, g?, n? }` pushed to `g.events`; the room's living layer reads them.

## The room's living layer: `RoomArt.extend(scene)` (src/render/rooms.ts)

Returns `{ update(dt), onEvent?(e), destroy?() }`. It is built when the shop scene starts and gets the `ShopScene`
(its `views`, `popText`, `sparkle`, `time`, `tweens`). Draw above depth 40 to be in front of the counter. Other room
options: `plateY` (where an eating guest's plate sits), `boardSlots` / `boardStep`, `rain: false`, `lanterns: []`.

## Rules the shared code relies on

- Coordinates are art pixels at 640 x 360. The counter's far edge is y 146, seated guests meet it at y 154, the chef
  line is the layout's `chefY`. The shared scene crops the room's rows 146-180 as the layer in front of seated guests.
- Keep the simulation free of Phaser imports: `npm run sim`, `npm run economy`, `npm run campaign` and the other checks
  import `city.ts` in Node.
- Don't edit shared files to add a city. If a hook is missing, add it in a general way and write it down here.
- Ids are shared by every city: cards, rules, wishes, guest looks, icons and food art. Registration keeps the first and
  records a clash (`src/sim/ids.ts`); `npm run craft` fails on any duplicate id and on two cards with the same name, and
  the browser warns `[neta] duplicate ...` for looks, icons and art. Prefix a new city's ids (Kanazawa's `kz_buri`) or
  reuse the shared card (Sapporo's spring catch uses Toyama's `masu`). The route's `order` must be unique too.

## Food art: `src/render/food.ts`

A local card without art is drawn as salmon (toppings) or a purple blob (everything else). In the city's `food.ts`:

| hook | what it draws |
|---|---|
| `registerTop(id, { ramp, pat?, shape?, fleck?, belt? })` | a topping on every dish and its card: a 4-colour ramp (dark to highlight), a pattern `(u, v, r, s)` returning a ramp index or `16 + colour`, a shape (`slab`, `round`, `heap`, `shrimp`, `block`, `flakes`, `tongues`), the specks of a `flakes` topping |
| `registerObject(id, (p, r) => ...)` | a form, rice, prep, sauce or garnish card's picture in a 44 x 36 box (outlined afterwards) |
| `registerAccent(id, (a) => ...)` | how an accent shows on a dish: `a.p`, the topping's box `a.x, a.y, a.w, a.h`, `a.big` at close-up size, `a.dots(col, n, col2?)` |

`missingArt(CARDS)` lists cards still on the fallback (it is empty now). Each topping keeps one feature you can spot at
16 px and differs from its shared neighbour (masaba's tiger stripes against saba, masu's silver line against salmon).

## What the chain gives a city (src/sim/chain.ts)

A city opens when the one before it reaches its goal (a modal offers the trip; the map can take you later). On the
first visit: its `startCoins`, the welcome pack, the best menu the chain's dishes make, and its intro hints.
Carried over: cards and pearls, the book, every dish invented, the managers (host: keeps the menu; buyer: opens
packs; steward: buys upgrades that pay back in under a minute; each joins at a milestone once you have done the chore
by hand often enough). Fresh: the till, seats, chefs, upgrades, buzz, the goal. A mastered shop runs on its own and
sends 10% of its income to the shop you are in; each shop star adds 2% to every price in the other shops. Away from
the game the chain earns 50% of its online rate for up to 4 hours (`offline`).

## Balancing a city: `npm run campaign`

Bots play the whole chain on the real code, city after city, until each goal (`--city <id>` stops after that city,
`--seeds N`, `--bots greedy,cheapest`). It checks: every goal within 90 minutes, no city more than 1.5x its neighbour,
the fast start (the new city's 2nd minute earns at least what the previous city earned in its 5th), no long stretch
with nothing to buy; with all six cities, the campaign ends in credits in 2-4 hours. Aim for about 30-40 minutes per
city. Results go to `out/balance/campaign.md`; write the city's numbers and changes in BALANCE.md.

## The six cities

**Tokyo** (`src/cities/tokyo/`): a narrow Edomae counter at night, eight stools unlocked from the door inwards, kohada
and anago, the old regular who asks for "something cured, Edo style", and the EDOMAE CURES twist (soy, kelp or vinegar
cures sell for x1.3).

**Osaka** (`src/cities/osaka/`): a bright kaiten-zushi off Dotonbori at dusk, the door on the left, fourteen close
seats along a conveyor belt, two chefs filling it from the right. Local catch: hamo (pike conger), corn, mayo and
shiroita kelp; the welcome pack adds the pressing box (oshizushi). Local guests: the Osaka auntie (thrifty with dear
plates), the baseball fan, the merchant who wants "something pressed, Osaka style". Local rules: corn mayo, torched
mayo. The twist: the belt (`belt.ts`) - chefs make dishes without orders, guests take passing plates that fit their
wish well enough for the plate's colour (green x0.7 to gold x2; kids and students balk at red and gold, tourists and
merchants don't), eat two to four and pay for the stack; guests who see nothing they would take leave cross; plates
that go round three times are thrown away. The player sets each dish's plate colour on the menu board (the menu panel
shows the share of guests who would take it); guests say "TOO DEAR!" when they let a plate they like pass. Pressed sushi
sells for x1.25. Music: marimba and celesta at 104 bpm over the belt's hum and a crowd.

**Toyama** (`src/cities/toyama/`): a snowy-evening masu-zushi shop of pale cedar and green plaster, the door in the
middle of the back wall, ten stools in two runs either side of a round stand, a window onto the Tateyama mountains
with a passing tram, a blinking firefly-squid tank. Local catch: cherry salmon (masu), firefly squid, white shrimp,
Himi winter yellowtail, bamboo leaf, vinegar miso. Guests: the train traveller ("something pressed for the train"),
the bay fisherman, the Etchu medicine seller. Classics: MASU-ZUSHI, SHIROEBI KOBUJIME, HOTARUIKA SU-MISO, and the
leaf-lined press. The twist: the masu press (`press.ts`) - a pressed dish is made a round at a time (to order, one
tier of 8 wedges or two tiers of 16, set on the menu board); a wedge sells for x1.3 fresh, falling to x1 at 90 s, when
the rest is thrown away. Music: harp in A dorian at 66 bpm over river, wind and trains.

**Kanazawa** (`src/cities/kanazawa/`): a stall on the Omicho market lane on a snowy morning, opening onto the lane in
the middle with an ice case of the day's catch, twelve stools filling outward, Kenroku-en's snow-rope pines in the
window, a gold-leaf screen. Local catch: buri, kobako snow crab, gasu ebi, gold leaf, ishiru fish sauce, pickled
turnip (ids prefixed `kz_`). Guests: the fishmonger, a geiko, a gold-leaf beater. Classics: KABURAZUSHI, gold on the
finest (and GAUDY gold on a common fish). The twist: the morning catch (`market.ts`) - every 150 s three fish land with
limited portions that sell for x1.8 (luxuries) to x4 (everyday fish) until they run out; gold leaf sells for x1.3.
Music: plucked lead over a minor scale at 88 bpm, market crowd, gulls.

**Sapporo** (`src/cities/hokkaido/`): a pine-walled Hokkaido counter, the door in the middle behind a frosted
windbreak porch, eleven stools filling in pairs from the door, a cast-iron stove with the cat beside it, a crab tank,
Otaru glass-float lamps. Local catch: herring, hairy crab, saury, king crab, kinki rockfish, Rishiri kelp, butter-soy,
wild garlic (and Toyama's cherry salmon in spring). Guests: the skier, the fisherman in yellow oilskins, the beer-hall
regular. Classics: RISHIRI UNI GUNKAN, BATA-YAKI (butter-soy and the torch on shellfish). The twist: the four seasons
(`seasons.ts`) - every 4 minutes of play the season turns and four fish are at their best, selling for x2.5; the
window, the season board and the menu marks follow. Music: music box and bells at 80 bpm over the stove and wind.

**Fukuoka** (`src/cities/fukuoka/`): a Hakata yatai on the Naka river at night, an open cart in the middle of the bank
under a tarp with red lanterns, Nakasu's neon on the black water, ten stools unlocked from the middle. Local catch:
Genkai saba, mentaiko, ara, sesame sauce, yuzu pepper. Guests: the taxi-driver regular, the date-night couple, Hawks
fans, an office party. Classics: GOMA-SABA, ABURI MENTAIKO; sesame dishes sell for x1.25. The twist: the night stall
(`yatai.ts`) - long quiet stretches, then a rush called a few seconds ahead ("THE HAWKS WON!"); dishes marked READY on
the menu board are made ahead in the quiet and go out at once in a rush, and spoil after 150 s. The last stop: its
goal rolls the credits. Music: plucked lead in D dorian at 84 bpm, swung, over the river and the grill.

