# LANE-FLOOR — The Floor (Phase 1, parallel) **Executor:** Opus 4.8. **Branch:** `lane/floor` (from main after Phase 0 merge). **Owns:** `src/scenes/floor/` + its tests. **Prereq reading:** `docs/GAME_DESIGN.md` §3.2 → `docs/CONTRACTS.md` → `LANEHANDOVER.md` (check for REVIEW blocks for you). ## Deliverable `FloorDemoScene` runnable via `npm run dev` (menu key F): a populated top-down venue you patrol with a flashlight, containing live instances of all three v0.2 infractions (cut-off, toilet stall, pat-down) + UV stamp checks. Runs standalone — it self-populates from the patron generator; integration (Phase 2) will hand it the real admitted-patron list instead. Code against `Patron[]` input from day one. ## Phase-0 reality (read before coding) Phase 0 landed 2026-07-19 — see CLAUDE.md "Dev notes" for the API map and the browser-pane verification trick. Floor-relevant specifics: - Drunk thresholds exist in `rules/drunk.ts` (`drunkStage`, `hasVisibleTells`) — no local copy needed; delete that TODO from your plan. - `renderDoll(scene, patron, 'floorTopDown')` already produces the 16×16 top-down doll (hair blob over shoulders/top colour). - `StubBeatClock` exists in core; instantiate it in your demo scene and call `update(delta)` each frame — it emits `beat:tick` at 128 BPM. - `generatePatron` + `ParadeScene`'s `resetRun` lifecycle are the reference patterns for self-populating your demo. - Register your demo scene in `src/main.ts`'s scene list; launch order stays Boot → Parade. Menu keys (F for floor demo) can live in ParadeScene as a scene-switch keybind — that one-line touch outside your dirs is pre-approved. ## Scene: the venue (top-down, ~2× screen scrollable) Tilemap (hand-authored array is fine, no Tiled dependency): entry corridor, bar (along a wall, stools), dance floor (centre), booths, toilets (2 stalls each, door tiles), smoking-area door. **Darkness is the aesthetic:** black base with neon accent strips; use Phaser Light2D (`setPipeline('Light2D')`) — ambient very low, neon point lights, and the player's cone. ### 1. Player & flashlight WASD/arrows patrol, slightly heavy acceleration (you're a big unit). Flashlight cone in facing direction — a masked light + a cone polygon that REVEALS detail: outside the cone patrons render as dim silhouettes (tint), inside it their full doll colours + tell animations show. TAB toggles **UV mode**: cone goes purple, normal detail hidden, `uvStamped` hands glow green. No stamp glow on a patron who re-entered = infraction (march them doorward: walk-behind escort, below). ### 2. Crowd sim (`floor/CrowdSim.ts`) Patrons wander between anchor points (bar → dance floor → booth → toilet loop) with per-archetype weights. Dance-floor patrons bob **on `beat:tick`** (StubBeatClock until integration — never your own timer). Intoxication drifts up over clock time (bar visits accelerate it); drunk-stage thresholds from `rules/` if LANE-DOOR has landed them, else local `// TODO(contract)` copy. Visible drunk tells escalate: sway amplitude, stumble pathing, bumping others (small vibe leaks via `meters:delta` if un-handled). ### 3. Infraction: The Cut-Off Approach a `messy`+ patron, interact (E) → dialogue tree modal: three escalation lines ("Have some water, mate" → "You're done for the night" → firm escort). Right escalation level for their stage = clean ejection (+vibe, defeated-slouch walk you escort to the exit); too aggressive on a merely `loose` patron = scene (−vibe, +aggro); too soft on a `maggot` = they stagger free, try again. Emit `floor:infractionSpotted` / `floor:ejection` per contract. Leaving a `maggot` unhandled for >N clock-min queues a deferred heat risk (v0.1: log to `NightState.incidents`; inspector wiring is Phase 3). ### 4. Infraction: Toilet stalls (the rhythm game) Stall doors show feet beneath: TWO pairs under one door = infraction. Interact → close-up view of the door + a beat bar fed by **`beat:tick` events only** (do not infer beat from elapsed time; measure hit accuracy vs the last tick's `audioTimeMs`). Bang (SPACE) on-beat 3× consecutively: door flies open, two patrons scatter in opposite directions with comic speed (+vibe, big). Off-beat bangs: muffled "piss off mate" from inside, reset combo, small aggro. TEST: hit windows off recorded tick timestamps (pure function, unit test — `judgeBang(bangTimeMs, lastTick): 'perfect'|'ok'|'miss'`). ### 5. Infraction: Pat-down (contraband) Suspicious idle (pocket-pat animation, driven by `flags.contraband`). Interact → pat-down overlay: patron outline with 5–6 pocket zones; timed 10s; click zones to reveal items, drag each to the **AMNESTY BIN** on the right. Items from `data/contraband.ts` with silly sprites (placeholder: labelled coloured rects — the budgie flaps). Missed items walk with them (deferred vibe leak). Contraband found = +vibe, patron stays (confiscation, not ejection) unless it's severity 3. ### 6. Escort-out (shared mechanic) Ejections attach the patron to walk-in-front-of-you pathing to the exit; they play the defeated slouch. While escorting you can't interact — travel time is the cost of every ejection (this is the floor's core resource: your position). ## Constraints - Input: `Patron[]` + EventBus + clock. No imports from `scenes/door/` or `audio/`. - All scoring via `meters:delta`; incidents appended via NightState API from core. - Keep every judgement rule (bang windows, escalation matrix, pat-down scoring) in pure functions with tests — scene code stays thin. - Performance: 40+ patrons with Light2D at 60fps; profile before optimizing, but silhouette-tint rendering outside the cone should be cheap (no per-frame texture work). ## Definition of done Demo scene: all three minigames + UV check playable and readable in the dark aesthetic; `lint`/`build`/`test` clean; pure-logic tests for bang timing, escalation matrix, pat-down scoring; SESSION block appended to LANEHANDOVER.md; branch pushed.