not-tonight/docs/VENUES.md
m3ultra 74cbd71ad3 Materials, and the floor-cache bug that would have eaten the promotion
Two things, one of them a real bug.

THE BUG: bakeTiles baked to a single global 'floor:tiles' and early-returned if
that key existed. Textures outlive a scene, so the first venue you played baked
the floor for every venue after it — you would survive a week at The Royal, get
promoted, walk into Voltage and still be standing on the pub's carpet. It only
shows on promotion, which is exactly the moment the venue ladder is supposed to
pay off, and never in a dev route because those reload the page. The baked
texture is now keyed per venue; verified by switching venues inside one session
and watching the concrete arrive.

MATERIALS: FloorLayout.materials names a generated plate per tile kind and the
baker stamps it TINTED to the palette colour. The plate carries grain, the
palette keeps hue — an untinted photo-real plate blows straight past the
brightness band the whole room was tuned in, and darkness is the mechanic.
Plates are 16x16, the game's own tile, because at that size "carpet" is
structured noise, which is also what pub carpet looks like from head height.
Ten of them: carpet, concrete, deck, parquet, looTile, paving, plaster, brick,
timber, steel. No plate named, or art not landed, falls back to flat colour
exactly as before.

Wave 2 filler for the two spaces the review found genuinely empty — The Royal's
central carpet (four props across ~500 tiles) and ROOM's east hall (~23% of its
interior with no lights and three props) — plus the kit every real room has and
no game room does: fire extinguishers, exit signs, CCTV, bins, a notice board,
ceiling fans. Voltage gets the same pass; being the reference room is how it
quietly missed the last one.

The invariant caught a duplicate prop id I introduced while placing these, which
is what it is for.

Also relaxed one ROOM assertion that keyed on array order: it checked that the
FIRST sodium light stands over the loading dock, which broke the moment the east
hall legitimately burned the same colour. It now checks that one of them does.

Gate: lint clean, tsc clean, 821 tests passing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 21:56:20 +10:00

