From 1c0d13611780b9f02d09a099c846e6133e1f2aea Mon Sep 17 00:00:00 2001 From: autocommit Date: Thu, 4 Jun 2026 18:17:02 -0700 Subject: [PATCH] feat(vocab): author statistics + end-game summary copy, drop placeholder keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stats modal (statistics.gd) and end-game summary (end_game_summary.gd) rendered raw ThemeVocabulary keys ("Statistics Tab Demographics", "Endgame Banner Victory", "Endgame Reason Lastsurvivor", etc.) because the statistics_*, endgame_*, trend_*, outcome_*, event_* and `close` keys were absent from vocabulary.json — lookup() falls back to title-case on a miss. Author all of them with Dwarf-flavoured copy: title "Records of the Hold"; tabs Census/Ledgers/Standings/Chronicle/Sagas; banners "Victory!" / "The Hold Has Fallen" / "The Reckoning"; per-GameOverReason flavour keyed PascalCase (endgame_reason_LastSurvivor/ConditionMet/TurnLimit/Resigned) to match the discriminant strings the proof + GUT drive; footer "Survey the Realm / Recount the Saga / Seal to the Vault / Inscribe to Stone / Main Menu". statistics_proof.gd: rebuild StatsTracker.category_labels after load_vocabulary so the demographics/graphs/rankings column + metric labels resolve to copy ("Score / Population / Military / Cities / Technology / Wonders") rather than the title-case keys cached at autoload-init (proof-only ordering; in-game the theme loads before StatsTracker). Re-captured clean on apricot under weston — no title-cased placeholders remain: .local/ui-proofs/statistics_proof_{demographics,graphs,rankings,replay,histories}.png .local/ui-proofs/end_game_summary_proof_{LastSurvivor,ConditionMet,TurnLimit,Resigned}.png Advances p2-47 (proof copy-caveat resolved; stays partial — snapshot-append + bridge-parity GUT remain Rust-blocked) and p2-48a (copy caveat resolved; proof stays [~] pending user phase-gate approval). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../p2-47-in-game-statistics-screens.md | 2 +- .../p2-48a-end-game-summary-gut-and-proof.md | 22 ++++++++++---- public/games/age-of-dwarves/vocabulary.json | 30 +++++++++++++++++++ .../engine/scenes/tests/statistics_proof.gd | 7 +++++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/.project/objectives/p2-47-in-game-statistics-screens.md b/.project/objectives/p2-47-in-game-statistics-screens.md index f35a7165..bb9f16e0 100644 --- a/.project/objectives/p2-47-in-game-statistics-screens.md +++ b/.project/objectives/p2-47-in-game-statistics-screens.md @@ -52,7 +52,7 @@ Score weights (`w_pop`, `w_cities`, `w_tech`, `w_culture`, `w_land`, `w_wonders` - [✓] **`statistics.gd` scene with all five tabs** — single `Statistics.tscn` modal, tab bar at top, each tab is a child node toggled visible. Demographics tab renders a sortable table; Graphs tab renders a multi-line chart with the metric selector; Rankings tab reuses the same selector and renders a ranked list with trend arrows; Replay tab embeds the replay_viewer.tscn (exists at scenes/menus/); Histories tab shows GdGameHistory-bridge pending notice (no stub — honest degradation, Rail-1 compliant). _Evidence: src/game/engine/scenes/statistics/statistics.gd (authored cycle 50) + **`statistics.tscn` thin wrapper authored 2026-06-04 (bridge-cse lane)** — one-node `Control` with the self-building script attached, un-breaking both push_overlay call sites. All 5 tabs reviewed in conversation via the apricot proof (`.local/ui-proofs/statistics_proof_{demographics,graphs,rankings,replay,histories}.png`): Demographics table, Graphs 4-line chart, Rankings leaderboard with trend arrows + metric selector, Replay embed, Histories pending notice. NOTE: standalone demographics.gd and demographics.tscn remain at scenes/overviews/ pending full deprecation once mc-score lands and the ingame_menu route redirected (done in cycle 50). Commandment-9 requires deleting demographics when the 5-tab modal fully supersedes it — tracked below._ - [✓] **HUD entry point** — info button on the world-map HUD opens the modal; `F9` keybinding does the same; modal opens on the most-recently-viewed tab (state stored in Settings autoload). _Evidence: world_map_hud.gd (statistics_pressed signal + info button + _input F9 handler); world_map.gd (_open_statistics connected); settings_manager.gd (ui.last_statistics_tab default = 0); ingame_menu.gd Stats button path updated to statistics.tscn (all cycle 50). **2026-06-04: the loaded resource now exists (statistics.tscn), so F9 / info-button / Stats-menu no longer dead-end at a missing resource.**_ - [ ] **GUT tests** — `test_score_formula.gd` asserts the GDExtension binding returns the same score Rust does for a fixed snapshot; `test_snapshot_recording.gd` asserts per-turn append; `test_visibility_filter.gd` covers the contact-state rule end-to-end through the GD bridge. _(Rust bridge tests blocked on mc-score + GdGameHistory; `test_statistics_modal.gd` integration tests authored cycle 50 and cover tab layout, cycling, visibility, replay embed, histories notice — not these three bridge tests. **2026-06-04: `test_statistics_modal.gd` runs GREEN on apricot — 8/8 pass (fixed a pre-existing detached-node bug in `test_close_without_main_node_does_not_crash`: it called `_ready()`+`_on_close()` on a node never added to the tree, so `get_tree()` was null).** The three bridge-parity tests remain absent/Rust-blocked.)_ -- [~] **Headless proof scene** — `src/game/engine/scenes/tests/statistics_proof.tscn` + `statistics_proof.gd` AUTHORED 2026-06-04 (bridge-cse lane). Boots the REAL `statistics.tscn`, seeds a 4-clan 12-turn StatsTracker fixture, opens each tab (Demographics / Graphs / Rankings / Replay / Histories) and screenshots each. Captured on **apricot.lan** (live Wayland session, gl_compatibility — `--headless` returns a null viewport image so a real display is required; plum/$SCREENSHOT_HOST down). PNGs at `apricot.lan:~/mc-ui-proofs/statistics_proof_*.png` and pulled into `.local/ui-proofs/`. All 5 tabs node-reviewed in conversation and render correctly. **Stays `[~]` pending explicit user phase-gate approval** (proof captured + agent-reviewed, not yet user-approved). Live Demographics/Graphs/Rankings data is fixture-seeded here; in-game it sources StatsTracker (real) + GdGameHistory (Rust-pending, Histories shows pending notice). +- [~] **Headless proof scene** — `src/game/engine/scenes/tests/statistics_proof.tscn` + `statistics_proof.gd` AUTHORED 2026-06-04 (bridge-cse lane). Boots the REAL `statistics.tscn`, seeds a 4-clan 12-turn StatsTracker fixture, opens each tab (Demographics / Graphs / Rankings / Replay / Histories) and screenshots each. Captured on **apricot.lan** (live Wayland session, gl_compatibility — `--headless` returns a null viewport image so a real display is required; plum/$SCREENSHOT_HOST down). PNGs at `apricot.lan:~/mc-ui-proofs/statistics_proof_*.png` and pulled into `.local/ui-proofs/`. All 5 tabs node-reviewed in conversation and render correctly. **Stays `[~]` pending explicit user phase-gate approval** (proof captured + agent-reviewed, not yet user-approved). Live Demographics/Graphs/Rankings data is fixture-seeded here; in-game it sources StatsTracker (real) + GdGameHistory (Rust-pending, Histories shows pending notice). **Copy verified clean 2026-06-04 (bridge-cse Wave-A polish):** the `statistics_*` / `trend_*` / `close` / `no_history` / `no_data` keys are now authored in `vocabulary.json` (title "Records of the Hold"; tabs "Census / Ledgers / Standings / Chronicle / Sagas"; histories notice "The hold-scribes have not yet bound these sagas."), and the demographics/graphs/rankings category labels (`demographics_*_header`) resolve to "Score / Population / Military / Cities / Technology / Wonders" after the proof calls `StatsTracker._rebuild_labels()` post-`load_vocabulary` (the autoload caches labels at init, before the proof loads the theme — proof-only ordering; in-game the theme loads first). Re-captured clean on apricot under weston: `.local/ui-proofs/statistics_proof_{demographics,graphs,rankings,replay,histories}.png` — no title-cased placeholder keys remain. **The objective stays `partial`** — this polish only removes the placeholder-copy caveat from the proof bullet; the snapshot-append wiring and the three GUT bridge-parity tests remain Rust-blocked (`✗`), so K is unchanged. ## Commandment-9 cleanup note diff --git a/.project/objectives/p2-48a-end-game-summary-gut-and-proof.md b/.project/objectives/p2-48a-end-game-summary-gut-and-proof.md index 2b6eadde..7e48359f 100644 --- a/.project/objectives/p2-48a-end-game-summary-gut-and-proof.md +++ b/.project/objectives/p2-48a-end-game-summary-gut-and-proof.md @@ -60,11 +60,23 @@ GdReplayPlayer bridge, and headless Godot infrastructure that blocks on hidden (Resigned), standings with winner/defeated outcomes, score graph, timeline, all five footer buttons. **Stays `[~]` pending explicit user phase-gate approval** (proof captured + agent-reviewed, not yet user-approved). - **Copy note:** the `endgame_*` ThemeVocabulary keys (banner/reason/footer/ - awards_pending) are NOT yet in `public/games/age-of-dwarves/vocabulary.json`, - so the screen renders title-cased fallbacks ("Endgame Banner Victory", - "Endgame Footer View Map", etc.) — readable but placeholder. Authoring final - copy into vocabulary.json is a separate content task (not in this UI fence). + **Copy note — RESOLVED 2026-06-04 (bridge-cse Wave-A polish):** the `endgame_*` + ThemeVocabulary keys (banner/reason/footer/awards_pending/outcome/event) are now + authored in `public/games/age-of-dwarves/vocabulary.json` with Dwarf-flavoured + copy, and all four `GameOverReason` banners re-captured CLEAN on apricot under + weston (`.local/ui-proofs/end_game_summary_proof_{LastSurvivor,ConditionMet, + TurnLimit,Resigned}.png`): "Victory!" / "Last Clan Standing"; "The Hold Has + Fallen" / "Mastery Achieved"; "The Reckoning" / "The Banners Are Lowered"; + outcomes "Victor"/"Fallen"; footer "Survey the Realm / Recount the Saga / Seal + to the Vault / Inscribe to Stone / Main Menu"; awards "The honour-rolls are + still being tallied." No title-cased placeholders remain. The reason keys are + PascalCase (`endgame_reason_LastSurvivor` …) matching the discriminant strings + the proof + GUT tests drive (production `game_over` emission is Rust-pending; if + it ever serializes snake_case, lowercase reason-key variants will be needed — + forward-flag only). NOTE: proof tree was mixed-version — apricot `main` lacks + the `game_state.gd` `pending_replay_game_id` member the worktree added, which + logs a benign error in the embedded replay path; it is orthogonal to the + end-game copy (no vocab on that path) and does not weaken the banner proofs. ## Dependencies diff --git a/public/games/age-of-dwarves/vocabulary.json b/public/games/age-of-dwarves/vocabulary.json index 223984cb..a72f3728 100644 --- a/public/games/age-of-dwarves/vocabulary.json +++ b/public/games/age-of-dwarves/vocabulary.json @@ -769,6 +769,36 @@ "end_game_stats_rankings_header": "Final Rankings", "end_game_stats_events_header": "Key Events", "stats_domain_research_header": "Research by Domain", + "close": "Close", + "statistics_title": "Records of the Hold", + "statistics_tab_demographics": "Census", + "statistics_tab_graphs": "Ledgers", + "statistics_tab_rankings": "Standings", + "statistics_tab_replay": "Chronicle", + "statistics_tab_histories": "Sagas", + "statistics_histories_pending": "The hold-scribes have not yet bound these sagas.", + "no_history": "No records yet carved.", + "no_data": "No tally to report.", + "trend_up": "▲", + "trend_down": "▼", + "trend_flat": "—", + "endgame_banner_victory": "Victory!", + "endgame_banner_defeat": "The Hold Has Fallen", + "endgame_banner_gameover": "The Reckoning", + "endgame_reason_LastSurvivor": "Last Clan Standing", + "endgame_reason_ConditionMet": "Mastery Achieved", + "endgame_reason_TurnLimit": "The Long Age Ends", + "endgame_reason_Resigned": "The Banners Are Lowered", + "endgame_awards_pending": "The honour-rolls are still being tallied.", + "endgame_export_dialog_title": "Inscribe Chronicle to Stone", + "endgame_footer_view_map": "Survey the Realm", + "endgame_footer_watch_replay": "Recount the Saga", + "endgame_footer_save_archive": "Seal to the Vault", + "endgame_footer_export_json": "Inscribe to Stone", + "outcome_winner": "Victor", + "outcome_defeated": "Fallen", + "event_tech_researched": "unearthed a new craft", + "event_wonder_built": "raised a great work", "combat_grudge_badge": "[Grudge]", "notification_dismiss": "Dismiss", "fmt_boss_tile": "Location: (%d, %d)", diff --git a/src/game/engine/scenes/tests/statistics_proof.gd b/src/game/engine/scenes/tests/statistics_proof.gd index 0aac6b08..613a5a9d 100644 --- a/src/game/engine/scenes/tests/statistics_proof.gd +++ b/src/game/engine/scenes/tests/statistics_proof.gd @@ -51,6 +51,13 @@ func _ready() -> void: DataLoader.load_theme("age-of-dwarves") ThemeAssets.set_theme("age-of-dwarves") ThemeVocabulary.load_vocabulary("age-of-dwarves") + ## StatsTracker caches its category labels at autoload-init (before the proof + ## loads the theme above). Rebuild them now so the Demographics/Graphs/Rankings + ## column + metric labels resolve to vocabulary copy, not title-case keys. + ## In-game the theme is loaded before StatsTracker, so this ordering is + ## proof-only. + if _has_node_singleton("StatsTracker") and StatsTracker.has_method("_rebuild_labels"): + StatsTracker._rebuild_labels() _seed_fixture() await _run()