- Avatar customization: hue/headgear/face enums, Menu AVATAR tab with 2D preview, live sync via extended hi + new avatar message (no moderation surface - palettes only, no free-form content). - Emotes on Z X C V: wave, beat-synced head-nod, point, rate-limited airhorn with positional synth + confetti. - Reset ritual: after a win the glass fuse becomes hold-breakable (5s, warned); server-authoritative reset with 10-min cooldown - music power-downs, lights die, workshop resets to wires-off, block edits survive, quest replayable. Needs Quest.reset() + 'quest:reset' bus event. - Tab-hold player list. Protocol validation/rate-limit requirements spelled out for ship-check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8.2 KiB
BRIEF — Social & the Reset Ritual
The booth is shared; now make it feel inhabited. Three features: avatar customization, emotes, and a diegetic way to kill the mix and play the quest again — plus a who's-here list.
For one Opus 4.8 session (features are separable if split later, but the protocol changes should land as one coherent relay version).
READ FIRST — this is the law from the last phase: on this shared tree
you build against the actual code, not brief-guesses. Read
docs/CONTRACTS.md, docs/INTEGRATION_NOTES.md, then the real files:
src/net/NetClient.ts + src/net/Avatars.ts + server/relay.mjs (the
protocol you're extending), src/machines/quest.ts + src/machines/workshop/
(what reset must unwind), src/ui/Menu.ts (where customization lives),
src/audio/AudioEngine.ts + src/audio/sfx.ts (how SFX are made),
src/fx/FxSystem.ts (how the booth's mood follows quest state),
src/core/events.ts (bus vocabulary — extend additively, primitives only).
You own: src/net/**, src/ui/** (additive), server/relay.mjs,
src/machines/quest.ts (+ small workshop reset hook), additive edits to
src/audio/**, src/fx/FxSystem.ts, src/core/events.ts, src/main.ts
wiring. Keep every edit additive; do not restructure working systems.
Feature 1 — Avatar customization ("avatar your player")
Players are already voxel DJs with headphones + name tags. Give them a look of their own. Keep it palette-y and chunky — no free-form content, so there's nothing to moderate.
- Options (all enum/palette, no user-drawn anything):
bodyHue: 12 swatches (hue ring; keep the current golden-ratio default as "auto").headgear:phones(current),cap(brim box),beanie,crown(win-flavored, tiny),none.face: 4 procedural canvas faces on the head front (dot eyes / shades / visor / happy) — painted, not typed.
- Settings UI: a third Menu tab
AVATARwith swatch buttons and a live preview (a small THREE scene in the panel is overkill — a 2D canvas mock-up of the front view is fine and cheap). Persists in the existing Settings localStorage object; applies live (no reload — see protocol). - Protocol: extend
hito{ t:'hi', name, avatar }whereavatar = { hue: 0-11, gear: 0-4, face: 0-3 }(integers only — server validates ranges, defaults on garbage). Add{ t:'avatar', avatar }for live changes; server stores per-peer and rebroadcasts;hello.peers[]carries it.Avatars.add/updateAvatarrenders it (rebuild that peer's meshes; they're tiny). - Your own avatar isn't rendered (first person) — the preview is the mirror.
Feature 2 — Emotes
No text chat (nothing to moderate, nothing to store). Expression through gesture + classic DJ noises.
- Four emotes on
Z X C V(pointer-locked only, no hotbar collision):Zwave — arm swing (procedural bone-less animation: swing a grouped arm box; avatars currently have no arms — add two small arm boxes to the avatar build, default hanging).Xhead-nod — the head bobs ON THE BEAT (subscribe toaudio:beatfor phase; when the booth is silent, nod at 118 BPM anyway — funnier). Toggles on/off; auto-off after 16 bars.Cpoint — arm extends toward look direction 1.5 s.Vairhorn — the classic. Synthesized (three detuned saws, pitch droop), positional at the emoter, plus a confetti puff. Rate-limited client AND server side (1 per 4 s per peer) or the booth becomes hell.
- Protocol:
{ t:'emote', kind: 0-3 }, relayed with peer id, server rate-limits (drop, don't queue). Local emote plays immediately; remote emotes animate that peer's avatar + positional sound. - A small HUD hint row in the help menu; also show
Z X C Vglyphs briefly when another player emotes near you the first time. - Self view: first-person players can't see their own wave/point — play the sound + a tiny camera nudge so it feels like something happened.
Feature 3 — The Reset Ritual ("blow the fuse")
Once the mix is live, the quest is spent — groups need a way to play it again. Make un-breaking it diegetic and deliberate:
- The act: the glass fuse in the PCB-Depths fuse box becomes breakable again after a win. Mining it takes a deliberate 5-second hold (not the usual glass 0.25 s — special-case this block position while the booth is won) with a HUD warning during the hold: "KEEP MINING TO KILL THE MIX — everyone starts over".
- The consequence (server-authoritative): client sends
{ t:'reset' }only when the hold completes. Server validates: booth actually won AND ≥ 10 minutes since the last win (anti-grief cooldown — reject silently otherwise, client shows "the fuse is still hot" subtitle). On accept: server clearsrepaired, resetsplattersto stopped, resets workshop state to assembled-but-undone (cart seated + rotated correctly, all four wires POPPED OFF, screws at 0.5, weight at a random notch 3–6 — the fun parts replay, the tedium doesn't), keeps ALL block edits (player builds survive), persists, broadcasts{ t:'reset', by: name }. - Client side: this needs the one genuinely new mechanism —
Quest.reset()and a bus event'quest:reset': { by: string }. Systems react: stems → 0 (music dies mid-bar — make it a power-down pitch drop, not a mute:AudioEnginebrake-to-silence on all stems + light thunk), platters spin down, HUD tracker unlights + big subtitle "⚡ DJ dusty42 BLEW THE FUSE", FxSystem drops emissive boost back to 0.35 over ~3 s (the booth visibly dies — this moment should feel HUGE), power switch un-arms/un-glows, workshop applies its reset pose, and the blown fuse block reappears in the box (worldgen block viaworld.setBlock— it's the one edit the reset writes). - Late joiners: server state already carries everything; verify a joiner post-reset sees a dead booth mid-second-run.
Feature 4 — Who's here (small)
Hold Tab: a translucent list of DJ names in the booth (you first),
with each name's avatar hue as a swatch. Data you already have
(hello.peers + join/leave). Release to hide. Also show total count on
the existing online chip (already done) — just verify it stays correct
through avatar/emote traffic.
Protocol & server discipline (ship-check will look here)
- New/changed messages:
hi(+avatar),avatar,emote,reset(client→server intent; server→clients authoritative). Every field validated: enums as bounded ints, unknown fields ignored, oversized frames already capped at 2 KB — keep new payloads tiny. - Server-side rate limits: emote 1/4 s/peer; avatar changes 1/2 s/peer; reset once per cooldown window globally.
booth-state.jsongainswinAttimestamp (for the cooldown) — must load old state files without it (treat as cooldown-expired).- Old clients vs new relay: unknown incoming message types are already ignored client-side — verify, don't assume.
Acceptance (live browser, two tabs minimum)
- Avatar editor: change hue/gear/face in tab A → tab B sees it within 1 s, no reload; persists across reload; garbage from console → server defaults it
- All four emotes visible/audible cross-tab; airhorn spam is capped (hold V — max 1 per 4 s lands remotely); nod syncs to the beat when music plays
- Full reset ritual: win the booth (debug handle ok) → fuse hold-break → BOTH tabs see the booth die (music power-down, lights fade, tracker clears, platters stop), workshop in its reset pose, blown fuse back in the box; quest replayable to a second win; block edits survived; cooldown rejects an immediate second reset with the subtitle; joiner mid-second-run sees correct state
- Tab list shows both DJs with hue swatches
npm run typecheckclean; zero console errors across a full two-tab session; relay fuzz (bad enums, wrong types) all dropped- Update
docs/INTEGRATION_NOTES.md(short section) + Menu help text (emote keys, reset ritual hint in TIPS)
Out of scope
Text chat (deliberately never), voice, friend lists, persistent accounts, avatar parts beyond the enums above, mobile input. Don't touch the deploy scripts — the integrator ships it.