# Sprint 30: Clean Slate — Client Tasks **Goal:** Close Phase 1 wiki quality debt — stereotype correction, data integrity fixes, and atlas regressions — so the wiki stands correct before Phase 1 UI work begins. **Branch:** `client` **Agents:** Stig (dev), Tyre (arch), Hoshe (QA) ## New Tickets | # | Title | Priority | Blocked by | |---|-------|----------|------------| | #718 | Persist CharacterVisualDescriptor on new game start | high | — | | #674 | Star map insert module — concentric hop-ring view | medium | — | | #712 | Add BoneAttachment3D marker above Head bone for floating icons | medium | — | | #719 | Hair highlight: add descriptor field or make swatch read-only | medium | — | | #720 | Replace DirAccess asset scanning with manifest JSON for export builds | medium | — | Use `tooling/db/ticket show ` for full details. ## Key Decisions - `decisions/architecture.md` — D-134 (full character customisation: hair, clothing, colors at tile scale), D-146 (character preview: tile-scale sprite, no separate portrait), D-043 (art direction: functional warmth), D-044 (visual hierarchy) - `decisions/scope.md` — D-114 (Phase 4 deliverable: player viewport with final-version assets) ## Notes ### Existing code to understand first Before starting any ticket, read: - `client/ui/character_creation.gd` — the character creation screen. Manages `CharacterVisualDescriptor`, layer selectors, color swatches, preview pane, and manifest loading. - `client/assets/characters/manifest.json` — asset manifest listing available body types, heads, hair, facial hair, clothing, accessories. Partially populated (heads array is empty). - `client/scripts/autoloads/game_state.gd` — player entity ID and session state. CharacterVisualDescriptor will be stored here on new game start. - `client/scripts/rendering/entity_renderer.gd` — current entity sprite system. Integration point for #718 (persisted descriptor must survive into gameplay rendering). ### #718 — Persist CharacterVisualDescriptor on new game start When the player confirms the character creation screen, the client sends a `CharacterVisualDescriptor` to the server. The server must receive it and persist it so the descriptor survives save/load cycles and is included in `ObserverSnapshot`. This ticket is the client side: wire the confirmed descriptor from `character_creation.gd` through to the new-game IPC call. Verify the descriptor reaches the server (coordinate with server team if the server-side storage is not yet implemented — server ticket is out of scope this sprint unless added separately). **What to deliver:** - On confirm in `character_creation.gd`, serialize descriptor and include it in the new-game start message to the server - Store descriptor in `GameState` so it persists across scene transitions - After load, confirm descriptor is restored from server snapshot (integration test: start game, check descriptor fields match what was entered) **File locations:** - `client/ui/character_creation.gd` — add serialization + send call - `client/scripts/autoloads/game_state.gd` — add descriptor field + restore from snapshot - `client/protocol/` — may need a new message type for descriptor transmission ### #712 — BoneAttachment3D marker above Head bone Add an empty `Marker3D` node anchored as a `BoneAttachment3D` to the Head bone in the player character skeleton scene. Offset approximately 0.3m above the Head bone origin. This node is the anchor point for floating UI elements — status indicators, thought bubbles, alert markers, speech icons. **What to deliver:** - Locate the skeleton scene (likely `client/assets/characters/skeleton/` or similar based on the Sprint 28 asset pipeline) - Add `BoneAttachment3D` with bone_name = "Head", child `Marker3D` at offset Vector3(0, 0.3, 0) - Export a `@export` var or named node path from the character root so other systems can reference the marker node without hardcoded paths - No functional systems need to use this marker yet — the ticket is infrastructure for future floating UI work **Gotcha:** BoneAttachment3D must be a child of the skeleton, not the character root. If the skeleton is a `Skeleton3D` inside a scene, the attachment must be added inside that scene, not the parent. ### #719 — Hair highlight: add descriptor field or make swatch read-only The hair highlight swatch in the character creation screen accepts user input but the override is a no-op. `CharacterVisualDescriptor` has no `hair_highlight_tint` field, and `_on_hair_highlight_changed` in `character_creation.gd` does not write to the descriptor. Two valid resolutions: **Option A (preferred if adding the field is low-risk):** Add `hair_highlight_tint: Color` to `CharacterVisualDescriptor` and wire `_on_hair_highlight_changed` to write it. The compositor must then read and apply it. This is the fuller fix. **Option B (if the compositor cannot accept it yet):** Make the swatch non-interactive — visually show the derived highlight color (`_derive_hair_highlight(descriptor.hair_tint)`) but remove the `ColorPickerButton` and replace with a plain `ColorRect` labeled "Auto". Add a `# TODO: ticket #719` comment noting the deferred field. Decide which option to implement based on compositor readiness. If the compositor already supports `hair_highlight_tint` (check `client/scripts/rendering/character_compositor.gd` if it exists), go with Option A. Otherwise Option B. ### #720 — Replace DirAccess with manifest JSON The character creation screen uses `DirAccess.open("res://...")` in two fallback functions (around lines 1849 and 1868 in `character_creation.gd`) to scan for available assets. This approach fails in exported PCK builds because `DirAccess` cannot enumerate `res://` paths inside a PCK archive. The manifest approach is already partially implemented — `_load_manifest()`, `_manifest_array()`, and `MANIFEST_PATH` are in place. The manifest file at `client/assets/characters/manifest.json` exists and is partially populated. **What to deliver:** - Remove the two `DirAccess.open()` fallback scanning functions (lines ~1849-1868) - Populate `manifest.json` fully: add all available asset IDs for heads, hair, clothing, accessories, and body_types by scanning the actual asset directories once (during development) and encoding the result into JSON - Add a `tooling/` script or `Makefile` target that regenerates `manifest.json` from the asset directories, so it stays in sync as artists add assets — this prevents the manifest going stale - Verify: in an exported build (or by disabling DirAccess manually), the character creation screen populates all tabs correctly from the manifest alone **Note:** `manifest.json` currently has `"heads": []` — the heads array is empty. If head assets exist in `client/assets/characters/heads/`, add them. If none exist yet, leave empty but document in a comment that the manifest is the source of truth. ## Dependency Chain ``` #718 (persist descriptor) — standalone, no client-side blockers #719 (hair highlight) — standalone, read compositor state first #720 (manifest JSON) — standalone #712 (bone marker) — standalone #674 (star map) — standalone ``` All five tickets are parallel tracks. No cross-dependencies within this sprint. ## 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): Sprint 30 character and UI fixes" --description "body" --base main --head client ```