Brief: Social & the Reset Ritual (avatars, emotes, blow-the-fuse)
- 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>
This commit is contained in:
parent
74482e5693
commit
036748543b
156
docs/briefs/SOCIAL_RESET.md
Normal file
156
docs/briefs/SOCIAL_RESET.md
Normal file
@ -0,0 +1,156 @@
|
||||
# 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 `AVATAR` with 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 `hi` to `{ t:'hi', name, avatar }` where
|
||||
`avatar = { 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/updateAvatar` renders 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):
|
||||
- `Z` **wave** — 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).
|
||||
- `X` **head-nod** — the head bobs ON THE BEAT (subscribe to `audio:beat`
|
||||
for phase; when the booth is silent, nod at 118 BPM anyway — funnier).
|
||||
Toggles on/off; auto-off after 16 bars.
|
||||
- `C` **point** — arm extends toward look direction 1.5 s.
|
||||
- `V` **airhorn** — 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 V` glyphs 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 clears `repaired`, resets `platters` to 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: `AudioEngine` brake-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 via
|
||||
`world.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.json` gains `winAt` timestamp (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 typecheck` clean; 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.
|
||||
Loading…
Reference in New Issue
Block a user