The creation screen is done — 302 client tests (up from the 250 baseline), content build green. Also flips the M3 Title screen from planned to done: it shipped in a77bf03/f071392 and the roadmap never caught up, which would have misled M4-c into thinking it must build one. Adds six new trap species to docs/traps.md from this milestone's review passes: substring-collision assertions, a BBCode wrapper masking an empty ContentDB, a test asserting the script's own loop bound, a trivially-true gating fixture, assertions already true before the action under test ran, an "iterates everything" guard that checked three hardcoded pairs, a node-type sweep that missed RichTextLabel, tests that called handlers instead of pressing nodes, GUT silently skipping an unparseable class_name test file at the old count, and a test that can hang instead of fail. Also documents the new client-docs convention: clickable cards are Buttons with mouse_filter = 2 on every child, and pressed on a toggle_mode Button is the chosen state. Human F6 confirmation of the rendered screen against the mock is still outstanding and is not claimed here.
4.9 KiB
/client/docs
Client-specific documentation. Scoped to the Godot project.
Put here:
- Scene tree / autoload structure
- Input model — menu vs free-text boundary, which must be visible (§15)
- Combat wiring — deterministic damage, per-encounter seeding (§10)
- Canon log data structure and fact-harvesting (§11)
- HTTP client + fallback-degradation handling (§13)
Cross-cutting design (anything touching both client and api) goes in the root /docs, not here.
UI scene conventions (shell + all screens)
Screens are authored editor-first — the layout lives in the .tscn, not in
code. See ADR 0001 for the why;
this is the how. Reference: scenes/shell/{MainWindowShell,SystemDock,NarrationBook}.tscn
scripts/ui/shell/*.gd, andscenes/creation/CharacterCreation.tscn+scripts/ui/creation/{character_creation,creation_draft,creation_copy}.gd(M4-b).
-
Clickable cards are Buttons with
mouse_filter = 2on every child. A card that holds a name, a blurb and a trait line is aButton(Button-base variation, sonormal/hover/pressedactually skin it) with aVBoxContainerof Labels inside. Every child needsmouse_filter = 2(IGNORE) or the Label swallows the click and the card never toggles.pressedon atoggle_modeButton is the "chosen" state — no script-side stylebox swapping. -
The
.tscnowns the tree. Author every node in the editor: containers, panels, labels, buttons, art-slot placeholders. Assign the shared theme on the scene root (theme = game_theme.tres) and set each node'stheme_type_variation(fromThemeKeys) in the editor, so the scene previews themed. Instance sub-screens (PackedSceneext_resource) rather than rebuilding them. -
The script does on-load work only.
@onready var x := $Path/To/Noderefs, signal/service wiring, animations, and binding state into the authored nodes (label text, colours fromPalette). Do not create nodes in_ready(). -
Data-driven repeats = fixed authored slots, bound at runtime. Author enough turn-order tokens / consumable slots / etc. in the editor; the script fills the first N from state and hides the rest. Previewable now; systems write them later.
-
Scene-root sizing. Give the root a fixed size via offsets (e.g. 1920×1080) for editor preview, but no full-rect anchors on the root — a root Control run via F6 collapses to (0,0) under full-rect anchors. Keep
size = get_viewport_rect().sizeon_ready+ asize_changedconnection (the M3-a collapse lesson). -
Button styling → Button-base variations only. Never put a PanelContainer variation (
DarkPanel,ParchmentCard) on aButton; it has nonormal/hover/ pressedstyleboxes so the button renders unskinned. Use the Button-base variations (PrimaryCTA,Tab,TabActive,Chip,DockButton,ParchmentButton). -
RichTextLabelprose reads its dark serif ink from the theme's baseRichTextLabelstyle (it does not inheritdefault_font/default_color). Just add theRichTextLabel— don't override its colour per-node. -
Theme is generated. All colours live in
palette.gd;game_theme.tresis built byscripts/theme/build_game_theme.gd(source of truth). To add/adjust a variation, edit the builder +theme_keys.gd, then regenerate:godot --headless -s res://scripts/theme/build_game_theme.gd. Never hand-edit the.tresor put a hex literal in a script or.tscn. -
Wiring the DM loop / injectable services. Follow the shell: a
servicevar settable betweeninstantiate()andadd_child()lets tests inject aFakeTransport-backedDmServiceso on-load calls stay hermetic;_readybuilds the realHttpTransport-backed one when none was injected. -
Visual gate. Headless GUT proves structure (nodes mount, sizes non-zero, bindings run); it cannot prove rendering. Every screen needs a human F6 eyeball before it's done. Note shader surfaces (
DarkBay, vignette) render their texture only on an F6 run, not in the editor.
Narrate live smoke (M2 client HTTP loop)
The narrate_harness scene is a throwaway proof of the client → /dm/narrate
loop. It is not the game UI.
Prereqs: the proxy running and Ollama reachable.
- Start the proxy: from
api/,uvicorn app.main:app --port 8000(with the.envfrom.env.example; Ollama up atOLLAMA_BASE_URL). - In the Godot editor, open
res://scenes/narrate_harness.tscnand run it (F6). - Click Narrate this scene. Expect grounded prose in the Narrator's voice
(referencing Greywater / Brannoc / the washed-out bridge), tags stripped, and
facts harvested: Nwith N ≥ 0. - Degraded path: stop the proxy and click again. Expect the authored
fallback line and
(degraded)in the status — never an error.
Override the proxy URL with the coc_rpg/proxy_base_url project setting.