refactor(@projects/@magic-civilization): 🎨 tokenize throne_room colours (p2-74 cluster 4)

Route scenes/menus/throne_room.gd off its 12 inline Color() literals
(the _layer_to_color placeholder palette) onto design tokens.

- Add color.throne.* (12 decoration-category placeholder colours) to
  design-tokens.json using exact-hex equivalents — zero visual change.
- Regenerate ui_theme.tres via tools/build-ui-theme.py (--check clean).
- Refactor _layer_to_color from a 13-return if-chain to a const
  LAYER_COLOR_TOKENS dict + 2 returns (DRY; clears a pre-existing
  max-returns gdlint warning too).

Verified on plum: JSON valid, theme --check clean, all 13 token refs
resolve, 0 Color() remain, gdlint fully clean on the file. Apricot visual
proof pending (placeholders render only on missing decoration sprites).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-15 21:57:49 -05:00
parent 021d57336f
commit fd5d6f1e88
4 changed files with 103 additions and 26 deletions

View file

@ -95,6 +95,68 @@
"$description": "Knowledge-tree — dimmed/requirement accent"
}
},
"throne": {
"structure": {
"$value": "#4d3824",
"$type": "color",
"$description": "Throne-room placeholder — structure (background/walls/ceiling/floor)"
},
"seat": {
"$value": "#8c6b1a",
"$type": "color",
"$description": "Throne-room placeholder — throne + backdrop"
},
"forge": {
"$value": "#804714",
"$type": "color",
"$description": "Throne-room placeholder — forge display / armory"
},
"trophy": {
"$value": "#802e1a",
"$type": "color",
"$description": "Throne-room placeholder — trophy wall / honor display"
},
"shrine": {
"$value": "#334766",
"$type": "color",
"$description": "Throne-room placeholder — shrine / ancestor monument"
},
"provisions": {
"$value": "#4d6626",
"$type": "color",
"$description": "Throne-room placeholder — brewery / gemstone vault"
},
"mapTable": {
"$value": "#264d59",
"$type": "color",
"$description": "Throne-room placeholder — map table"
},
"garden": {
"$value": "#1f522e",
"$type": "color",
"$description": "Throne-room placeholder — garden"
},
"court": {
"$value": "#665947",
"$type": "color",
"$description": "Throne-room placeholder — court"
},
"pedestal": {
"$value": "#8c7a33",
"$type": "color",
"$description": "Throne-room placeholder — wonder pedestal"
},
"special": {
"$value": "#73528c",
"$type": "color",
"$description": "Throne-room placeholder — special decorations"
},
"default": {
"$value": "#40382e",
"$type": "color",
"$description": "Throne-room placeholder — unmapped layer fallback"
}
},
"background": {
"deepest": {
"$value": "#171219",

View file

@ -87,13 +87,24 @@ always-green. Order by player visibility: HUD/world-map → city → combat →
— only `engine/src/` + test dirs are). **Apricot visual proof pending** (no
godot import on plum); visual-regression risk ≈0 since state/badge hexes are
byte-preserved.
- **Cluster 4** (throne room) — landed. `scenes/menus/throne_room.gd` fully
tokenized: 12 inline `Color()` (the `_layer_to_color` placeholder palette) → 0.
Added `color.throne.*` (12 decoration-category placeholder colours) to
design-tokens.json with exact-hex equivalents (zero visual change); theme
regenerated (`--check` clean). Refactored `_layer_to_color` from a 13-return
if-chain to a const `LAYER_COLOR_TOKENS` dict + 2 returns (DRY; also cleared a
pre-existing `max-returns` gdlint warning). gdlint now **fully clean** on the
file. Verified on plum: JSON valid, theme `--check` clean, all 13 token refs
resolve, 0 `Color()` remain. Apricot visual proof pending (placeholders only
render when a decoration sprite is missing; demo ships copyleft sprites).
- **Remaining**: minimap (`scenes/hud/minimap.*`) is **already compliant** — its
23 `Color()` are the documented `TERRAIN_COLORS` game-content exception (Rail-2
follow-up, OOS for this visual pass) + token-seeded `_ready()` fallbacks;
`world_map_hud.*` / `weather_visualizer.*` / `encyclopedia_panel.*` already 0.
Genuinely-remaining convertibles: `menus/throne_room.gd` (12),
`world_map/courier_route_overlay.gd` (5), `world_map/arena_playback.gd` (4),
`ui/lens_switcher.gd` (4), `ui/ingame_menu.gd` (4), plus single-literal stragglers.
Genuinely-remaining convertibles: `world_map/courier_route_overlay.gd` (5),
`world_map/arena_playback.gd` (4), `ui/lens_switcher.gd` (4),
`ui/ingame_menu.gd` (4), plus single-literal stragglers
(`world_map_units.gd`, `world_map.gd`, `tile_info_panel.gd`, `arena_overlay.gd`).
Precursor scenes `overviews/demographics.gd` (10) / `end_game_stats.gd` (6) /
`victory_screen.gd` (2) are Commandment-9 deletion candidates (p2-47/p2-48) —
skip tokenizing doomed files.

View file

@ -102,7 +102,7 @@ corner_radius_bottom_left = 2
corner_radius_bottom_right = 2
[resource]
metadata/tokens = "{\"accent.gold\":\"d9a020\",\"accent.goldBright\":\"d9b33f\",\"accent.goldPress\":\"ffd14d\",\"accent.goldResource\":\"f2d133\",\"accent.ping\":\"ffd973\",\"accent.sage\":\"66b866\",\"accent.science\":\"66bfff\",\"background.base\":\"1a1410\",\"background.deepest\":\"171219\",\"background.happiness\":\"0f0d07\",\"background.hud\":\"00000099\",\"background.list\":\"120e1e\",\"background.listSelected\":\"3f2d0d\",\"background.menu\":\"0e0a17\",\"background.overlay\":\"0000009e\",\"background.panel\":\"17121e\",\"background.raised\":\"2a2018\",\"background.surface\":\"221a14\",\"border.divider\":\"99731f80\",\"border.focus\":\"d9b340ff\",\"border.happiness\":\"b39940d9\",\"border.list\":\"4d4014b2\",\"border.listSelected\":\"d9b340cc\",\"border.panel\":\"73591fcc\",\"button.bgHover\":\"331a0d\",\"button.bgNormal\":\"1f1733\",\"button.bgPressed\":\"472f0f\",\"climate.cold\":\"1a4dff\",\"climate.hot\":\"ff260d\",\"climate.textCold\":\"66b3ff\",\"climate.textNeutral\":\"d9e0d9\",\"climate.textWarming\":\"ff731a\",\"climate.warm\":\"26cc40\",\"fog.explored\":\"000000b2\",\"fog.unexplored\":\"1a160fff\",\"guide.bgPrimary\":\"1a1410\",\"guide.bgSecondary\":\"221a14\",\"guide.bgTertiary\":\"2a2018\",\"guide.dwarfAccent\":\"8b6a1a\",\"guide.dwarfPrimary\":\"c07040\",\"guide.dwarfPrimaryDark\":\"8a4a28\",\"guide.dwarfPrimaryLight\":\"e09868\",\"guide.textMuted\":\"7a6048\",\"guide.textPrimary\":\"f0e4d0\",\"guide.textSecondary\":\"b8a078\",\"player.blue\":\"3366ff\",\"player.brown\":\"806659\",\"player.cyan\":\"1accd9\",\"player.gray\":\"999999\",\"player.green\":\"33cc4d\",\"player.magenta\":\"cc4d80\",\"player.navy\":\"4d4d99\",\"player.orange\":\"e6801a\",\"player.purple\":\"b24de6\",\"player.red\":\"e63333\",\"player.sage\":\"66b366\",\"player.yellow\":\"e6cc1a\",\"semantic.diplomacy\":\"e68c73\",\"semantic.goldenAge\":\"ffeb66\",\"semantic.negative\":\"d95940\",\"semantic.positive\":\"66e666\",\"semantic.trade\":\"ccbf73\",\"semantic.warning\":\"e69933\",\"tech.availableBg\":\"d9bf1ae6\",\"tech.availableBorder\":\"ffe64d\",\"tech.currentBg\":\"4d80e6e6\",\"tech.lockedBg\":\"666666b3\",\"tech.lockedBorder\":\"808080\",\"tech.researchedBg\":\"33b333e6\",\"tech.researchedBorder\":\"4de64d\",\"tech.selectedBorder\":\"ffffffff\",\"text.button\":\"e0d199\",\"text.buttonHover\":\"ffeb80\",\"text.buttonPressed\":\"ffffb3\",\"text.disabled\":\"80806680\",\"text.muted\":\"b2b2b2\",\"text.primary\":\"e0d8c8\",\"text.secondary\":\"bfb7a6\",\"text.title\":\"f2d973\",\"unlockAccent.building\":\"8b6914\",\"unlockAccent.dim\":\"ffffff8c\",\"unlockAccent.improvement\":\"4a7c3f\",\"unlockAccent.lens\":\"2d5a8b\",\"unlockAccent.mechanic\":\"6b3fa0\",\"unlockAccent.resource\":\"a0522d\",\"unlockAccent.unit\":\"c9a84c\",\"unlockAccent.wonder\":\"a06a3f\"}"
metadata/tokens = "{\"accent.gold\":\"d9a020\",\"accent.goldBright\":\"d9b33f\",\"accent.goldPress\":\"ffd14d\",\"accent.goldResource\":\"f2d133\",\"accent.ping\":\"ffd973\",\"accent.sage\":\"66b866\",\"accent.science\":\"66bfff\",\"background.base\":\"1a1410\",\"background.deepest\":\"171219\",\"background.happiness\":\"0f0d07\",\"background.hud\":\"00000099\",\"background.list\":\"120e1e\",\"background.listSelected\":\"3f2d0d\",\"background.menu\":\"0e0a17\",\"background.overlay\":\"0000009e\",\"background.panel\":\"17121e\",\"background.raised\":\"2a2018\",\"background.surface\":\"221a14\",\"border.divider\":\"99731f80\",\"border.focus\":\"d9b340ff\",\"border.happiness\":\"b39940d9\",\"border.list\":\"4d4014b2\",\"border.listSelected\":\"d9b340cc\",\"border.panel\":\"73591fcc\",\"button.bgHover\":\"331a0d\",\"button.bgNormal\":\"1f1733\",\"button.bgPressed\":\"472f0f\",\"climate.cold\":\"1a4dff\",\"climate.hot\":\"ff260d\",\"climate.textCold\":\"66b3ff\",\"climate.textNeutral\":\"d9e0d9\",\"climate.textWarming\":\"ff731a\",\"climate.warm\":\"26cc40\",\"fog.explored\":\"000000b2\",\"fog.unexplored\":\"1a160fff\",\"guide.bgPrimary\":\"1a1410\",\"guide.bgSecondary\":\"221a14\",\"guide.bgTertiary\":\"2a2018\",\"guide.dwarfAccent\":\"8b6a1a\",\"guide.dwarfPrimary\":\"c07040\",\"guide.dwarfPrimaryDark\":\"8a4a28\",\"guide.dwarfPrimaryLight\":\"e09868\",\"guide.textMuted\":\"7a6048\",\"guide.textPrimary\":\"f0e4d0\",\"guide.textSecondary\":\"b8a078\",\"player.blue\":\"3366ff\",\"player.brown\":\"806659\",\"player.cyan\":\"1accd9\",\"player.gray\":\"999999\",\"player.green\":\"33cc4d\",\"player.magenta\":\"cc4d80\",\"player.navy\":\"4d4d99\",\"player.orange\":\"e6801a\",\"player.purple\":\"b24de6\",\"player.red\":\"e63333\",\"player.sage\":\"66b366\",\"player.yellow\":\"e6cc1a\",\"semantic.diplomacy\":\"e68c73\",\"semantic.goldenAge\":\"ffeb66\",\"semantic.negative\":\"d95940\",\"semantic.positive\":\"66e666\",\"semantic.trade\":\"ccbf73\",\"semantic.warning\":\"e69933\",\"tech.availableBg\":\"d9bf1ae6\",\"tech.availableBorder\":\"ffe64d\",\"tech.currentBg\":\"4d80e6e6\",\"tech.lockedBg\":\"666666b3\",\"tech.lockedBorder\":\"808080\",\"tech.researchedBg\":\"33b333e6\",\"tech.researchedBorder\":\"4de64d\",\"tech.selectedBorder\":\"ffffffff\",\"text.button\":\"e0d199\",\"text.buttonHover\":\"ffeb80\",\"text.buttonPressed\":\"ffffb3\",\"text.disabled\":\"80806680\",\"text.muted\":\"b2b2b2\",\"text.primary\":\"e0d8c8\",\"text.secondary\":\"bfb7a6\",\"text.title\":\"f2d973\",\"throne.court\":\"665947\",\"throne.default\":\"40382e\",\"throne.forge\":\"804714\",\"throne.garden\":\"1f522e\",\"throne.mapTable\":\"264d59\",\"throne.pedestal\":\"8c7a33\",\"throne.provisions\":\"4d6626\",\"throne.seat\":\"8c6b1a\",\"throne.shrine\":\"334766\",\"throne.special\":\"73528c\",\"throne.structure\":\"4d3824\",\"throne.trophy\":\"802e1a\",\"unlockAccent.building\":\"8b6914\",\"unlockAccent.dim\":\"ffffff8c\",\"unlockAccent.improvement\":\"4a7c3f\",\"unlockAccent.lens\":\"2d5a8b\",\"unlockAccent.mechanic\":\"6b3fa0\",\"unlockAccent.resource\":\"a0522d\",\"unlockAccent.unit\":\"c9a84c\",\"unlockAccent.wonder\":\"a06a3f\"}"
Button/colors/font_color = Color(0.878431, 0.819608, 0.6, 1)
Button/colors/font_hover_color = Color(1, 0.921569, 0.501961, 1)
Button/colors/font_pressed_color = Color(1, 1, 0.701961, 1)

View file

@ -42,6 +42,30 @@ const DRAW_ORDER: Array = [
"brewery", "map_table", "garden", "court",
]
## Placeholder backdrop colour per decoration layer → design token name.
## Shown only when a decoration sprite is missing. `special_*` layers and any
## unmapped layer fall back to throne.special / throne.default respectively.
const LAYER_COLOR_TOKENS: Dictionary = {
"background": "throne.structure",
"walls": "throne.structure",
"ceiling": "throne.structure",
"floor": "throne.structure",
"throne": "throne.seat",
"throne_backdrop": "throne.seat",
"forge_display": "throne.forge",
"armory": "throne.forge",
"trophy_wall": "throne.trophy",
"honor_display": "throne.trophy",
"shrine": "throne.shrine",
"ancestor_monument": "throne.shrine",
"brewery": "throne.provisions",
"gemstone_vault": "throne.provisions",
"map_table": "throne.mapTable",
"garden": "throne.garden",
"court": "throne.court",
"pedestal": "throne.pedestal",
}
## Set false when used as the main menu background (hides close button).
var show_close: bool = true
@ -182,29 +206,9 @@ func _on_decoration_unhovered() -> void:
func _layer_to_color(layer: String) -> Color:
if layer in ["background", "walls", "ceiling", "floor"]:
return Color(0.30, 0.22, 0.14)
if layer in ["throne", "throne_backdrop"]:
return Color(0.55, 0.42, 0.10)
if layer in ["forge_display", "armory"]:
return Color(0.50, 0.28, 0.08)
if layer in ["trophy_wall", "honor_display"]:
return Color(0.50, 0.18, 0.10)
if layer in ["shrine", "ancestor_monument"]:
return Color(0.20, 0.28, 0.40)
if layer in ["brewery", "gemstone_vault"]:
return Color(0.30, 0.40, 0.15)
if layer == "map_table":
return Color(0.15, 0.30, 0.35)
if layer == "garden":
return Color(0.12, 0.32, 0.18)
if layer == "court":
return Color(0.40, 0.35, 0.28)
if layer == "pedestal":
return Color(0.55, 0.48, 0.20)
if layer.begins_with("special"):
return Color(0.45, 0.32, 0.55)
return Color(0.25, 0.22, 0.18)
return ThemeAssets.color("throne.special")
return ThemeAssets.color(LAYER_COLOR_TOKENS.get(layer, "throne.default"))
func _on_close_pressed() -> void: