5.6 KiB
LANE-DOOR — The Door (Phase 1, parallel)
Executor: Opus 4.8. Branch: lane/door (from main after Phase 0 merge).
Owns: src/scenes/door/, src/rules/ (shared with reviewer approval — you are
its primary author in Phase 1), tests/ mirrors. Prereq reading:
docs/GAME_DESIGN.md §3.1 → docs/CONTRACTS.md → LANEHANDOVER.md (check for
REVIEW blocks addressed to you).
You are the v0.1 critical path. When this lane is done, the game is shippable as a Door-only night. Bias every decision toward "playable and funny" over "architecturally complete".
Deliverable
npm run dev → full Door-only night: 9:00 PM to 3:00 AM, patrons queue and step
up, you inspect and rule on them, Dazza texts land, meters move, night ends in a
summary screen (win) or a fail state (Vibe 0 / Aggro 100). A stranger can play it
with zero explanation.
Scene layout (640×360, static tableau)
Street view: queue snaking from the left, rope + patron-up position centre, venue door right, rain + kebab-shop glow (reuse ParadeScene ambience). Desk UI along the bottom: ID tray · dress-code card (latest rules) · clicker · phone · verdict buttons. Patron steps up BIG (queue-pose doll scaled ×3) so outfit details read.
Systems to build (in rough order)
1. Queue manager (door/QueueManager.ts)
Spawns patrons from the generator on a clock-driven arrival curve (quiet 9 PM,
slams 10:30–12:30). Maintains visible queue of ≤8 dolls + an offscreen count.
The Rope: next patron only steps up when the player clicks the rope hook.
Idle time while queue is non-empty and no patron is up: +hype (capped), +aggro
(uncapped, accelerating). Wire via meters:delta. Phone prop: clicking your phone
while someone waits gives bonus hype and a little animation — reward the theatre.
2. Inspection surface
Patron-up view with hover/click zones: face (compare to ID photo), each outfit
slot (tooltip: "white sneakers — look new"), eyes (bloodshot?), stance (sway tween
driven by intoxication tell from doll renderer). The ID card: click tray → card
zooms to half-screen; DOB, expiry, name, photo (rendered idPhoto pose from
photoSeed — mismatch tell = it's visibly a different doll). Fake tells per
CONTRACTS.md: peeling laminate (corner pixels), wrong hologram (wrong colour
shimmer), joke name.
3. Rules engine (src/rules/ — PURE, tested, no Phaser imports)
dressCode.ts: implementDressCodeRule[]for night 1 (the 4 escalations from the design doc) + 4 more of your own invention in the same escalating-absurdity register.activeRules(clockMin),violations(patron, clockMin).idCheck.ts:idVerdict(idCard, nightDate)→{underage, expired, tells[]}. All date math here, tested against boundary cases (18th birthday IS tonight; expiry tomorrow; leap-day DOB).judge.ts:judge(patron, verdict, ctx) → VerdictOutcome— the single scoring function. Encode the design's tensions:- deny a violator: +vibe; deny a clean punter: +vibe small, +aggro (arbitrary power is rewarded but heats the queue)
- admit a violator: deferred −vibe ("Dazza does a lap" ~2–5 min later → dazza text + vibe hit)
- admit underage: deferred
heat:strike(fires on audit/inspection, Phase 2 — for v0.1 fire it at night end so it still hurts) - deny an actual owner's mate: big −vibe + furious Dazza text TEST every branch of this table.
4. Verdict flow & stamps
Verdict buttons: ADMIT / DENY / TEST / PAT-DOWN / (rope = wait). DENY = the big
red stamp: button press → stamp slams down over the patron with screen shake and a
dust ring, patron does the defeated-slouch walk-off. Spend real time on this
feel — it's the game's signature interaction. (LANE-JUICE will supply the stamp
widget + SFX later; build a local version now, swap on integration — mark it
// TODO(juice): swap for ui/Stamp.) ADMIT: rope unhooks, patron walks in the
door, clicker increments only if the player clicks it — the on-screen clicker
count vs true inside-count divergence is tracked in NightState per CONTRACTS.md.
5. Sobriety test (dialogue minigame)
Modal dialogue: pick one of 3 challenges (alphabet backwards / riddle / cursor line-walk — implement the first two v0.1, line-walk stretch). Their answer quality is derived from intoxication + rng, rendered as typed-out text with drunk typos. Player then rules PASS/FAIL regardless of performance — failing someone who passed: +vibe, +aggro. It's a power trip, remember.
6. Dazza & the phone
Phone widget (bottom corner): buzz animation + preview on dazza:text; click to
open thread history. Rule-bearing texts add the rule to the dress-code card with a
NEW badge. Strings from data/strings/dazza.ts; add more as needed (keep his
voice: aggressive lowercase, no punctuation, occasional fear of the owner).
7. Night shell (temporary, yours for v0.1)
Minimal NightScene wrapper: starts clock, runs Door, listens for fail states,
3 AM → NightSummaryScene (temporary version: vibe graph from sampled history,
cash = base + hypePeak bonus, verdict stats table). Phase 2 integration will
replace this shell — keep it thin and clearly marked // TODO(integration).
Constraints
- Consume
beat:tickfrom StubBeatClock for any rhythm flourish; never your own timer. - All scoring via
meters:delta— never touch NightState directly. - No imports from
scenes/floor/oraudio/. - Dialogue/strings in
data/strings/, Aussie register per design doc §5.
Definition of done
Full night playable; both fail states reachable by playing badly on purpose;
lint/build/test clean; rules-engine coverage includes every judge branch;
SESSION block appended to LANEHANDOVER.md; branch pushed.