Files
settled-reach/docs/sprints/sprint-8/client.md
T
jpmschweitzerandClaude Opus 4.6 7873b77a5c chore(meta): add Sprint 8 briefings and DB backup
Sprint 8: React — briefings for server, client, audio, and joint teams.
Database snapshot after Sprint 8 ticket assignments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:26:04 +01:00

9.9 KiB

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):

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