Compare commits

..

No commits in common. "main" and "lane/door" have entirely different histories.

306 changed files with 400 additions and 30475 deletions

View File

@ -1,15 +1,6 @@
{ {
"version": "0.0.1", "version": "0.0.1",
"configurations": [ "configurations": [
{ { "name": "not-tonight", "runtimeExecutable": "npm", "runtimeArgs": ["run", "dev"], "port": 5199 }
"name": "not-tonight",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run",
"dev"
],
"port": 5199,
"autoPort": true
}
] ]
} }

6
.gitignore vendored
View File

@ -1,6 +1,2 @@
node_modules node_modules/
dist/ dist/
art_incoming/
public/props/manifest.lock
__pycache__/
*.pyc

View File

@ -29,46 +29,7 @@ by parallel "lanes" coordinated through files, reviewed by Fable.
branch. An unpushed session didn't happen. branch. An unpushed session didn't happen.
- Remote is Gitea: `ssh://git@100.71.119.27:222/monster/not-tonight.git`. - Remote is Gitea: `ssh://git@100.71.119.27:222/monster/not-tonight.git`.
## Dev notes (venue expansion landed 2026-07-28) ## Dev notes (Phase 0 landed 2026-07-19)
- **The ladder's four venues now have four different rooms** (`docs/VENUES.md`).
Layouts live in `src/scenes/floor/layouts/` (`theRoyal`, `elevate`, `room`);
**Voltage stays in `venueMap.ts`** so the others can import primitives from it
without an import cycle. A night picks its room from `NightContext.venue.id`.
- Nothing about a room may be a module singleton any more. Props, lights, posts,
probes and the tile palette all hang off `FloorLayout`. `venueMap.VENUE/PROPS/
LIGHTS` still exist and are still Voltage's — they are the reference room, not
"the venue".
- **Dev route `#floor:<venueId>`** boots the floor demo straight into one room
(`#floor:room`, `#floor:theRoyal`, `#floor:elevate`). Plain `#floor`/F = Voltage.
- `tests/floor/layoutInvariants.ts` is the executable rulebook every layout runs:
perimeter, reachability, anchors on walkable tiles, posts not sealed, probes
reachable, props on-grid. Add a room, add it to `LAYOUTS`, it gets checked.
- Asset generation is `tools/gen/``assets.py` is the manifest (one entry per
sprite), `run_batch.py` walks it, resumable, `--workers N`. **Cloudflare Workers
AI first** (10k free neurons/day, resets 00:00 UTC), falling back to MODELBEAST
`flux_local` automatically when the day's allocation is spent. Meshes use the
**512 tier + vertex baker** (`mb.FAST_MESH`): ~90s vs ~6min, and the difference
does not survive the downscale to 32px. Heartbeat: `~/.jobs/not-tonight-assets.json`.
- `props_import.py` takes its sizes FROM `tools/gen/assets.py`, so sizes cannot
drift between the farm and the game. It locks `manifest.json` — parallel imports
used to lose entries to a read-modify-write race, which shows up as a prop that
is on disk but renders as a placeholder. `tools/gen/rebuild_manifest.py` repairs.
## Dev notes (Phase 2 landed 2026-07-19)
- The game boots into the NIGHT (Thu→Fri→Sat run, saved). Controls: click the
rope/verdicts at the door; click the venue doorway to step inside; on the
floor WASD + E (E at the entry exits back to the door). While you're inside,
Kayden works the door — badly, on purpose.
- Dev routes: N night · P parade · F floor demo · J juice demo (+ same-named
URL hashes) · M toggles the MIX DESK overlay anywhere (ear-pass rig).
`localStorage.removeItem('not-tonight-save')` for a clean run.
- Audio unlocks on first click (TechnoEngine + location filter). Before that a
StubBeatClock keeps `beat:tick` honest.
- Venue spaces/props/lights are DATA (`venueMap.PROPS`/`LIGHTS`, plan in
docs/SPACES.md). Generated art drops into `public/props/` via
`tools/props_import.py` + manifest — placeholders render until then.
## Dev notes (Phase 0, still true)
- Gate: `npm run lint && npm run build && npm test` — all three must pass before - Gate: `npm run lint && npm run build && npm test` — all three must pass before
any push. Dev server: `npm run dev` (port 5199, config in `.claude/launch.json`). any push. Dev server: `npm run dev` (port 5199, config in `.claude/launch.json`).
- Key APIs (read the source, it's short): `core/EventBus` (`on` returns an - Key APIs (read the source, it's short): `core/EventBus` (`on` returns an
@ -90,13 +51,3 @@ by parallel "lanes" coordinated through files, reviewed by Fable.
The game is Aussie hospitality satire — affectionate, funny, with a moral undertow The game is Aussie hospitality satire — affectionate, funny, with a moral undertow
(design doc §4.3). If content you're writing reads as mean-spirited rather than (design doc §4.3). If content you're writing reads as mean-spirited rather than
absurd, soften toward absurd. absurd, soften toward absurd.
## Deploy (live since 2026-07-19)
- Live at **https://monsterrobot.games/not-tonight/** (botchat VPS, forum-nginx
bind mount — host path `/home/humanjing/monsterrobot.games/games/not-tonight/`,
`/not-tonight` is a symlink to it; landing tile in `../index.html`).
- Ship: `npm run build && rsync -az --delete dist/ humanjing@100.71.119.27:/home/humanjing/monsterrobot.games/games/not-tonight/`
then cache-busted curl to verify. Hashed asset names make CF cache a non-issue;
index.html is text/html (uncached by CF default).
- Cover art: `covers/nottonight.jpg` + `_riso.jpg` (MODELBEAST flux_local seed
4208 + PIL stamp text/riso pass — recipe in LANEHANDOVER Phase-3 deploy note).

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
raw drops go here; process with tools/props_import.py

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 823 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 944 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@ -26,42 +26,16 @@ nike_air_max, moschino, quiksilver, kmart, disruptor_ii…), dynamically loaded
sprites before committing — pixelated photo-sprites can look mushy; may need a sprites before committing — pixelated photo-sprites can look mushy; may need a
cleanup pass or MODELBEAST-assisted redraw per sprite. cleanup pass or MODELBEAST-assisted redraw per sprite.
### 2. RIGROOM — the character × animation factory (surveyed 2026-07-19) ### 2. Drunk/idle/dance animation references
**This supersedes the old "animation references" note. The fleet has a full `ultra:~/Documents/90sDJsim/web/world/models/` — 22 ped GLBs + **9 anim clip GLBs
character/animation pipeline, live and organised:** incl. `walk_drunk`, idle, sit** (from the shared `character_kit` rig). Plus
- **RIGROOM library** (`ultra:~/meshgod-backups/library`, 2.4 GB): **607 `ultra:~/Documents/MOTIONLIB/` — 2,703 motion/anim FBX.
animation FBX** in themed packs + 59 character files. Web UI live at - Option A (cheap): use `walk_drunk` etc. as *visual reference* for hand-animating
`http://100.91.239.7:8010/rigroom` (characters × animations pairing, three.js the pixel sway/stumble frames.
preview, instant Mixamo-ready FBX download). CLI: - Option B (pipeline): pre-render `character_kit` characters + MOTIONLIB clips to
`MESHGOD/scripts/rigroom.py classify|sort|scan`. top-down sprite-sheet rotations for the Floor scene (Blender orthographic cam →
- Clips that read like THIS game's shot list: `Searching_Pockets` (pat-down), pixelate). Heavier, but gives unlimited consistent NPC animation. Decide at v0.4;
`Standing_Arguing` + a full boxing/knockout pack (floor fights), the doll contract's `floorTopDown` pose doesn't care which path wins.
`Shaking_Head_No` (the deny), `Inspecting`/`Looking_Around` (ID check),
`Leaning_Idle02_Smoking` (smoking area), dozens of idles/sits, dance packs,
`Brutal To Happy Walking`, zombie stagger (maggot walk), 12 DJ motions.
Raw source: `m3ultra:~/Documents/Rokoko_Free_Mocap_FBX_263` (606 FBX).
- **Modular character kit** (`m3ultra:~/Documents/character_kit_modular`,
canonical `ultra:~/Documents/character_kit/modular/`): GODVERSE socket
standard — head/hands/feet/torso swap on one shared 65-bone `mixamorig:`
skeleton, 40 embedded actions, every library clip retargets for free.
- **repose** (`m3ultra:~/Documents/repose`): photo → T-pose views (SD1.5 +
ControlNet, all-local) → MeshGod multi-view → autorig. A face becomes a
rigged character without leaving the tailnet.
- **For NOT TONIGHT (v0.4 art pass), the pre-render path is now fully paved:**
modular kit character + RIGROOM clip → Blender orthographic top-down/front →
pixelate → sprite sheets behind the existing `renderDoll` poses. The outfit
variety could even come from kit part swaps. Same pipeline, one venue's
worth of NPC animation for free. Decide at v0.4; contracts don't move.
- Also present, unsurveyed depth: `MOCAPGOD` (empty queue — service scaffold),
`mixamo_upload` staging, `MOTIONLIB` (2,703 FBX, ultra) still the deep pool.
### 2b. procity (m3ultra) — the 90s-Aussie town sim
Walkable procedural shopping town (three.js, no build step), built by lanes
like ours. Pipeline: 186 GLB/FBX with fit/normalize/decimate tooling
(`procity_fit_*` props: arcade cabinet, clothes racks, shelving), 21 peds.
The **prop pipeline conventions** (batch manifests, decimation results JSON)
are the pattern to copy if NOT TONIGHT ever goes 3D-prop-assisted; the venue
furniture (bar, booths, stools) could come straight from its fit pipeline.
### 3. Venue / street reference art ### 3. Venue / street reference art
- `ultra:~/Documents/90sDJsim/web/world/media/` named scene/venue art — 100 wired - `ultra:~/Documents/90sDJsim/web/world/media/` named scene/venue art — 100 wired

View File

@ -68,20 +68,15 @@ scene code. Beat sync example: audio engine emits `beat:tick` on the bus; the to
rhythm game consumes `beat:tick`; until integration, LANE-FLOOR uses the rhythm game consumes `beat:tick`; until integration, LANE-FLOOR uses the
`StubBeatClock` from core (same event, fixed 128 BPM). `StubBeatClock` from core (same event, fixed 128 BPM).
### Phase 2 — Integration — ✅ DONE 2026-07-19 (executed by Fable) ### Phase 2 — Integration (ONE lane, sequential)
Night flow machine: Door and Floor run concurrently all night, player toggles Night flow state machine (Door ⇄ Floor interleave, radio pulls, Last Drinks,
via the doorway / floor exit; Kayden rules the real queue while you're inside Summary), real audio engine replaces stubs, save/progression, Kayden-at-door
(80% admit, 1-in-7 unstamped); TechnoEngine + location filter + SFX live in the simulation while player is on Floor. Playtest pass: one full night start→summary.
night; ui/Stamp + MeterHud swapped in; Thu→Fri→Sat run with saved run-scoped
heat; 3rd strike = immediate LICENCE PULLED, run over. See the Phase-2 SESSION
block in LANEHANDOVER.md.
### Phase 3 — Content & polish (parallel lanes OPEN) ### Phase 3 — Content & polish (parallelizable again)
`lanes/LANE_CONTENT.md` (guest list, moral encounters, inspector, incident Archetypes, scripted moral encounters, inspector, incident report memory, regulars,
report), `lanes/LANE_FLOOR2.md` (fights, smoking area, aggro decay), venues 24, real pixel art swapped into the paper-doll layer slots, MODELBEAST-
`lanes/LANE_JUICE2.md` (ear pass, ambience, night arc). Later in this phase: assisted art generation if useful.
venues 24, real pixel art into the paper-doll slots (ASSETS.md), regulars'
cross-night memory, the economy tuning pass (John + reviewer, NOT a lane).
### Phase 4 — The role ladder (design §6; future, do not start) ### Phase 4 — The role ladder (design §6; future, do not start)
Glassie, bartender, DJ roles + roster board. Not scoped yet — but it imposes ONE Glassie, bartender, DJ roles + roster board. Not scoped yet — but it imposes ONE

View File

@ -120,18 +120,9 @@ export interface EventMap {
'meters:delta': { vibe?: number; aggro?: number; hype?: number }; 'meters:delta': { vibe?: number; aggro?: number; hype?: number };
'meters:changed': { vibe: number; aggro: number; hype: number }; 'meters:changed': { vibe: number; aggro: number; hype: number };
'heat:strike': HeatStrike; 'heat:strike': HeatStrike;
// folded in at the Phase-1 review (2026-07-19):
'incident:log': IncidentRecord; // single writer: core/meters.ts appends to NightState.incidents
'door:phoneTheatre': { durationMs: number };
'audio:unlocked': { atMs: number };
'beat:bar': { barIndex: number; beatIndex: number; audioTimeMs: number };
} }
``` ```
Phase-1 review also added: `PatronFlags.handHolding?` (CCR-1),
`VerdictOutcome.hypeDelta?` (CCR-2), `DressCodeRule.short?` (CCR-4).
`src/data/types.ts` remains the source of truth where this doc lags.
**Bus discipline:** scenes emit `meters:delta`; only `core/meters.ts` mutates state **Bus discipline:** scenes emit `meters:delta`; only `core/meters.ts` mutates state
and emits `meters:changed`. HUD listens to `meters:changed` only. This keeps three and emits `meters:changed`. HUD listens to `meters:changed` only. This keeps three
lanes from fighting over the same numbers. lanes from fighting over the same numbers.

View File

@ -1,91 +0,0 @@
# NOT TONIGHT — Character, Scenario & Interaction Backlog
*The idea shelf. Everything here obeys §4.3: choices cost something real, the
game never grades them, and the humane option is never secretly optimal.
Status: ✅ implemented · 🔜 next nibbles · 💡 backlog.*
## Interactions & mini-systems
- ✅ **The joint bust** (smoking yard). E on a smoker who smells herbal → three
verbs, none of them clean: **BIN IT** (by the book — write-up in the truth
log, smoker's night dampened), **POCKET IT** (the game's first corruption
verb — nothing happens now; maybe a Dazza sting later; and it sits in the
incident log waiting for the report form, where your account of it can be a
lie the audit remembers), **DIDN'T SEE IT** (the yard relaxes, vibe drifts
up… and it's still on the record as ignored). 'joint' is real contraband, so
it also turns up in pat-downs at the rope.
- ✅ **The glassie run** (the glassie role's fantasy, previewed as a floor
task). A wire rack of glasses appears at the bar's east end; carry it the
length of the venue to the kitchen dishwasher. Crowd bumps and sharp turns
build WOBBLE; wobble breaks glasses one smash at a time. Deliver what
survives for vibe. Racks keep coming. The door is unattended while you do it
— Kayden is thrilled.
- ✅ **ID passback detection** (2026-07-20): the same card twice in one night — the name on
the guest-list clipboard you already checked; second appearance of an
identical card = passback. Needs a "cards seen tonight" memory at the desk.
- ✅ **Regular step-up lines** (2026-07-20) keyed off timesDeniedBefore ("back again. new
moustache. no hard feelings").
- ✅ **Smoking-door slip-in** (2026-07-20) (FLOOR2 §2's second noStamp source).
- ✅ **The 1:30 lockout** (2026-07-20) (deeply Queensland): after 1:30 AM, admissions are
ILLEGAL. The queue still forms; Dazza still wants bodies; every post-lockout
admit is a guaranteed strike risk vs a rioting line. A whole late-game phase
in one law.
- ✅ **Vomit protocol** (2026-07-20): someone redecorates the dance floor → deploy the wet
floor sign (we own the prop) → slips if you dawdle; the glassie mop loop.
- ✅ **Phone confiscation** (2026-07-20): someone filming on the dance floor — the
influencer's revenge arc.
- ✅ **Water station duty** (2026-07-20) (RSA): press a cup on a `loose` patron; done right
it de-escalates the ripen timer. The humane tool that costs time.
- ✅ **First aid** (2026-07-20): a fainter. Recovery position via a QTE that is deliberately
NOT funny; the room hushes; the night resumes changed.
- ✅ **Kebab run** (2026-07-20): Dazza demands a kebab mid-slam. The shop is right there.
The queue can see you. The whole game in one errand.
## Characters (door archetypes & scripted people)
- ✅ **The off-duty cop** (2026-07-20) — flashes a badge at the rope. Real cop (deny freely,
he respects it; admit and he quietly reports the venue's breaches — a
walking inspector) or a $30 costume badge (the tells: hat, confidence,
wrong lanyard). The ID game's scariest bluff.
- ✅ **The celebrity incognito** (2026-07-20) — cap + sunnies (two live rule breaches!).
Deny: nothing happens, ever, and Dazza never finds out who. Admit: vibe
rocket OR pap swarm outside (aggro storm). You cannot know which.
- ✅ **The twins** (2026-07-20) — two people, one face, different names. Not a fake:
genuinely twins. Everything about the photo check is suddenly on trial.
- ✅ **The sovereign citizen** (2026-07-20) — "I don't recognise your dress code as having
authority over my person." Aggro engine in a polo. His ID is a laminated
business card.
- ✅ **The delivery rider** (2026-07-20) — insists the DJ ordered food. The DJ did not
order food. OR DID HE. (The booth lamp flickers hungrily.)
- ✅ **The promoter** (2026-07-20) — arrives with a roll of wristbands and 'his' guest list
that doesn't match yours. Two clipboards enter, one leaves.
- ✅ **The noise-complaint neighbour** (2026-07-20) — pyjamas, at the rope, at 1 AM, not
trying to get in: trying to get it turned DOWN. The only patron who wants
less club. Handle him badly and the inspector hears about it.
- ✅ **The hen's party, lost** (2026-07-20) — seven matching sashes, wrong venue, right
energy. Admitting them is a vibe grenade with a random timer.
- ✅ **The ex-doorman** (2026-07-20) — watches you work a patron, then tells you what you
missed (a live tutorial character; his tips are right ~80% of the time,
which is the joke).
## Systems worth a whole phase
- ✅ **Venue 4 / ROOM boss week** (2026-07-21, first cut): fourth rung on the
ladder (promotes from Elevate). No Dazza rule chronology — the board IS the
law and re-randomises every 15 in-game minutes (3 rules per window, seeded,
with a toast on the turn); every rope line renders as bracketed lip-read
guesses (`[FIST? LIST?]`) because the techno ate the dialogue. 60 licensed,
1.2 arrival scale, inspector working, crowd of heads and VIP-claimers.
- ✅ **The DJ shift** (2026-07-20, first cut): E at the booth hands you the decks
while the resident steps out. B pulls the bass (the room leans in); SPACE
drops, judged against the LIVE bar line (clean/late/clang — early and late
are the same offence); requests arrive at the booth glass with per-request
odds ("got any dnb", nan's song, the DUN DUN one) and three answers that all
cost something. A build held too long runs out of stairs. Movement locks
while you hold them; the floor and the door keep happening without you.
- ✅ **Bartender shift** (2026-07-21, first cut): E at the till hands you the
taps (movement locks; the floor keeps happening). Orders arrive as choices —
the order line degrades with the patron's stage (the spelling IS the sobriety
test) — SERVE / WATER / CUT OFF all legal, all priced. Serving messy/maggot
is the RSA breach: the vibe pays up front, and 35% of the time somebody with
a lanyard saw it (ONE deferred write-up per night, deduped by reason).

View File

@ -1,162 +0,0 @@
# NOT TONIGHT — Spaces Plan
*How the game's two spaces grow from readable-but-abstract into places, using the
fleet's asset factories. Written 2026-07-20. Code contract: everything lands
behind data tables (`venueMap.PROPS` / `LIGHTS`) and a sprite drop-in convention —
generated art replaces placeholders with zero code changes.*
> **Superseded in part, 2026-07-28 — see [VENUES.md](VENUES.md).** This document
> describes ONE venue floor, which is what the game had when it was written. The
> venue ladder now has four distinct rooms, and props/lights/posts/probes hang
> off a per-venue `FloorLayout` rather than the module singletons named below.
> The pipeline sections (§4 sprite drop-in, and the settled prop pipeline at the
> foot of this file) are still exactly right and still the house rules — only
> "the venue floor" in §2 is now specifically *Voltage's* floor.
## 1. Design principles
1. **Darkness stays the mechanic.** Props read as silhouettes in ambient dark and
resolve under the torch. Nothing may make the room bright enough to scan from
the doorway.
2. **Navigate by light signature.** Each zone owns a colour of light, so the
player orients by glow before geometry: bar = warm amber · dance floor =
pulsing pink (on the actual beat) · DJ booth = deep blue · toilets =
flickering green fluoro · smoking yard = cool rain-blue spill · entry/exit =
venue pink. The torch is white; UV is purple. No other light sources.
3. **Props are data.** `PropDef { kind, tile pos, footprint, emissive? }` — the
renderer prefers a generated texture (`gen:prop:<kind>`) and falls back to a
procedural placeholder, so the game is complete TONIGHT and gets prettier as
art lands.
4. **Spaces anticipate the role ladder** (design §6): the bar interior is laid
out for a future bartender shift, the DJ booth for the DJ shift, the glassie
station and smoking yard for floor loops that already have lanes speccing them.
## 2. The venue floor (80×45 tiles, one room + pockets)
```
┌──────────────────────────────────────────────────────────────────┐
│ N ═ BAR ══════════ back-shelf glow ┌─ DUNNIES ────────┐│
│ taps · till · glasses │ s1 s2 s3 s4 ││
│ ○○○○○○ stools │ sinks + mirror ││
│ └───door───────────┘│
│ W │
│ ┈entry┈ ┌ DANCE FLOOR (pulsing grid) ┐ ╔═ DJ BOOTH ╗ booths│
│ corridor │ speaker · · · · · speaker │ ║ decks+mixer║ E│
│ posters │ · · · disco ball · · · │ ║ (sealed) ║ booths│
│ cloak │ speaker · · · · · speaker │ ╚════════════╝ │
│ glassie └───────────────────────────┘ booths│
│ station │
│ ▓ SMOKING YARD (open sky, rain) ▓ tables+candles S │
└────────▓ fence · butt bin ▓──────────────────────────────────────┘
```
Zone-by-zone (tile coords in `venueMap.ts`):
| Zone | What's there (props) | Light signature | Future role hook |
|---|---|---|---|
| **Bar** (north run) | back-bar shelf (emissive), 3 beer taps, till, glass stacks | steady amber pools along the shelf | Bartender shift: the serving lane between bar tiles and shelf is already unwalkable-to-patrons |
| **Dance floor** (centre) | 4 corner speaker stacks, disco ball (centre), floor grid cells | pink grid cells pulse ON `beat:tick`; ball throws a slow-rotating glint | DJ role reads crowd here |
| **DJ booth** (east of dance, sealed box facing the floor) | 2 decks + mixer (from the 90sDJsim gear GLBs!), booth monitor, lamp | deep blue wash + VU flicker | The DJ shift's playable pocket |
| **Booths** (south + east walls) | table per booth pair, candle | tiny warm candle pools | — |
| **Dunnies** (NE walled room) | sink row + mirror on west wall, hand dryer | green fluoro, FLICKERS (torch almost redundant inside — deliberate) | — |
| **Smoking yard** (SW walled pocket, open sky) | butt bin, milk crate seats, fence | cool blue outdoor spill + rain particles INSIDE the yard | FLOOR2's smoking loop (specced) |
| **Entry corridor** (west) | poster wall (3 art slots — MODELBEAST posters!), cloakroom window, stamp podium | venue-pink spill from the door | UV re-entry checks happen here |
| **Glassie station** (bar's west end alcove) | glass racks, tub trolley | dim amber | Glassie role's home base |
Sim impact: walkability only changes where NEW walls go (DJ booth box, yard
fence, sink row). All existing anchors keep working; reachability is asserted in
tests. The yard gets a `smoke` anchor upgrade so wanderers actually go out for one.
## 3. The door street (640×360 tableau)
Already reads well. Two upgrades, both drop-in images (no layout change):
1. **Backdrop plate** behind the existing rects: one wide pixel-art street wall
(brick, posters, grime) — swaps in as `gen:street:backdrop` when generated.
2. **Kebab shop interior glow card** in its window + rotating poster art in two
street frames (same poster slots as the entry corridor).
## 4. The sprite drop-in contract (how John's art lands)
- Drop processed PNGs in **`public/props/`** named `<kind>.png` and list them in
**`public/props/manifest.json`** (`{"props": ["deck", "speaker", ...]}`).
- The floor loads the manifest at boot; anything listed replaces its placeholder
(texture key `gen:prop:<kind>`). Missing = placeholder. No code changes.
- **`tools/props_import.py`** (PIL, runs anywhere with python3+PIL — ultra has it)
takes raw generated images → target pixel size + palette quantise + transparent
background → writes `public/props/` + updates the manifest:
`python3 tools/props_import.py incoming/deck_raw.png deck`
- Target sizes are in the script (`TARGETS`): hero props 4864 px, furniture
2432 px, small items 16 px. It refuses unknown kinds so typos can't ship.
## 5. GENERATION QUEUE — John's list (in priority order)
Pipelines: **[MB]** = MODELBEAST `flux_local` (params given) → drop raw output in
`art_incoming/`, run the import script (or hand me the raws — either works).
**[GLB]** = Blender ortho prerender of an existing fleet mesh (camera: orthographic,
top-down 90° for floor props / front 0° for the street, transparent film, ~512px,
neutral even lighting) → same import path.
| # | Item (kind) | Pipeline & source | Notes |
|---|---|---|---|
| 1 | `deck` | [GLB] `ultra:~/Documents/90sDJsim/web/world/models/gear/deck_t3_technics_sl_1200mk2.glb`, top-down | The hero. One render serves both booth decks |
| 2 | `mixer` | [GLB] same dir, `mixer.glb` (or the urei once landed), top-down | Sits between the decks |
| 3 | `speaker` | [GLB] 90sDJsim `models/props/` has boombox/speaker meshes; else [MB] prompt: "top-down view of a large black PA speaker stack, pixel art game sprite, dark background, viewed directly from above" 512×512 seed 4210 | 4 placements |
| 4 | `discoball` | [MB] "mirrored disco ball seen directly from above, glinting, pixel art sprite, black background" 512×512 seed 4211 | Centre of dance floor |
| 5 | `barShelf` | [MB] "top-down bar back-shelf with glowing bottles rows, amber light, pixel art sprite, dark" 1024×256 seed 4212 | Emissive — the amber source |
| 6 | `taps` | [MB] "row of three beer taps on a bar counter viewed from above, pixel art, dark bg" 256×256 seed 4213 | |
| 7 | `boothTable` | [MB] "small round pub table with two drinks seen from directly above, pixel art, dark bg" 256×256 seed 4214 | 6 placements |
| 8 | `sink` | [MB] "public bathroom sink row with mirror viewed from above, green fluorescent light, pixel art, dark" 512×256 seed 4215 | |
| 9 | `buttBin` + `crate` | [MB] one sheet: "outdoor smoking area props: cigarette butt bin and milk crate, top-down pixel art, dark bg" 512×256 seed 4216 | Yard dressing |
| 10 | `poster1..3` | [MB] three runs: "grimy nightclub gig poster, 90s Australian pub rock / techno rave / missing cat, pixel art, portrait" 256×384 seeds 4217-19 | FRONT view (posters hang on walls) — used at door + corridor |
| 11 | `street:backdrop` | [MB] "wide pixel art night street wall: dark brick, torn posters, graffiti, neon reflections, rain, front-on orthographic, game background" 1600×440 seed 4220 | The door scene plate. Biggest single visual win |
| 12 | `cloak` / `glassRack` | [MB] "cloakroom window with hanging jackets / metal glass rack with stacked pint glasses, top-down pixel art, dark" 256×256 seeds 4221-22 | Corridor + glassie station |
| 13 | `arcade` | [GLB] `m3ultra:~/Documents/procity/pipeline/_normalized/procity_fit_arcade_cabinet_01.glb`, top-down | Corner easter egg near entry |
**Ambition tier (later, not blocking):** pre-rendered patron sprite sheets from
the modular character kit + RIGROOM clips (`Searching_Pockets`, boxing pack,
`Leaning_Idle02_Smoking`, zombie stagger for maggots) — replaces the procedural
dolls entirely per docs/ASSETS.md §2. Wants its own session to set the
palette/density once.
*(flux params for all [MB] jobs: `model flux2-klein-4b, steps 4`; put "no text,
no lettering" in every prompt EXCEPT the posters, where mangled text reads as
authentic gig-poster grot.)*
## Prop pipeline — settled 2026-07-22 (A/B'd, not guessed)
Two routes, both free and unlimited on MODELBEAST. Pick by object, not by habit.
**[MB] flux direct** — `flux_local``tools/props_import.py`. Fast (~9s).
Use for FLAT things: posters, decals, graffiti, signage, floor markings.
**[GLB] TRELLIS → Blender** — `flux_local` (product shot) → `trellis2_mlx`
(`asset_id` is a TOP-LEVEL job field, NOT inside `params`) →
`tools/glb_to_sprite.py <mesh.glb> <kind>` → sprite + manifest, headless, no
GUI. Use for objects with real VOLUME: tables, fridges, machines, furniture.
Why the split, from the A/B (same pool table, both routes): flux picks its own
camera every generation — the same prompt style gave a perfectly top-down
urinal and a front-on fridge in one batch, and mismatched camera angles never
read as one room. The mesh route renders every prop from one fixed ortho
camera, and TRELLIS carries detail flux flattens: worn felt, real pockets,
rails and legs, clean alpha.
**RAKE by object HEIGHT — and I had this backwards first time.**
Low and wide (pool table, booth table, crate): **60°**. Tall (arcade, fridge,
speaker, anything standing): **~76°**. The instinct to *lower* the rake for tall
objects "so you see more face" is wrong — a tall object already shows nothing
but face; it needs to be steeper to read as a floor prop at all. A speaker at
55° is a featureless black slab.
**Judge sprites at SHIP size, not render size.** A 512px render of a fridge from
above looks ambiguous; the same sprite at 32x32 under the game's own point light
reads fine, because the venue's LIGHTS do half the identifying work (the fridges
carry a cyan glow, the arcade a flickering blue). Do not re-roll a prop off the
back of the big preview.
**The old guidance, kept because it is still true for low objects:** The venue's props are
three-quarter top-down, not straight down. Re-rolled from directly overhead, a
bar fridge is a grey rectangle — physically correct and visually dead, because
the glowing bottles that say "fridge" are on its front. 60° keeps the top
surface dominant while leaving enough face to identify the object at 32px.

View File

@ -1,150 +0,0 @@
# 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.

View File

@ -1,74 +0,0 @@
# LANE-CONTENT — Door depth & the moral layer (Phase 3, parallel)
**Executor:** Opus 4.8. **Branch:** `lane/content` (from main after Phase 2).
**Owns:** `src/scenes/door/`, `src/rules/`, `src/data/strings/` (door-related
files), tests. **Prereq reading:** CLAUDE.md → `docs/GAME_DESIGN.md` §3.1/§4 →
the Phase-2 REVIEW block in `LANEHANDOVER.md`.
## Phase-2 reality (read before coding)
- The night is now a real flow: `NightScene` owns clock/meters/audio/save and a
`NightContext` (see its interface — `sfx`, `requestLocation`, `beatIntervalMs`
are new since Phase 1). Door and Floor run concurrently ALL night; the player
toggles via the doorway ("step inside") and the floor's entry exit.
- While the player is on the floor, **Kayden rules the real queue** in
`DoorScene.kaydenRule()` (admit 80%, same `judge()`, no UV stamp 1-in-7 —
stamp accuracy is decided in `NightScene.onVerdict`).
- Heat is run-scoped and saved; 3rd strike ends the night AND the run
immediately (`endReason: 'licence'`). Nights chain Thu→Fri→Sat via
`NightSummaryScene.nextAction()`; `save.ts` carries `GameState`.
- `ui/Stamp` + sfx are live in the deny path; `data/strings/door.ts` has
`FAIL_TEXT.licence`.
## Deliverables (in priority order)
### 1. The Guest List clipboard (design §3.1 — specced, never built)
A desk clipboard panel: tonight's list (generated, 814 names, some misspelled
versions of real patron names). Patrons with `claimsGuestList` get a "'m on the
list" line when they step up; player cross-checks. `knowsOwner` patrons who are
DENIED trigger the existing furious-Dazza path (judge already scores this —
surface it: their step-up line should drop hints ("Terry said swing by"), and
`data/strings/dazza.ts` has the `owner-scare` text to foreshadow).
Misspelling rule: a name that ALMOST matches is usually legit (bad handwriting);
an exact-match name on a confident patron is sometimes a lift. Tells, not
certainty. Pure logic in `rules/guestList.ts` + tests.
### 2. Scripted moral encounters (design §4.3 — the heart)
23 scripted patrons injected per night by the generator override hook
(`generatePatron(ctx, clockMin, archetypeOverride)` + a new scripted-encounter
scheduler in `rules/` or `data/`): the girl who needs back in to grab her mate
(no stamp, over capacity), the freshly-dumped regular who just wants one quiet
beer (already `loose`). Multi-beat dialogue via the existing toast/dialogue
patterns. Doing the human thing costs Vibe or risks Heat; the game never says
which choice was right. NO achievement chimes, no morality meter — the design
doc is explicit that this stays unscored.
### 3. The Inspector (archetype exists, does nothing yet)
Activate `inspector`: reads as a boring `punter` at the rope (their doll is
deliberately unremarkable). If admitted, they observe for ~30 clock-min:
any live breach in that window (capacity over licensed, a `maggot` on the floor,
underage inside) converts its deferred strike to IMMEDIATE. Then they leave and
Dazza texts "that was the INSPECTOR you muppet". If denied — nothing happens,
ever (they were just a quiet person; paranoia is the tax). Pure logic
`rules/inspector.ts` + tests; wiring via bus events only.
### 4. The Incident Report (design §3.3 — lying groundwork)
On the Night Summary: a short form (34 incidents sampled from
`NightState.incidents`) where the player picks a written account per incident —
truthful or flattering. Store both truth and account in
`GameState.pastReports`. Cross-night audit (contradictions → strikes) is
Phase 4; THIS lane only builds the form + storage + one taste: a lie about a
`kayden` incident can be contradicted by Kayden's own log line next morning
(Dazza text). Keep the form fast — 20 seconds, not homework.
### 5. Sobriety line-walk (stretch)
The third challenge: cursor-follow line at the desk. Only if 14 land clean.
## Constraints
- All scoring through `judge()`/`meters:delta`/`heat:strike` — no new direct
meter paths. New events need a CONTRACT CHANGE REQUEST (declaration-merge
bridges are BANNED now — request and wait for the fold).
- Kayden must interact sanely with everything you add (e.g. he auto-admits
guest-list liars — that's fine and funny; make sure it can't crash).
- Every pure module tested; play a full 3-night run before your handover and
report what the run FELT like — the economy findings from Phase 1 are still
open and your content changes the mix.

View File

@ -1,62 +0,0 @@
# LANE-FLOOR2 — Floor completion & aggro physiology (Phase 3, parallel)
**Executor:** Opus 4.8. **Branch:** `lane/floor2` (from main after Phase 2).
**Owns:** `src/scenes/floor/`, `src/data/strings/floor.ts`, tests. **Prereq
reading:** CLAUDE.md → `docs/GAME_DESIGN.md` §3.2 → Phase-2 REVIEW block in
`LANEHANDOVER.md`.
## Phase-2 reality (read before coding)
- `FloorDemoScene` now runs in TWO modes: standalone `'FloorDemo'` (key F,
unchanged) and night mode `'Floor'` (launched by NightScene with a
`NightContext`; see `bindNight()`). Night mode: shared bus/clock/state, fed by
real door admissions (`door:verdict` → delayed `crowd.spawn`), `MeterHud` for
HUD, exit via E at the entry anchor, sim keeps running while the player is at
the door (`present === false` path in `update()`).
- Incidents flow through `incident:log` (single writer in core/meters).
- `NightContext.sfx` exists (SFX handle, always crisp past the location filter).
- Kayden's unstamped admits (1 in 7) are your `noStamp` supply in night mode.
## Deliverables (in priority order)
### 1. Fight brewing (design §3.2 — the last missing v0.2 infraction)
Two agents in escalating shove animation (crowd sim picks pairs with rising
aggro contribution when they collide repeatedly). A swing timer (~8s from first
shove): player must physically walk BETWEEN them before it ends. Interposed =
defused (+vibe, both marked handled); missed = fight (big vibe, +aggro, both
auto-ejected, `incident:log` kind `fight`, and if the inspector is inside —
LANE-CONTENT's problem — it's a heat risk). Pure pair-selection + timer logic
in `floor/fights.ts`, tested. While the player is at the door, fights still
happen (that's radio-pull pressure — emit a `radio:call` urgency 3 via the
night's existing pattern: log the incident, NightScene already radios on
`maggotUnhandled`; add `fight` to that trigger via CONTRACT CHANGE REQUEST or
ask the reviewer to widen the trigger — one-line, will be approved).
### 2. Smoking area & pass-outs (closes the UV loop from the door side)
The smoking-area door becomes live: patrons drift out for a smoke (activity
'smoking' exists) and BACK IN through the entry. Re-entering smokers keep their
stamp — but a small % of NEW patrons try to slip in through the smoking door
(no stamp, never saw the front door). That's the noStamp mechanic getting a
source that isn't only Kayden. Numbers small (23 a night), tells readable
(they enter from the wrong door if you're watching).
### 3. Aggro physiology (FLOOR-2 CCR, deferred at Phase-1 review — now approved)
Implement slow aggro decay on a calm floor: when no unhandled infraction exists
and no fight is brewing, drift aggro down ~0.3/clock-min (numbers yours to
tune, state the final value in the handover). Implementation note: this is a
sim-level `meters:delta` emit from the floor's update, gated on `present`
being EITHER value (a calm floor calms the night whether you're watching it or
not). Add tests for the gating logic.
### 4. Night-mode polish
- Stall bangs and pat-down bin drops should hit `NightContext.sfx`
(`doorBang`, plus pick something for the bin thunk) — overlays take an
optional sfx handle via their constructors.
- Escorted patrons should exit via the ENTRY in night mode (they're being
walked out the front, past the queue — future door-side schadenfreude hook).
- Perf check at 44 agents + fights + smokers in night mode; report frame times.
## Constraints
- Demo mode must stay working and identical-feeling (it's the tuning harness).
- No imports from `scenes/door/`; the night talks to you via bus + context.
- Pure logic tested; verify each mechanic by playing BOTH modes; handover per
protocol.

View File

@ -1,59 +0,0 @@
# LANE-JUICE2 — The night has a sound (Phase 3, parallel)
**Executor:** Opus 4.8. **Branch:** `lane/juice2` (from main after Phase 2).
**Owns:** `src/audio/`, `src/ui/`, tests. **Prereq reading:** CLAUDE.md →
LANE_JUICE.md (your Phase-1 spec) → Phase-2 REVIEW block in `LANEHANDOVER.md`.
## Phase-2 reality (read before coding)
- `TechnoEngine` + `Sfx` now live inside the actual night (`NightScene` creates
them; unlock on first pointerdown; `StubBeatClock` hands over on
`audio:unlocked`). The location filter sweeps on every doorway/exit toggle.
- `audio:unlocked.atMs` now carries the ANCHORED beat-0 origin (your open
question, ruled and implemented).
- `MeterHud` has `initialHeat` (ruled: read run-scoped strikes at construction —
wired in the floor's night mode).
- `ui/Stamp` is the door's stamp, with `stampSlam`/`stampInk` wired. The decal
is destroyed ~900ms post-slam by DoorScene.
- Your `ui/juiceEvents.ts` was deleted at review — its events live in
`data/types.ts` now. Extensions go via CONTRACT CHANGE REQUEST only.
## Deliverables (in priority order)
### 1. Ear pass with John (the debt from Phase 1: "verified by ear: NOTHING")
Sit a human in front of the JuiceDemo AND a real night. Tune: does the kick
read as a kick, is the bassline musical, `typeTick` level (it fires per
character), stamp slam vs music balance, the door→floor sweep (does it feel
like a door opening). Document every constant you change and why. This is THE
deliverable — everything below is secondary.
### 2. Ambience layer
- `rainLoop` at the door (start/stop on `audio:location` — rain is outside).
- A crowd-murmur bed INSIDE (filtered noise, level follows
`NightState.capacity.inside` — read via a bus listener on `meters:changed`?
No — capacity isn't in that payload; request what you need or derive from
`door:clicker`/`door:verdict` counting. State your approach in the handover).
- The kebab-shop fluoro hum if you're feeling artistic (tiny, dry, mono).
### 3. The night has an arc
Track intensity should rise across the night: thin kick+hat at 9 PM, bassline
enters ~10, pads and full mix by midnight, strip back at Last Drinks. Drive it
from `clock:tick` (you know the clock minute). Keep every parameter externally
settable — this surface IS the future DJ role's control panel (BUILD_PLAN
Phase 4 rule).
### 4. Widget debts
- `Phone` thread: scroll instead of truncating the newest over-tall message.
- `DialogueBox` drunk-typo mode: wire into the floor cut-off overlay (it
currently renders plain text — coordinate via handover, LANE-FLOOR2 owns the
call site; you own the widget).
- Optional: `MeterHud` variant sized for the door's top bar, so DoorScene can
drop its bespoke `MeterBar`s (currently duplicated look). Only if clean —
the door HUD works and this is cosmetic unification, not a rescue.
## Constraints
- Location filter architecture is non-negotiable: music through the lowpass,
SFX and ambience around it (rain is OUTSIDE — it belongs on the sfx bus but
only audible at the door; that's a routing decision, justify yours).
- No imports from door/floor scenes. Bus + constructor config only.
- Pure-testable math tested; everything else honestly listed as ear-checked or
not, per your own Phase-1 standard.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 746 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 B

View File

@ -1,131 +0,0 @@
{
"props": [
"arcade",
"ashUrn",
"ashtray",
"bainMarie",
"balustrade",
"banquette",
"barFridge",
"barShelf",
"barrier",
"beerUmbrella",
"binRound",
"bistroTable",
"boothLamp",
"boothTable",
"brokenBasin",
"buttBin",
"cableSnake",
"canStack",
"carpet",
"cctv",
"cdj",
"ceilingFan",
"champBucket",
"cloak",
"cocktailBar",
"crate",
"cubicleDoor",
"cueRack",
"dartboard",
"deck",
"discoball",
"dishwasher",
"distroBox",
"djPlinth",
"exitSign",
"fireExt",
"fireHose",
"firepit",
"flightCase",
"front_elevate",
"front_room",
"front_theRoyal",
"front_voltage",
"glassRack",
"glassStack",
"graffiti",
"handDryer",
"highTable",
"iceWell",
"jukebox",
"kebabShop",
"keg",
"liftDoor",
"loungeChair",
"marbleSink",
"mirror",
"mixer",
"monitor",
"mopBucket",
"noticeBoard",
"oldCouch",
"pallet",
"patioHeater",
"picnicTable",
"pieWarmer",
"pillar",
"plant",
"planter",
"plywoodBar",
"pokie",
"poolTable",
"poster1",
"poster2",
"poster3",
"poster4",
"poster5",
"poster6",
"poster7",
"poster8",
"poster9",
"raffleDrum",
"rollerDoor",
"ropePost",
"ruleBoard",
"sandwichBoard",
"sinkRow",
"skylineCard",
"smokeMachine",
"speaker",
"stackF1",
"staffBartender",
"staffChef",
"staffCleaner",
"staffDj",
"staffGlassie",
"staffSecurity",
"stampPodium",
"stanchion",
"streetBin",
"streetLamp",
"street_backdrop",
"street_elevate",
"street_room",
"street_theRoyal",
"strobe",
"subBass",
"tabBoard",
"taps",
"taxi",
"tex_brick",
"tex_carpet",
"tex_concrete",
"tex_deck",
"tex_looTile",
"tex_parquet",
"tex_paving",
"tex_plaster",
"tex_steel",
"tex_timber",
"till",
"topiary",
"trough",
"truss",
"tvSport",
"urinal",
"wetFloor",
"wheelieBin"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 888 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 778 B

Some files were not shown because too many files have changed in this diff Show More