151 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NOT TONIGHT — the four venues
*Written 2026-07-28. The venue ladder in `src/data/venues.ts` has always been
four venues; up to now they shared ONE floor map and differed only by difficulty
knobs. This document is the design contract for giving each its own room.*
## 0. Why bother
Design §6 sells the ladder as *promotion* — survive a week, get a better door.
But a promotion you cannot see is a number, not a reward. Walking into Elevate
and finding The Royal's carpet under your feet undoes the fiction the whole run
is built on. Each venue now has its own bar layout, its own dunnies, its own
smoking area and its own DJ booth, because those four things are exactly what a
bouncer actually learns about a room.
## 1. Shared contract (all four)
- **Grid is fixed at 80×45 tiles.** Venues differ by ARRANGEMENT, not extent —
the camera, minimap and bounds maths all key off `MAP_W`/`MAP_H`, and a
per-venue grid size would be a refactor with no visible payoff. A small venue
reads small by walling its unused grid to `void`, not by shrinking the array.
- **Every layout exports a `FloorLayout`**: `{ id, map, props, lights, posts,
probes }`. Nothing else may be a module singleton — that was the old shape and
it is what made four venues impossible.
- **Every layout must satisfy the same invariants** (`tests/floor/layouts.test.ts`):
1. every `AnchorKind` has ≥1 anchor, and every anchor is walkable;
2. all walkable tiles are mutually reachable (flood fill), except sealed
cubicle interiors, which must be reachable from their `StallDef.door`;
3. every `POSTS` entry is walkable and has a walkable neighbour (a post on a
sealed tile freezes the player for the night — this has shipped twice);
4. every `PROBES` entry has a walkable tile within reach;
5. no prop hangs off the grid.
- **Materials are grain, the palette is hue.** `FloorLayout.materials` names a
generated plate per tile kind (`tex_carpet`, `tex_concrete`, …) and the baker
stamps it **tinted to the palette colour**. That is deliberate: an untinted
photo-real plate blows straight past the brightness band everything else was
tuned in, and the room stops being dark. Plates are 16×16 — the game's tile —
because at that size "carpet" is structured noise, which is also what pub
carpet looks like from head height. No plate named, or art not landed: flat
palette colour, exactly as before.
- **The baked floor is keyed per venue** (`floor:tiles:<id>`). Textures outlive a
scene, so a single global key meant the first venue you played baked the floor
for every venue after it — you would survive a week at The Royal, get promoted,
and walk into Voltage still standing on the pub's carpet. It only shows on
promotion, which is precisely when the ladder is supposed to pay off.
- **Light signatures survive** (SPACES.md §1): bar = amber, dance = pulsing
pink on the real beat, DJ = deep blue, toilets = flickering green, smoking =
cool blue, entry = venue pink. A venue may add ONE signature colour of its
own — Elevate's skyline gold, ROOM's sodium orange — and no more, or the
navigate-by-glow rule stops working.
- **Darkness stays the mechanic.** No venue may be bright enough to scan from
the doorway.
## 2. The Royal — "a dive with a dance floor"
Licensed 80 · regulars and punters · nobody checks anything.
A suburban Australian pub that had a dance floor bolted on in about 2004 and has
not redecorated since. Patterned carpet, a horseshoe public bar, and the dance
floor is plainly the least important thing in the building.
| Space | What's in it |
|---|---|
| **Public bar** (horseshoe, west) | the long counter, taps, till, keg stack, a TV showing the footy, bar fridges |
| **Pool room** (north-west) | two pool tables, cue rack, a dartboard on the wall |
| **TAB / pokies corner** (north-east) | bank of poker machines, TAB screens, stools, its own miserable light |
| **Bistro** (centre-north) | bistro tables, bain-marie counter, the meat-raffle drum |
| **Dance floor** (south-east) | small, an afterthought, one mirror ball, a jukebox on the wall |
| **DJ corner** | not a booth — a folding table with a controller, beside the dance floor |
| **Dunnies** (east) | a stainless TROUGH, two cubicles (one out of order), one basin |
| **Beer garden** (south, open sky) | picnic tables, umbrellas, patio heater, butt bin — the smoking area, and the nicest room in the pub |
Light: warm amber everywhere, green cone over each pool table, the pokies' own
sickly blue, and the beer garden cool blue. No pink except the dance corner.
## 3. Voltage — "valley institution"
Licensed 110 · the tuning baseline · the inspector drinks here.
**This is the existing map and it does not change.** It is tuned under the
darkness sheet, its reachability is asserted, and every existing floor test
targets it. It stays the default layout and the reference for the other three.
## 4. Elevate — "rooftop, small room, big opinions"
Licensed 70 · influencers and owner's mates · every second person is a VIP.
The whole venue is outdoors, which makes the rain a design element rather than a
yard-only effect. Small footprint — a third of the grid is sky. You arrive by
lift, not off the street, so the entry is a lobby.
| Space | What's in it |
|---|---|
| **Lift lobby** (west) | lift doors, a rope line, the stamp podium, one mirror |
| **Cocktail bar** (north) | short backlit bar, no taps — bottles and a champagne bucket |
| **VIP booths** (east) | banquettes behind velvet rope posts, low tables |
| **Dance floor** (centre, small) | it is a terrace with a dance floor, not a club |
| **DJ plinth** | raised, open, no booth walls — the DJ is on display |
| **Toilets** (north-east) | two unisex cubicles, marble basin, enormous mirror |
| **Terrace edge** (south + east) | glass balustrade, planters, the city skyline beyond |
| **Smoking** | the whole roof is open air; a designated corner with a standing ash urn and heat lamps |
Light: warm amber bar, cool blue-white terrace, deep blue DJ, and the venue's
own signature — **skyline gold** spilling from the city card beyond the glass.
## 5. ROOM — "no sign, no socials"
Licensed 60 · heads and chancers · the board is the law.
A concrete warehouse. No branding anywhere, because branding is how you get
found. The sound system is the most expensive thing in the building by an order
of magnitude and everything else is borrowed.
| Space | What's in it |
|---|---|
| **Main floor** | concrete pillars, no booths, the crowd is the furniture |
| **The stacks** | Funktion-One-style speaker towers + subs — huge, four corners |
| **DJ booth** | low, central, facing the floor, no barrier — CDJs not turntables |
| **Bar** (west) | a plywood plank on scaffolding, cans only, no taps, no till |
| **Toilets** (north-east) | industrial, graffiti on every surface, three cubicles of which one works, a cracked basin, no mirror |
| **Loading dock** (south, open sky) | roller door, pallets, a wheelie bin — this is the smoking area |
| **Chill-out** (north-west) | two ruined couches under a single bulb |
| **The board** | the rule board that rewrites itself (`shuffleDressCode`) — a physical prop by the entry |
Light: almost none. Sodium orange over the loading dock (ROOM's signature),
one flickering green fluoro in the dunnies, strobes on the floor, and the
stacks' own power LEDs. This is the darkest venue in the game on purpose.
## 6. Prop vocabulary added for these rooms
Route per SPACES.md §"Prop pipeline": **[F]** = flux direct (flat things —
signage, boards, screens, backdrops); **[M]** = product shot → TRELLIS.2 →
Blender ortho render (things with volume). Rake follows the house rule —
**60° low and wide, ~76° tall**.
| Venue | Kinds |
|---|---|
| The Royal | `pokie`[M76] `tabBoard`[F] `bistroTable`[M60] `picnicTable`[M60] `trough`[M70] `keg`[M76] `dartboard`[F] `jukebox`[M76] `raffleDrum`[M60] `bainMarie`[M60] `tvSport`[F] `beerUmbrella`[M80] `ashtray`[M60] |
| Elevate | `cocktailBar`[M60] `banquette`[M60] `ropePost`[M76] `planter`[M60] `balustrade`[M76] `djPlinth`[M60] `marbleSink`[M70] `ashUrn`[M76] `champBucket`[M60] `loungeChair`[M60] `liftDoor`[F] `skylineCard`[F] |
| ROOM | `stackF1`[M76] `subBass`[M60] `plywoodBar`[M60] `rollerDoor`[F] `pallet`[M60] `wheelieBin`[M76] `oldCouch`[M60] `pillar`[M76] `ruleBoard`[F] `strobe`[M76] `smokeMachine`[M60] `cableSnake`[M60] `brokenBasin`[M70] `canStack`[M60] `cdj`[M60] |
| Staff (set dressing, all venues) | `staffBartender` `staffDj` `staffGlassie` `staffSecurity` — all [M76] |
Staff figures are PROPS, not patrons. The procedural dolls stay exactly as they
are: `dollPlan` draws the specific items the dress code convicts on, so
replacing patrons with pre-rendered art would make the convictions invisible
(recorded in LANEHANDOVER, FABLE-SOLO-23). A bartender who never moves is set
dressing and carries no mechanical weight, which is why she can be a sprite.
Street plates (door scene, one per venue, [F], 640×176):
`street:royal` `street:elevate` `street:room``street:backdrop` stays Voltage's.