4.9 KiB
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.
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 fromscenes/door/oraudio/. - 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.