Commit graph

3562 commits

Author SHA1 Message Date
Natalie
9e6d9ccb48 test(@projects/@magic-civilization): flip stale GdTechWeb absent-sentinel — binding has landed
test_gdextension_contract asserted GdTechWeb does NOT exist (mc-tech backlog),
but the crate + binding have since landed (`#[class(base=RefCounted)] GdTechWeb`
at api-gdext/src/lib.rs:7770; positive coverage in test_tech_web.gd). Per the
test's own instruction, removed the ABSENT_CLASSES sentinel and flipped
test_gd_tech_web_absent → test_gd_tech_web_present (guards against regression).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 20:11:15 -05:00
Natalie
060af304f9 feat(@projects/@magic-civilization): 🖥️ sprite-gen workers, operations panel + GUI dashboards
Adds a worker abstraction (engine/worker.py) with registry wiring and a server
operations endpoint, surfaced in the GUI via new Workers and Operations pages
plus dashboard/coverage/theater/variant page updates. Refreshes the ranker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:09:01 -05:00
Natalie
edcc5454e6 fix(@projects/@magic-civilization): 🎨 defer palette-variant warning until palettes load
set_palette_variant can run before set_theme populates _palettes (settings_manager
applies saved display prefs at _ready, ahead of theme load). Remember the desired
valid variant silently instead of warning; the missing-variant warning now only
fires once palettes are actually loaded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:09:01 -05:00
Natalie
0b147e66a9 feat(@projects/@magic-civilization): 🎭 hotseat multiplayer with per-seat views (p3-15)
Two+ humans sharing one device get a pass-the-device hand-off (hotseat_handoff)
gated by GameState.is_hotseat(). Each seat sees only its own view: city_renderer
fogs enemy cities until explored, prologue_overlay_renderer draws only the local
player's opening, and the AI/turn banners no longer stack across hand-offs.
Documents the flow in TURN_SEQUENCE.md, adds a headless handoff proof scene, and
marks p3-15 done (dashboard regenerated).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:08:47 -05:00
Natalie
a351a4fb44 fix(@projects/@magic-civilization): 🐛 add literal fallbacks to remaining fmt lookups (latent i18n crash de-risk)
Same fragility as 64154c8bd, applied to the 8 other `lookup("fmt_*") % args`
call sites my i18n batches introduced (knowledge_tree tier badge, credits
entry/link, game_setup AI slot/clan, past_games entry, ransom_offers tooltip,
merge_panel not-found). Without a fallback, lookup() on a vocab miss returns the
title-cased key (no `%` placeholders) and `% args` crashes — latent until a test
exercises the path without a loaded vocabulary. Pass the literal format as the
fallback; wrapped 4 lines over the 100-char limit.

