--- title: "Sprint 14 — Client Briefing" description: "Monologue display, passive dialogue panel, F3 debug overlay" type: sprint status: archived sprint: 14 team: "client" --- # Sprint 14: Live — Client Tasks **Goal:** Bring NPCs to life — mood, trust, routine execution, and NPC-to-NPC conversation drive the first emergent social observables; the simulation produces a world that breathes independently of the player. **Branch:** `client` **Agents:** Stig (UI/rendering dev), Tyre (architect), Hoshe (QA) ## Carry-over from Sprint 13 None. All Sprint 13 client tickets done. ## New Tickets | # | Title | Blocked by | |---|-------|------------| | #122 | Monologue display — client | — | | #535 | Passive dialogue panel — overheard NPC conversation display | #247 (server) | | #511 | F3 debug overlay (deferred — WRONG button covers same data) | #495 done | Use `db/connectors/ticket show ` for full details. ## Key Decisions - `decisions/architecture.md` — D-020 (ObserverSnapshot — `current_monologue` field), D-042 (UI microcopy format — YAML autoload for HUD labels) - `decisions/perception.md` — D-016 (internal monologue — text display, atmosphere function), D-018 (three-range sound model — NPC-to-NPC conversations are Voice events), D-055 (sprint suppresses monologue at 40% rate — client renders what server sends; no client-side suppression needed) - `decisions/content.md` — D-032 (separate monologue pools per character — client only needs to render; no character branching in display layer), D-035 (tag taxonomy — monologue has `character`, `trigger`, `prerequisite` tags; client ignores, server filters) - D-078 (overheard NPC conversation — passive dialogue panel, server-authoritative occlusion, pre-occluded text rendering, speaker attribution) ## Notes - **#122 — Monologue display — client:** The server-side monologue trigger system is complete — `MonologueEvent` arrives in `ObserverSnapshot.current_monologue` (field added Sprint 5, `server/src/bridge/types.rs`). The visual spec for monologue display was authored in Sprint 13 (#315, `docs/design/monologue-display-spec.md`). Sprint 14 work: implement the client-side display using that spec. Key requirements from the spec: - **Position:** Bottom-left of viewport, above the interaction verb list, below the minimap. Not in the dialogue box area (max 20% height, D-061). - **Typography:** Italic, smaller than dialogue (spec has exact px/pt — read `docs/design/monologue-display-spec.md` §Typography). - **Stacking:** Queue — if a new monologue arrives while one is displaying, queue it (max depth from spec). Do not overwrite mid-display. Sprint 13 note: "monologue not lost on overwrite" is a P0 test (#477, done). - **Fade:** Auto-fade after display duration. Duration is embedded in `MonologueEvent.display_duration` (float, seconds). Standard Godot `Tween`. - **Character differentiation:** Smuggler and detective lines use different text colours — hex values in spec. Client reads the active character from `game_state.gd` (autoload at `client/scripts/autoloads/game_state.gd`) or derives it from the snapshot. The server already partitions monologue pools per character (D-032) so the client receives only the correct character's lines — no client-side branching needed. - Existing `world_renderer.gd` (`client/scripts/rendering/world_renderer.gd`) is the root scene; monologue display is a new `CanvasLayer` child node or a `Control` node in the HUD layer (z-layer 6 per D-049 rendering stack). Do not put it in the dialogue box scene. - **Integration with audio:** Monologue display does NOT trigger audio (monologue chime fires at cognitive delay onset from the server's recognition pipeline, not from text display). No client-side audio calls needed here. - Delivery: `client/scripts/rendering/monologue_display.gd` (or similar), integrated into the HUD scene, reading `ObserverSnapshot.current_monologue`. Tests via Hoshe's gdUnit4 harness: queue management, fade-out timing, character colour, no-overwrite on active display. - **#535 — Passive dialogue panel — overheard NPC conversation display:** Consumes `ConversationEvent` from `ObserverSnapshot` (added by server #247). Render overheard NPC-to-NPC conversations in the existing dialogue box in passive (read-only) mode. Per D-078 amendment — occlusion is server-authoritative; this ticket is a pure renderer of pre-occluded text. Key requirements: - **Speaker attribution:** Display header as "Speaker → Target" (e.g. "Kael → Mira") using `speaker_name` and `target_name` from the event. Use the same attribution label slot as player conversation NPC-name display — no new layout region needed. - **Pre-occluded text renderer:** Render `occluded_line` as-is. The server has already performed per-word Bernoulli drops and replaced dropped words with `...`. The client performs no stochastic logic — no `clarity_score`, no `randf()`, no per-word processing. Display the received string directly in the passive panel. - **Read-only mode:** The dialogue box must suppress response options and player input when displaying a passive conversation. No response list, no input capture. Player can still move freely. - **Dismissal:** On `conversation_end` event (or if player moves far enough that no `ConversationEvent` arrives for the NPC pair), dismiss via the standard 300ms fade (same `Tween` as walk-away fade, #437 done). - **Monologue isolation:** Monologue display (#122, z-layer 6) is unaffected. Do not route conversation text through the monologue display. The server triggers `witness_interaction` which may produce a monologue — that arrives separately via `current_monologue` and is rendered by #122 normally. - **Blocked by #247** — `ConversationEvent` struct must exist on `ObserverSnapshot` before this can be wired. Start with a stub event type and placeholder renderer if #247 is not yet landed. - Delivery: passive mode in `client/scripts/ui/dialogue_box.gd` (or a new `passive_dialogue.gd` sibling), verbatim `occluded_line` render, speaker attribution header, `conversation_end` dismissal. Tests via Hoshe's gdUnit4 harness: attribution renders correctly, `occluded_line` with `...` tokens renders verbatim, panel does not show response options in passive mode, dismisses on `conversation_end`. - **#511 — F3 debug overlay (deferred):** This ticket is low priority. The WRONG button (F12, #507 done) already captures snapshot state, text render, and description on demand without per-frame cost. The F3 overlay would duplicate that information as a persistent overlay. Stig recommended deferral in Sprint 12. Only implement this sprint if server tickets finish early and QA explicitly needs real-time overlay data that WRONG button cannot provide. Default: skip. ## Dependency Chain ``` #122 (monologue display) — standalone, no server dependency this sprint #535 (passive dialogue panel) — blocked by #247 (server, NPC-to-NPC conversation system) #511 (F3 overlay) — standalone, low priority, implement only if capacity allows ``` #122 has no blockers — `current_monologue` in the snapshot has been live since Sprint 5. The spec (#315) is done. This is a pure client rendering task. #535 is blocked on #247 landing the `ConversationEvent` struct with `occluded_line: String` on `ObserverSnapshot`. Stig can stub the event type locally and build the renderer against it in parallel; wire-up follows when #247 merges. No stochastic logic needed on the client — render `occluded_line` verbatim. ## 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(client): description" --description "body" --base main --head client ```