Files
settled-reach/docs/sprints/sprint-30/client.md
T
jpmschweitzerandClaude Sonnet 4.6 4722598017 chore(meta): plan Sprint 30: Clean Slate
Sprint 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.

26 tickets: copy (18), client (5), server (3).
Epics #638, #683, #738 closed. #766 deferred (own sprint).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 19:28:20 +02:00

7.5 KiB

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

tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(client): Sprint 30 character and UI fixes" --description "body" --base main --head client