Verified: headless boot parses clean (exit 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 19:18:06 -05:00
Natalie
64154c8bd8 fix(@projects/@magic-civilization): 🐛 add literal fallbacks to fmt lookups — fixes i18n format-crash regression
My earlier i18n batch (07a10054f) converted literal format strings like
`"%s (%s) — Tier %d" % [...]` to `ThemeVocabulary.lookup("fmt_...") % [...]`
without a fallback. lookup() on a miss returns the title-cased key (no `%`
placeholders), so `% [args]` throws "not all arguments converted" whenever the
vocabulary isn't loaded — which is the case in GUT unit tests. This crashed
test_great_person_modal (16) and test_throne_room_great_works (6).

Pass the literal format as the fallback arg (matching the existing
lookup(key, fallback) idiom) so these are robust when vocab is absent. Wrapped
the 4 lines that exceeded the 100-char gdlint limit.

Verified: GUT string-formatting errors 41 → 0; both test scripts now green
(suite 51 → 40 failing; the rest are pre-existing/other-session).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 19:13:46 -05:00
Natalie
8fd238b3ae refactor(@projects/@magic-civilization): ✂️ split procedural drawing into procedural_painter.gd (p2-10k)
Extract the Image-space drawing layer — role/category classifiers, fill
primitives, and unit/building/wonder/city silhouette painters + the
ROLE_*/WONDER_SHAPE_* visual constants (~330 LOC, all static & private to the
render flow) — into a self-contained ProceduralPainter helper. procedural_
renderer.gd keeps orchestration, texture caching, env toggle, and colour
derivation. 554 → 221 lines (under the 500 cap); painter 347.

Verified: gdlint clean on both; headless boot exit 0; procedural_renderer_proof
scene renders all 20 units / 10 buildings / 5 wonders / 5 cities correctly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 18:39:13 -05:00
Natalie
2c4b97a2f0 refactor(@projects/@magic-civilization): ✂️ split data_loader I/O pipeline into data_loader_io.gd (p2-10k)
Extract the JSON file-loading + shape-extraction + subscription-manifest
pipeline (~184 LOC, all private to the load flow) into a DataLoaderIo helper
that operates on _data/_raw by reference — mirroring the existing _ecology /
_worlds delegation idiom. data_loader.gd 652 → 457 (under the 500 cap).

Verified: gdlint clean on both files; headless boot loads 815 entries through
the new pipeline (manifest filtering intact), exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 18:31:44 -05:00
Natalie
507e782fa1 refactor(@projects/@magic-civilization): 🔥 cull 3 orphaned helpers (~266 LOC)
All three statically unreachable + clean headless boot. Verified the scan is
sound (a known-live helper correctly resolves its callers):
- game_state_serialization_helpers.gd — docstring "Called by game_state.gd" is
  stale; the caller was removed by the npc_buildings refactor
- turn_processor_signals.gd — no longer referenced by the turn_processor module
- achievement_tracker.gd — orphaned by the legacy-save-manager removal

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 18:23:52 -05:00
Natalie
7dd049df13 refactor(@projects/@magic-civilization): 🔥 cull 13 dead .gd files (~3,135 LOC) — orphaned during atomic rebuild
Statically unreachable (no .tscn ext_resource, no preload/load-by-path, no
class_name usage, no dynamic string-built loads) + clean headless boot verifies
no load-time breakage. Confirmed dead, with what superseded each:

- selection_manager.gd (482) — selection handled by event_bus/unit/player/
  turn_processor_helpers; movement_animator.gd (67) used only by it (dead cluster)
- hex_overlay_renderer.gd (475) — superseded by overlay_renderer.gd; only
  comment mentions remained
- weather_events.gd / WeatherEvents (474) — weather is Rust-side; sole "ref" was
  a climate.gd config dict-key "weather_events": true, never the class
- indicator_renderer (295), river_renderer (121), road_renderer (88) — superseded
  by procedural_renderer / overlay suite; zero engine refs
- ecosystem_simplified (292), fauna_simplified (124) — prototype variants; live
  fauna.gd is the real one
- atmosphere_chemistry (254), water_body_finder (197), map_loader (133),
  pending_actions (133) — orphaned helpers, zero engine refs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 18:19:59 -05:00
Natalie
42ac86e7ec refactor(@projects/@magic-civilization): 🔥 delete dead duplicate entities/auto_play.gd (2677 LOC tech debt)
The live autoplay harness is scenes/tests/auto_play.gd (registered autoload
`res://engine/scenes/tests/auto_play.gd`). src/entities/auto_play.gd was a stale
fork — diverged from the tests/ copy at de7b8df16, never received later updates,
and is referenced by NOTHING (no preload/load/class_name; only a comment mention
in a test). Verified zero string-based loads across .gd/.tscn/.sh/.py/.tres.

Removes one of the gdlint max-file-lines violations cleanly (Commandment 9 —
dead code goes entirely). engine/src over-cap files: 12 → 11.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 17:58:33 -05:00
Natalie
7eb2897854 feat(@projects/@magic-civilization): 🎨 Grok sprite-generation pipeline + starter orchestration
Adds a Grok image backend (grok_generator.py) behind a generator factory, a
starter-set orchestrator (starter.py, orchestrate_starter.py, starter_manifest.json)
and a Grok PoC harness with proof renders. Updates the ranker/processor/composition
prompts and sprite-config; refreshes the sprite-gallery design preview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 17:57:01 -05:00
Natalie
269316722e feat(@projects/@magic-civilization): 🎬 declarative start-script system (p3-14)
Game opening becomes a moddable JSON script driven by mc_worldsim::StartScriptRunner
and exposed to Godot via GdStartScript. Start scripts + dwarf tribe/wanderer units
live in public/resources/start_scripts; START_SCRIPTS.md documents the contract.
Adds tools/validate-start-scripts.py + wires it into CI (stage 3b) and verify.sh
(step 0b). Marks p3-14 done and regenerates the objectives dashboard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 17:56:50 -05:00
Natalie
30f12f5e7e i18n(@projects/@magic-civilization): 🌐 route remaining .tscn UI text through ThemeVocabulary — i18n gate GREEN
Wire the static .tscn label/button text in 5 scenes (end_game_summary section
titles + footer, merge_panel, city_screen merge button, game_setup section
headers, past_games) through ThemeVocabulary.lookup() in their controllers'
_ready, and remove the hardcoded .tscn text. Add the corresponding vocab keys;
drop 3 redundant endgame_* keys (footer buttons already use endgame_footer_*).

validate-i18n now passes: 145 scenes scanned, 0 hardcoded UI strings.
This clears the i18n verify gate with no bypass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 17:42:38 -05:00
Natalie
07a10054f4 i18n(@projects/@magic-civilization): 🌐 route 25 hardcoded .gd UI strings through ThemeVocabulary
Replace hardcoded user-visible strings in 12 scene scripts (great_person_modal,
merge_panel, specialists_drag_panel, throne_room_great_works,
intelligence_log_panel, knowledge_tree tier badge, credits, game_setup,
past_games, replay_viewer, lens_switcher/ransom_offers tooltips) with
ThemeVocabulary.lookup() + add the corresponding keys (incl. fmt_* for format
strings). Part of clearing validate-i18n with no bypass. (48 → 23 remaining.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 17:34:35 -05:00
Natalie
5b2b88b2a9 i18n(@projects/@magic-civilization): 🌐 route replay_viewer button/label text through ThemeVocabulary
replay_viewer.tscn hardcoded its title/placeholder/turn/play/step/speed-label
text (pre-existing since May). Route the word labels through ThemeVocabulary
(replay_back/title/placeholder/step keys) set in _ready; runtime-set labels
(turn/play-pause/speed) lose their static .tscn text. Symbolic speed buttons
(0.5×/1×/2×) left as-is (not flagged). Clears validate-i18n for this scene.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:32:16 -05:00
Natalie
91cfb09dc2 docs(@projects/@magic-civilization): 📝 p2-87 Godot-layer colour-SoT complete (override→inheritance + StyleBox DRY)
Mark override→inheritance done: all font_color migrated to theme_type_variation
/ redundant-deleted / dynamic carve-outs, and the one repeated modal-panel
StyleBox extracted into shared PanelModal/PanelModalPadded variations
(ac019c060). Remaining inline StyleBoxes are single-use-custom or dynamic
carve-outs (no shared pattern left). Coverage gate guards regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:16:12 -05:00
Natalie
ac019c0607 refactor(@projects/@magic-civilization): 🎨 shared PanelModal theme variations replace 4 inline modal styleboxes (p2-87)
Extend the type-variation pipeline to emit StyleBox variations, then dedup the
repeated "modal panel" stylebox (bg=background.panel, border=border.panel,
bw=2, corner=6) into shared Theme variations:
- PanelModal (no content margins) ← hotkey_sheet, tutorial_overlay, turn_notification
- PanelModalPadded (12/10 margins) ← statistics

4 inline StyleBoxFlat builds → theme_type_variation inheritance. Value-preserving
(variation stylebox == the inline geometry/colours). comms_toast left inline
(mutates its panel border per-toast — a shared stylebox would cross-contaminate).

Verified: PanelModal/Padded baked into ui_theme.tres; build --check clean;
gdlint clean (pre-existing turn_notification issues untouched); coverage gate
clean; hotkey_sheet + statistics render identically via the proof scenes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:03:08 -05:00
Natalie
7cf1393c19 docs(@projects/@magic-civilization): 📝 p2-87 — Godot-layer colour-SoT complete
Record audit finding (no remaining base-palette dedup; guide bg tokens aliased)
and add a status header: all in-game colour derives from the single token
source, verified + gated. Remaining items are non-Godot (guide de-hex,
accessibility variants) or non-colour (StyleBox geometry, font-size).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:40:56 -05:00
Natalie
d2e711ee9e refactor(@projects/@magic-civilization): 🎨 dedup guide bg tokens → background.* aliases (p2-87)
guide.bg{Primary,Secondary,Tertiary} held the same hexes as
background.{base,surface,raised} — the only genuine colour duplication left in
the token table. Aliased them so each base colour lives in exactly one place.
Value-preserving (resolved meta blob unchanged; ui_theme.tres byte-identical).

Audit finding: no other dedup exists in the Godot-layer tokens — tech.* are
already aliases of palette.*, and the gold/green families are distinct values,
not duplicates. The single-colour-source goal has no remaining godot-layer work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:39:49 -05:00
Natalie
0c2d7c6d4a feat(@projects/@magic-civilization): colour-SoT coverage gate locks in the migration (p2-87)
Add tools/check-ui-color-sources.py: fails if a hardcoded numeric Color()/Color8()
is applied to a widget in a scene (add_theme_*_override / StyleBox *_color).
Allows computed Color(accent.r,…), transparent, named constants, and var-init
fallbacks; excludes scenes/tests + the 3 precursor deletion files. Passes clean
on live scenes (exit 0). Wired into ./run verify as step 17 so a hardcoded
colour can't creep back in.

Capstone for the override→inheritance / single-colour-system work: colours in
live scenes now provably come from the design-token source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:27:56 -05:00
Natalie
7e110ded35 docs(@projects/@magic-civilization): 📝 p2-86 — render-client fix verified (connect-first + 150s grace)
Tools register after restart (confirmed). First in-session call timed out (slow
throttled MCP-spawned windowed Godot); fixed via connect-first + 150s grace +
respawn (6310433cc), re-verified through the built client (NODE_EXIT=0, real
3.85MB PNG). Running MCP needs to reload the post-fix dist (one restart) to use
it in-session.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:16:52 -05:00
Natalie
5b3dcd56dc chore(@projects/@magic-civilization): 🔧 prune 7 stale agent worktree gitlinks
Stale May 31–Jun 8 agent worktrees, all behind main with no unique work
(every change landed or superseded). Removed checkouts, branches, and the
accidentally-committed gitlink entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 09:50:19 -05:00
Natalie
6310433cc5 fix(@projects/@magic-civilization): 🐛 render MCP client — connect-first + longer boot grace (p2-86)
The first in-session magic_civ_screenshot timed out: windowed Godot spawned by
the MCP (a throttled background GUI process under Claude Code) inits far slower
than a terminal launch, exceeding the 45s connect timeout — though the driver
DID bind the port (verified 8787 open). Fixes:

- connect-first: if a rendered driver is already listening (e.g. a foreground
  `MC_MCP_RENDER=1 ./run play`), connect to it directly and skip the slow,
  throttled background spawn entirely.
- start timeout 45s -> 150s for the spawn fallback.
- a failed start now clears its cached promise + reaps the child so the next
  tool call respawns instead of replaying the rejection until an MCP restart.

Re-verified end-to-end through the built RenderClient (spawn path): ping {ok},
screenshot -> a real 3420x1923 PNG of the live world map.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 09:11:20 -05:00
Natalie
0349a4e8fd chore(@projects/@magic-civilization): 🔧 .local→.lan mesh hosts + objectives dashboard sync
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:30:29 -05:00
Natalie
0234bb5892 feat(@projects/@magic-civilization): freepeople tribe-founding prologue (p0-34)
mc-turn prologue/chronicle, mc-mapgen spawn_box, api-gdext surface,
prologue_driver + AI turn-bridge dispatch, setup.json start-mode
tournament/custom + wanderer spawn tuning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:30:09 -05:00
Natalie
d41a65bd50 feat(@projects/@magic-civilization): lair POI sprites + tile tooltips (p2-85)
world_map lair POI overlay, tile_info_panel tooltip wiring, lair standin
sprites + build_demo_lairs.py, tooltip unit test, lair proof scenes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:29:54 -05:00
Natalie
8e77d36434 feat(@projects/@magic-civilization): add dwarf gendered unit standin sprites + gen tooling
New per-unit male/female standin PNGs, build_standins.py + icon_rules
updates, license/standins ledgers, manifest roster + DevSpritesPage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:29:36 -05:00
Natalie
45d0278522 refactor(@projects/@magic-civilization): 🎨 unit sprite paths → _dwarf_male/_dwarf_female naming
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 05:29:19 -05:00
Natalie
fa7dbb6b47 docs(@projects/@magic-civilization): 📝 p2-87 — colour-SoT override→inheritance complete; loop stopped
font_color override→inheritance migration done. Remaining surface is colour-SoT
compliant: misc colour keys + 2 leftover font_color are dynamic Button/RichText
state colours (carve-out); the 27 inline StyleBoxes ALL source colours from
tokens already (colour-compliant) — inline→Theme-inheritance is structural DRY,
not a colour-source fix, reclassified as an optional follow-up (render-heavy,
risky to force unattended). Loop a52b2931 stopped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:45:18 -05:00
Natalie
04e2dda443 docs(@projects/@magic-civilization): 📝 p2-87 font_color migration complete; StyleBox sweep next
font_color override→inheritance done (variations + 14 redundant deletes + 2
dynamic button carve-outs). Remaining loop work: misc colour keys (triage) +
27 inline StyleBoxes (migrate default-duplicates, carve out custom).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:41:30 -05:00
Natalie
4daab42f72 refactor(@projects/@magic-civilization): 🎨 delete 14 redundant Label font_color=text.primary overrides (p2-87)
These Label nodes set font_color to text.primary — which is already the Label
theme default in ui_theme.tres — so the override was a no-op duplicate. Deleted
across 12 scenes; the Labels now inherit the default. Value-preserving by
construction (default == deleted value); all targets confirmed Label-typed.

gdlint: no new issues (pre-existing class-order/const-name in some files
untouched). knowledge_tree render-verified via tech_tree_proof (exit 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:39:54 -05:00
Natalie
3942adf26b refactor(@projects/@magic-civilization): 🎨 top_bar happiness label → conditional theme_type_variation (p2-87)
Migrate the %HappinessLabel state-conditional font_color (positive/negative/
neutral) to theme_type_variation (LabelPositive/Negative/Secondary). It's a
Label (top_bar.tscn:86), so the variation applies cleanly and is value-preserving.

This completes the static Label font_color override→inheritance migration. The
only remaining add_theme_color_override("font_color", ...) are 2 Button
state-toggles (lens_switcher active-lens, overlay_panel) — genuinely dynamic
(toggled per UI state, would need a Button-based variation), left as the
dynamic carve-out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:32:01 -05:00
Natalie
6a3bc95b06 docs(@projects/@magic-civilization): 📝 p2-87 override→inheritance sweep progress (~66 migrated)
Record the loop sweep: knowledge_tree/statistics/hotkey/happiness + 19-scene
node-type-aware batch. Remaining: ~7 Button-node overrides (need a Button
variation), redundant-default deletes, font-size + StyleBox sub-sweeps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:28:34 -05:00
Natalie
dc78a15670 refactor(@projects/@magic-civilization): 🎨 migrate 19 scenes' Label colours to theme_type_variation (p2-87 override→inheritance)
Node-type-aware batch: replace add_theme_color_override("font_color",
ThemeAssets.color("<token>")) with theme_type_variation on declared Label vars
only (35 overrides across 19 HUD/menu/notification/world-map scenes). Non-Label
targets (Buttons in top_bar/overlay_panel/lens_switcher) and dynamic/param
colours are left as overrides — a Label variation would break a Button's
stylebox lookup.

Value-preserving (each variation carries the identical token colour; instance
font_size overrides are untouched). All changed files gdlint-clean. Pattern is
render-proven by the statistics + hotkey_sheet iterations; live world_map render
skipped here because the working tree carries a concurrent session's uncommitted
world_map.gd / tile_info_panel edits (left unstaged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:27:37 -05:00
Natalie
3f82ff6fb5 refactor(@projects/@magic-civilization): 🎨 happiness_breakdown_panel labels → theme_type_variation (p2-87)
Migrate 5 font_color overrides to theme_type_variation
(LabelTitle/Secondary/Positive/Negative/Muted). Inline StyleBoxFlat (panel bg)
left for the StyleBox sub-sweep.

Value-preserving (variations carry the same token colours) + gdlint clean. No
direct render harness (this panel opens on in-game interaction, not coverable by
a proof scene or the magic-civ open_screen surface); the variation pattern is
render-proven by the statistics + hotkey_sheet iterations.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:20:26 -05:00
Natalie
3b09bb35a1 refactor(@projects/@magic-civilization): 🎨 hotkey_sheet labels → theme_type_variation (p2-87 override→inheritance)
Migrate 6 font_color overrides to theme_type_variation
(LabelTitle/Muted/Gold/Disabled) and delete 1 redundant text.primary override
(== Label theme default). Inline StyleBoxFlat (panel bg) left for the StyleBox
sub-sweep.

Value-preserving; render-verified via hotkey_sheet_proof — title/headers gold,
keys gold, descriptions inherit default, no regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:17:35 -05:00
Natalie
a86aa6f67c refactor(@projects/@magic-civilization): 🎨 statistics screen labels → theme_type_variation (p2-87 override→inheritance)
Migrate 15 add_theme_color_override("font_color", ...) calls in statistics.gd to
theme_type_variation (LabelTitle/Secondary/Muted/Disabled/Positive/Negative).
Widgets inherit the colour from ui_theme.tres instead of hand-setting it.

Value-preserving (variations carry the same token colours). Render-verified on
plum via statistics_proof — all 5 tabs render; Rankings shows title gold, metric
secondary, trend arrows green, no regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 04:12:47 -05:00
Natalie
1cb6a657ea docs(@projects/@magic-civilization): 📝 p2-87 — override→inheritance foundation + pilot landed
Record the type-variation foundation (build-tool emission + 9 Label variations)
and the knowledge_tree pilot (4 labels migrated, render-verified). Note the
remaining scene-by-scene sweep + redundant-default deletes + font-size/StyleBox
sub-sweeps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 02:51:46 -05:00
Natalie
b26116acc9 refactor(@projects/@magic-civilization): 🎨 knowledge_tree labels inherit via theme_type_variation (p2-87 pilot)
First override→inheritance migration, proving the pattern. Replace 4
add_theme_color_override("font_color", ...) calls with theme_type_variation:
cost→LabelMuted, hint→LabelDisabled, title→LabelTitle, flavor→LabelSecondary.
Widgets now inherit the colour from ui_theme.tres instead of hand-setting it.

Value-preserving (variations carry the same token colours). Render-verified on
plum via tech_tree_proof — cost labels render the muted colour correctly, tree
unchanged. Remaining accent-param labels (_make_pill/_make_section) stay as
overrides (dynamic colour). The redundant text.primary overrides (= Label
default) are a separate delete-pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 02:50:37 -05:00
Natalie
54767fbd98 feat(@projects/@magic-civilization): 🎨 theme type-variations — inheritance targets for override→inheritance migration (p2-87)
Foundation for collapsing the ~188 add_theme_color_override calls onto Godot
Theme inheritance. Adds a `typeVariations` section to design-tokens.json and
emits each as a Godot type variation in ui_theme.tres.

9 Label variations covering the high-count font_color override patterns:
LabelTitle/Muted/Secondary/Disabled/Positive/Negative/Warning/Gold/Science
(→ text.title/muted/secondary/disabled, semantic.positive/negative/warning,
accent.gold/science). Widgets set `theme_type_variation = "LabelMuted"` to
inherit instead of `add_theme_color_override("font_color", ...)`.

Additive — nothing consumes them yet, zero visual change. Verified: variations
baked into ui_theme.tres, theme --check clean, headless load exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 02:45:59 -05:00
Natalie
6f89d0faaa docs(@projects/@magic-civilization): 📝 p2-87 — minimap render VERIFIED + phase 1d partial
Mark the minimap biome-colour single-source path verified via the magic-civ
rendered driver (live world_map shows minimap with real biome colours), and
record phase 1d progress (city_proof + world_gen_lab_proof rerouted; 3 proof
copies remain — climate legend + 2 that don't load_theme).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 01:45:11 -05:00
Natalie
b6573bcb30 refactor(@projects/@magic-civilization): 🎨 route city/world_gen_lab proofs to biome_colors single source (p2-87 phase 1d)
Delete the hardcoded TERRAIN_COLORS dict copies in city_proof.gd and
world_gen_lab_proof.gd; both now call DataLoader.get_biome_color(tile.biome_id)
(both load the theme, so the source is populated). Verified: city_proof renders
correct biome colours (ocean/forest/plains/mountains/volcano), no magenta.

Remaining phase-1d copies: climate_proof (also draws a colour legend off the
dict) + improvement_proof / world_map_proof (don't load_theme — would need a
theme-load added first). Tracked in p2-87.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 01:43:25 -05:00
Natalie
9dabf0941b docs(@projects/@magic-civilization): 📝 p2-87 biome-colour reconciliation progress (phases 1a–1c)
Record the single-source biome-colour work: biome_colors.json + DataLoader
accessor (1a), hex_renderer reroute (1b), minimap reroute + biome_id bugfix
(1c). Note remaining phase 1d (5 proof-scene dicts) + the minimap visual-proof
gap (needs a real world_map render).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 00:03:02 -05:00
Natalie
ca7aa6d797 fix(@projects/@magic-civilization): 🎨 minimap reads biome colour from single source + biome_id bugfix (p2-87 phase 1c)
Delete minimap.gd's divergent hardcoded TERRAIN_COLORS dict (+ DEFAULT_TERRAIN_COLOR);
terrain colour now comes from DataLoader.get_biome_color() — the same single
source (biome_colors.json) the main map renderer uses.

Also fixes a latent bug: the minimap keyed on `raw_tile.get("terrain_id")`, but
tiles only carry `biome_id` (map_loader.gd:119) — so the old terrain lookup
returned nothing and the dict's simple-name keys never matched the biome_id
namespace. Now reads biome_id, matching hex_renderer.

Value-preserving (get_biome_color returns hex_renderer's lifted values) and a
strict improvement over the broken terrain_id path. gdlint clean.

NOTE: populated-minimap visual proof deferred — a standalone mount can't
reproduce the minimap's world_map HUD context (camera/scale/GameMapScript); to
be confirmed via a real world_map render (magic-civ rendered driver).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 00:01:40 -05:00
Natalie
eb1a81f009 refactor(@projects/@magic-civilization): 🎨 hex_renderer reads biome colour from single source (p2-87 phase 1b)
Delete hex_renderer.gd's hardcoded 69-entry TERRAIN_COLORS dict; the terrain
sprite fallback now calls DataLoader.get_biome_color(biome_id) (the values were
lifted from this very dict in phase 1a, so value-preserving). One fewer copy of
the biome palette; file drops ~82 lines. gdlint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 23:47:30 -05:00
Natalie
16bfb2ad31 feat(@projects/@magic-civilization): 🎨 single-source biome_colors.json + DataLoader.get_biome_color (p2-87 phase 1a)
Establish ONE data source for biome render colour, lifting hex_renderer.gd's
authoritative 69-entry palette (value-preserving, biome_id -> [r,g,b] 0-255).

- public/games/age-of-dwarves/data/biome_colors.json — the single source.
- DataLoader: _load_biome_colors() at theme load + get_biome_color(biome_id)
  with '_default' fallback then magenta sentinel.

Additive only — no consumer rerouted yet (next phases: hex_renderer, minimap,
proof scenes all read this + delete their hardcoded TERRAIN_COLORS dicts).
Verified: headless load clean, biome_colors.json parses, 0 script errors.
(data_loader.gd max-file-lines is pre-existing, tracked by p2-10k.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 23:44:50 -05:00
Natalie
8fd3ef4ee3 docs(@projects/@magic-civilization): 📝 p2-86 phase 2 done — MCP rendered tools verified via built TS
magic_civ_screenshot / open_screen tools + render_client TCP transport +
render-driver-server.sh, verified end-to-end through the compiled RenderClient
(ping {ok}, screenshot -> 3420x1923 PNG of the live world map). Only the
tool-surfacing-in-a-Claude-session step remains (needs a restart; dist/ builds
locally).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 21:10:20 -05:00
Natalie
70537bc0d1 feat(@projects/@magic-civilization): claude-player-mcp rendered tools — magic_civ_screenshot / open_screen (p2-86 phase 2)
- render_client.ts: TCP client that spawns the rendered game via
  render-driver-server.sh, then sends screenshot / open_screen / ping correlated
  by id (distinct from the headless stdin HarnessClient).
- scripts/render-driver-server.sh: boots the REAL game (MC_AUTO_START +
  MC_MCP_RENDER, gl_compatibility, not --headless) so the driver captures real
  frames; client connects on MC_MCP_PORT.
- index.ts: magic_civ_screenshot + magic_civ_open_screen tools, render-client
  holder + cleanup.

Verified end-to-end through the BUILT TypeScript (no Claude restart needed):
RenderClient -> game -> TCP ping {ok}, screenshot -> a real 3420x1923 PNG of the
live world map. dist/ is gitignored (built locally per .mcp.json); the tools
surface in a Claude session after the next restart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 21:09:10 -05:00
Natalie
6721f3f2f3 docs(@projects/@magic-civilization): 📝 p2-86 phase 1 done — auto-start + TCP rendered driver verified
Mark Phase 1 acceptance complete: MC_AUTO_START rendered boot, mcp_render_driver
TCP autoload (screenshot/open_screen/ping), plum-verified. Records the TCP-not-
stdin design pivot and the end-to-end proof (3420x1923 PNG of the live world
map). Phase 2 (TS MCP tools + npm install + .mcp.json + restart) remains.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 20:30:48 -05:00