Files
settled-reach/docs/sprints/sprint-8/client.md
T
jpmschweitzerandClaude Opus 4.6 bccdcfcdcb docs(docs): add frontmatter to all sprint briefings
Standardized YAML frontmatter on all 115 sprint briefing files across
sprints 1-26 with title, description, type, status, sprint number, and
team fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 00:15:45 +01:00

155 lines
10 KiB
Markdown

---
title: "Sprint 8 — Client Briefing"
description: "Dialogue response selection, walk-away mechanic, confrontation text styling"
type: sprint
status: archived
sprint: 8
team: "client"
---
# Sprint 8: React — Client Tasks
**Goal:** Activate the dialogue pipeline end-to-end — player talks to NPCs, NPCs respond, the world reacts.
**Branch:** `client`
**Agents:** Stig (dev), Tyre (arch), Hoshe (QA)
## Carry-over from Sprint 7
| # | Title | Status | Notes |
|---|-------|--------|-------|
| #431 | Fog entity visualization — sound pings, cognitive delay anim | in_progress | Protocol v7 bump + visualization implementation |
## New Tickets
| # | Title | Blocked by |
|---|-------|------------|
| #435 | Dialogue response selection — max 3 visible, invisible locks | #434 (done) |
| #437 | Walk-away mechanic — WASD detect during dialogue, 300ms fade | — |
| #436 | Confrontation text styling — italic voice, monologue beat | #435 |
Use `db/connectors/ticket show <id>` for full details.
## Key Decisions
- `decisions/content.md` — D-062 (invisible locked dialogue options), D-063 (confrontation same box different weight), D-064 (walk-away 3-phase consequences)
- `decisions/perception.md` — D-059 (fog shader 5 layers), D-060 (cognitive delay), D-061 (dialogue box max 20% height)
- `decisions/architecture.md` — D-066 (dual-scale grid 0.5m sim / 1m visual)
## Open Questions to Resolve Early
None.
## Notes
### #431: Fog entity visualization — sound pings, cognitive delay anim (HIGH, carry-over)
**What exists:**
- `client/scripts/rendering/fog_shader.gd` — FogShader node with 5-layer shader (D-059), noise animation
- `client/scripts/protocol/protocol.gd` — MessagePack decoder for ObserverSnapshot (protocol v6)
- Fog layers 1-3 (clear, light fog, deep fog) render via shader, no entity visualization yet
**What the ticket needs to deliver:**
- **Protocol v7 bump:** Add `pending_recognitions` and `sound_events` fields to ObserverSnapshot in protocol.gd decode logic
- **Sound ping rendering:** On FogState layer 6 (insert overlay, NOT affected by fog shader), render 2-3 thin concentric expanding rings from sound source direction. Sonar-style. Insert white-blue color. Loud = 3 rings bright fast. Quiet = 1 ring faint slow. Fade over 1.5s. Use simple Sprite2D or Line2D, not particles.
- **Recognized entity in fog:** Entity with `pending: false` renders on layer 6 as: D-033 color glow (via PointLight2D ~40% blend) + faint silhouette feature (cached sprite at ~20% opacity) + 0.8s breathing pulse (shader uniform or tween) + ±0.5 tile position drift (approx position, not exact). Silhouette feature: Kael's vest, Lera's apron (sprite metadata from entity.silhouette_feature field).
- **Unrecognized entity in fog:** Entity with `pending: true` renders as neutral grey #555566 blob (simple circle sprite, no identifying features). No silhouette. No D-033 color.
- **Recognition transition:** When `pending` flips from true → false, tween grey blob → color+silhouette over 0.3s (within the 0.6s cognitive delay window per D-060). Monologue fires during this tween.
**Integration points:**
- Pairs with server #423 (cognitive delay system, done Sprint 7)
- Protocol v7 wire format must match server ObserverSnapshot struct additions
- Builds on fog shader from Sprint 6
**Non-obvious gotchas:**
- Sound pings and fog entities render on layer 6 (insert overlay), NOT layer 5 (fog shader). Insert data is computational, not perceptual — not affected by vision cone.
- ±0.5 tile drift is VISUAL only (client-side randomness for "approximate" feel). Server sends exact position; client adds noise.
- Silhouette feature sprite is entity metadata, not a full sprite. Think "identifying accessory" not "full character render."
- Tween recognition over 0.3s to align with monologue timing (D-060: monologue fires DURING delay, not after)
---
### #435: Dialogue response selection — max 3 visible, invisible locks (HIGH)
**What exists:**
- `client/ui/interaction_list.gd` — Vertical list for entity interaction verbs (Talk, Observe, etc.)
- `client/scripts/protocol/protocol.gd` — Decodes NearbyInteraction from snapshot
- D-061 dialogue box spec: bottom screen, max 20% height, max-width, no portraits
**What the ticket needs to deliver:**
- **New scene:** `dialogue_box.tscn` — Bottom-screen container with two sections: NPC speech (Label, top), player response options (VBoxContainer, bottom). Max 20% viewport height, max-width (exact value: viewport width minus 40px margin). Left-aligned text. Insert-styled per D-057/D-061.
- **Response option rendering:** Each response is a Button (or custom Control with hover state). Show max 3 visible options. Sort by priority (server provides sorted list). D-062: locked options are invisible (not rendered at all). No grayed-out options. No lock icons. Player doesn't know what they don't know.
- **New protocol field:** `dialogue_response` in ObserverSnapshot — array of {text: String, response_id: String, priority: int}. Decode in protocol.gd.
- **Integration:** When player selects Talk verb from interaction_list, server sends snapshot with dialogue_response populated. Client shows dialogue_box, hides interaction_list. Player clicks response → send PlayerInput::DialogueResponse{response_id}.
- **Monologue interaction:** Monologue floats ABOVE dialogue box on z-layer 7 (spatial separation, D-061). Both can be visible simultaneously — character thinks one thing while NPC says another.
**Integration points:**
- Blocked by #434 (interaction verb execution, done Sprint 7)
- Pairs with server #305 (dialogue selection pipeline)
- Blocks #436 (confrontation styling)
**Non-obvious gotchas:**
- Max 3 VISIBLE means if server sends 5 options, show top 3 by priority. But D-062 invisible locks mean server should never send locked options at all (server filters before protocol). Client just renders what it receives.
- Player response text can be variable length (confrontation lines are longer than casual small talk). Use word wrap, auto-expand button height.
- No close button per D-064 — walk-away is the exit. Dialogue box stays until player walks away or server sends empty dialogue_response (conversation ended).
---
### #437: Walk-away mechanic — WASD detect during dialogue, 300ms fade (MEDIUM)
**What exists:**
- Input handling in `client/scripts/main.gd` captures WASD for movement, sends PlayerInput::Move
- Dialogue box (#435) renders dialogue but doesn't handle walk-away yet
**What the ticket needs to deliver:**
- **Input override:** When dialogue_box is visible, WASD input triggers walk-away instead of movement. Send PlayerInput::WalkAway{npc_id} to server.
- **Visual fade:** Dialogue box fades out over 300ms (Tween modulate alpha 1.0 → 0.0). No sound. Silence. D-064 phase 1.
- **NPC may react:** Server may send updated NPC animation or monologue after walk-away (D-064 phase 2). Client renders these as normal snapshot updates — no special walk-away animation on client.
- **KG recording:** Server handles D-064 phase 3 (#427). Client just sends WalkAway input.
**Integration points:**
- Pairs with server #427 (walk-away KG recording)
- Depends on #435 (dialogue box exists)
**Non-obvious gotchas:**
- Walk-away is ANY WASD press during dialogue, not just "walk away from NPC." Even pressing W (toward NPC) triggers walk-away. The direction doesn't matter — the intent to move ends the conversation.
- After WalkAway input sent, dialogue box fades but player can't move until fade completes (300ms input block). Prevents "walk through NPC" edge case.
- If server sends new dialogue_response during fade (NPC talks again), cancel fade and re-show dialogue. Rare edge case but possible (NPC calls after player).
---
### #436: Confrontation text styling — italic voice, monologue beat (MEDIUM)
**What exists:**
- Dialogue box (#435) renders NPC speech + player responses
- D-063 spec: confrontation uses same UI, different weight via styling + monologue beat
**What the ticket needs to deliver:**
- **Confrontation option styling:** Response options marked as `confrontation: true` (new protocol field) render in italic + first-person voice. Example: regular = "Shift schedule", confrontation = *"I saw you in corridor B-7"* (italic BBCode tag).
- **Pre-delivery monologue beat:** When player clicks confrontation option, 1-2 second pause before sending DialogueResponse input. During pause, show monologue: *"This changes things. No taking it back."* (hardcoded v0.1 line, future from content pools). Monologue displays on layer 7, dialogue box dims slightly (reduce opacity to 70%).
- **Audio dip:** Ambient audio volume drops to 50% during confrontation selection (via AudioServer bus manipulation). Restore after response sent.
- **No camera change:** D-063 defers camera tighten to post-v0.1. Same camera position throughout.
**Integration points:**
- Blocked by #435 (dialogue box)
- Pairs with server #305 (dialogue pipeline marks confrontation lines)
**Non-obvious gotchas:**
- Monologue beat is client-side delay (pause before sending input). Server doesn't know about the pause — from server's view, DialogueResponse arrives 1-2s after Talk verb.
- Italic rendering: use BBCode `[i]...[/i]` in RichTextLabel. Standard Label doesn't support italics.
- Don't apply italic to NPC speech — only to player confrontation responses. Regular dialogue responses stay non-italic.
- Audio dip is temporary (duration of monologue beat only). Restore ambient volume after response sent, not after NPC replies.
## Dependency Chain
```
#431 (fog visualization) → standalone, protocol v7 prerequisite for other features
#435 (dialogue box) → #437 (walk-away) depends on this, #436 (confrontation styling) depends on this
#437 (walk-away) → standalone once #435 exists
#436 (confrontation styling) → depends on #435
```
## PR Workflow
When ready to submit, create a PR with `tea` CLI. **All flags are required** to avoid TTY prompts (see CLAUDE.md "Gitea access" section):
```bash
tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(ui): dialogue pipeline client (Sprint 8 client)" --description "Implements #431 #435 #437 #436 — fog entity viz, dialogue response selection, walk-away, confrontation styling" --base main --head client
```