--- title: "Sprint 6 — Client Briefing" description: "Cursor state machine, fog shader rebuild, entity interaction list, inventory UI" type: sprint status: archived sprint: 6 team: "client" --- # Sprint 6: Touch — Client Tasks **Goal:** Movement stances, fog rebuild, multi-verb interactions, and smuggler inventory **Branch:** `client` **Agents:** Stig (UI/rendering), Tyre (architecture), Hoshe (QA) ## New Tickets | # | Title | Blocked by | |---|-------|------------| | #429 | Cursor state machine — 4 states, Araminta spec, 150ms transitions | — | | #430 | Fog shader rebuild — 5-layer, CanvasGroup Layer 5, animated noise | — | | #432 | Entity interaction vertical list — insert-styled, z-layer 6 | #429 | | #433 | World radial menu — 2 spokes v0.1 (Observe + Insert) | — | | #438 | Inventory UI — 3x3 grid, 1-9 hotkeys | #449 (server) | | #439 | Stance toggle UI — keybind + HUD indicator | #449 (server) | Use `db/connectors/ticket show ` for full details. ## Key Decisions - `decisions/perception.md` — D-056 (Cursor states), D-057 (Entity interaction vertical list), D-058 (World radial menu), D-059 (Fog shader rebuild), D-061 (Dialogue box) - `decisions/scope.md` — D-053 (Movement stances), D-065 (Smuggler inventory) - `decisions/architecture.md` — D-049 (Z-level rendering stack) ## Open Questions Resolved - **OQ-24: Inventory capacity** — Resolved to 3x3 grid = 9 slots (ticket #445, done) ## Notes ### #429: Cursor state machine — 4 states - **What exists:** Basic cursor rendering in Godot (default arrow) - **Needs:** Implement 4-state cursor using D-048 insert geometric aesthetic (thin ticks, bloom shader): - **Default:** Four thin inward-pointing ticks with bloom. White-blue #c8d0e0. Barely visible. - **Entity hover:** Ticks expand outward (150ms), corner brackets frame entity, color shifts to D-033 relationship color, verb tooltip in insert styling, bloom pulse ~10% brighter on entity outline - **Object hover:** Ticks rotate 45° to X-shape, muted grey #8b8ba0 (amber #e8c547 if flagged), simpler frame than entity - **Weapon aim:** Hard transition. Ticks extend, center gap widens, lines thicken 1→2px, warm white #f0e8d8, NO bloom. Entity in sights tints to D-033 color (aiming at friend = green tint should feel wrong). - **D-056 spec:** All transitions 150ms linear. z-layer 7. Never changes by zone/narrative state (D-045). Cursor changes on LOS, not just proximity. Click interaction range: ~2 tiles. Weapon-selected mode suppresses interaction prompts unless Shift held. - **Diegetic test (Stig):** Interaction labels render on z-layer 6 (insert overlay). If insert is off, labels disappear. - **Integration:** Blocks #432 (entity interaction list — cursor states trigger list display) ### #430: Fog shader rebuild — 5-layer from scratch - **What exists:** `client/scripts/rendering/fog_renderer.gd` extends TileMapLayer with 2-state fog (opaque black, semi-transparent edge). Lines 1-50 show current implementation. - **Needs:** **REBUILD from scratch.** Replace TileMapLayer-based fog with CanvasGroup + fragment shader. Five distinct layers: 1. **Clear (vision cone):** Soft gradient edge 3-4 tiles (Darkwood approach), no hard line 2. **Light fog (peripheral):** Desaturated 40-50%, brightness -30%, animated Perlin noise overlay (8-10s cycle), entity D-033 colors visible but reduced 3. **Deep fog (previously explored):** Near-monochrome with ~10% "zone temperature" tint (bar=warm dark, hub=cool dark, corridor=neutral dark), more pronounced noise (15-20s cycle), fog breathes 4. **Unexplored + maps app:** Geometric wireframe outlines #333340 (insert data aesthetic) 5. **Unexplored, no maps:** Solid near-black #12141a (information zero) - **Fog entities (sprites on Layer 5):** - **Sound pings:** 2-3 thin concentric expanding rings (sonar-style) from source direction, insert white-blue. Loud = 3 rings bright fast. Quiet = 1 ring faint slow. Fade over 1.5s. - **Recognized entity:** D-033 color glow + faint identifying silhouette feature (e.g., "Kael's vest") + 0.8s breathing pulse + ±0.5 tile position drift (approximate, not exact). Recognition transition governed by D-060 cognitive delay. - **Unrecognized entity:** Neutral grey #555566 blob. No silhouette, no identifying features. - **D-059 spec:** Knowledge-graph-driven — same fog shows different information per character based on their KG. Smuggler recognizes dock workers = green icons. Detective sees same entities = grey blobs. "Fog is not darkness — it's the absence of your attention." - **Performance target:** <1ms/frame total. Vision cone = PointLight2D. Fog = natural darkness + noise shader on CanvasGroup (Layer 5). Sound pings = 0-5 sprites. Entity ghosts = 0-10 sprites typical. - **Gotcha:** This is a complete rewrite, not an enhancement. Delete `fog_renderer.gd` and start fresh with shader-based approach. Araminta's spec in D-059 is the source of truth. - **Integration:** Blocks #431 (fog entity visualization, deferred to Sprint 7+) ### #432: Entity interaction vertical list — insert-styled - **What exists:** Interaction prompt system (#405, done in Sprint 5) shows "E: " on nearest entity - **Needs:** Replace single-verb prompt with compact vertical list (2-4 options max), anchored to entity position, insert-styled with Araminta's geometric aesthetic (thin lines, bloom, z-layer 6). New options unlocked by KG changes highlighted with gradient glow background. Max 3 visible response options in dialogue context. - **D-057 spec:** Variable-length text options (confrontation lines in character voice) break radial spatial memory. List handles 1-4 options cleanly. Radial reserved for world menu only (#433). - **Server integration:** Reads `nearby_interactions` from ObserverSnapshot (Phase 2 filtered verbs per D-057). Server provides 2-4 verbs, client renders list. Clicking a verb sends PlayerAction::Interact with target + verb. - **Diegetic test:** Labels render on z-layer 6. If insert is off, labels disappear (passes "is this information from character's implant?" test). - **Integration:** Blocked by #429 (cursor states — entity hover triggers list display). Works with server #422 (two-phase verb computation). ### #433: World radial menu — 2 spokes v0.1 - **Needs:** Right-click opens radial world menu. v0.1: 2 spokes only (Observe + Insert). Insert-styled: geometric lines, thin spokes with icons (eye icon for Observe, phone icon for Insert), nearly transparent. Renders on z-layer 6. Drag-release for power users (high-speed drag-to-select), click-click for newcomers. Future: scale to 4 spokes (add Comms, Wait). - **D-058 spec:** Radial works for world menu because items are fixed categories that don't vary by knowledge state (unlike entity verbs which use vertical list #432). Spatial memory builds quickly (~10 minutes). - **Integration:** Standalone. Complements #432 (entity list vs world radial — different UX for different contexts). ### #438: Inventory UI — 3x3 grid, 1-9 hotkeys - **Needs:** Bottom-right HUD area, 3x3 grid (40x40px icons), mapped to 1-9 keys. No empty slots displayed — icons appear only when items are carried. Reads `player_inventory` from ObserverSnapshot v6 (server #449). - **D-065 spec:** OQ-24 resolved to 9 slots universal (3x3 grid). Smuggler gets 3 items in v0.1 (manifest copy, access token, comm log). Detective gets 2 slots used. Items are world entities with CarriedBy component (server-side). - **UI positioning:** Bottom-right, does not overlap dialogue box (max 20% height per D-061). Icons render at z-layer 7 (HUD elements). - **Integration:** Blocked by server #449 (ObserverSnapshot v6 adds player_inventory field). Works with server #424 (CarriedBy component + Take/Place verbs). ### #439: Stance toggle UI — keybind + HUD indicator - **Needs:** Keybind for stance toggle (suggest C key, or Ctrl as ladder climb), HUD indicator showing current stance (icon + text: "Sprint" / "Walk" / "Careful" / "Crouch"). Reads `player_stance` from ObserverSnapshot v6 (server #449). Visual feedback on stance change (brief highlight or fade). - **D-053 spec:** Toggle-based ladder: Sprint → Walk → Careful → Crouch → (Prone future). Each stance affects movement speed and monologue rate. v0.1: 4 stances (Sprint/Walk/Careful/Crouch). - **UI positioning:** Top-left or top-right HUD area (not overlapping dialogue box or inventory). z-layer 7. - **Integration:** Blocked by server #449 (ObserverSnapshot v6 adds player_stance field). Works with server #417 (stance system). ## Dependency Chain ``` Critical path: #429 (Cursor states) → #432 (Entity interaction list) Parallel tracks: #430 (Fog shader rebuild) → standalone, large rewrite #433 (World radial menu) → standalone #438 (Inventory UI) → blocked by server #449 #439 (Stance UI) → blocked by server #449 ``` ## 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): cursor states, fog shader rebuild, interaction list, inventory + stance UI" \ --description "Sprint 6 client deliverables per D-056, D-057, D-058, D-059, D-061, D-065" \ --base main \ --head client ```