refactor(ui): tokenize world-map HUD + minimap colors (p2-74 cluster 3b)

Route inline Color() in the world-map HUD banners and minimap onto ThemeAssets
design tokens. Visual-only (Rail 3), no logic change:

- world_map_hud: patrol banner box -> background.panel/accent.science + label
  accent.science; rally picker box -> background.panel/accent.sage + title
  accent.science; notification toast box -> background.panel/accent.gold +
  label text.primary
- minimap: const FOG_COLOR/UNEXPLORED_COLOR/PING_RING_COLOR -> _ready()-populated
  vars from fog.explored/fog.unexplored/accent.ping; border stylebox border ->
  border.panel; backdrop fill -> background.deepest

Minimap TERRAIN_COLORS biome palette stays hardcoded with an explicit comment:
it is game-content color (no UI token applies) and a deliberately distinct muted
minimap rendering, not a terrain.json mirror (the arrays diverge). Re-sourcing it
is a Rail-2 data follow-up, out of scope for this visual-only token pass.

DataLoader RGB conversion, env-var viewport color, transparent structural fills,
and the neutral gray default left as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
autocommit 2026-06-04 21:36:41 -07:00
parent d721d58d04
commit 3c07f0680c
2 changed files with 32 additions and 17 deletions

View file

@ -9,7 +9,12 @@ const CityScript: GDScript = preload("res://engine/src/entities/city.gd")
const UnitScript: GDScript = preload("res://engine/src/entities/unit.gd") const UnitScript: GDScript = preload("res://engine/src/entities/unit.gd")
const PlayerScript: GDScript = preload("res://engine/src/entities/player.gd") const PlayerScript: GDScript = preload("res://engine/src/entities/player.gd")
## Terrain colors indexed by terrain_id — keep in sync with terrain.json ## Minimap terrain palette indexed by terrain_id. These are game-content
## colors (no UI design token applies) and are a deliberately distinct,
## muted minimap rendering — NOT a mirror of terrain.json's `color` arrays
## (those diverge by 10-70/channel; only `coast` coincides). p2-74 leaves
## this hardcoded; routing it through a data source is a Rail-2 follow-up
## (logic change, out of scope for the visual-only token pass).
const TERRAIN_COLORS: Dictionary = { const TERRAIN_COLORS: Dictionary = {
"ocean": Color(0.12, 0.24, 0.55), "ocean": Color(0.12, 0.24, 0.55),
"coast": Color(0.31, 0.71, 0.86), "coast": Color(0.31, 0.71, 0.86),
@ -28,8 +33,6 @@ const TERRAIN_COLORS: Dictionary = {
"volcano": Color(0.55, 0.16, 0.12), "volcano": Color(0.55, 0.16, 0.12),
} }
const DEFAULT_TERRAIN_COLOR: Color = Color(0.3, 0.3, 0.3) const DEFAULT_TERRAIN_COLOR: Color = Color(0.3, 0.3, 0.3)
const FOG_COLOR: Color = Color(0.0, 0.0, 0.0, 0.70)
const UNEXPLORED_COLOR: Color = Color(0.0, 0.0, 0.0, 0.90)
const CITY_DOT_RADIUS: float = 4.0 const CITY_DOT_RADIUS: float = 4.0
const UNIT_DOT_RADIUS: float = 3.0 const UNIT_DOT_RADIUS: float = 3.0
const OWNER_TINT_ALPHA: float = 0.38 const OWNER_TINT_ALPHA: float = 0.38
@ -39,7 +42,6 @@ const OWNER_TILE_SIZE: Vector2 = Vector2(3.0, 3.0)
## `_ping_remaining` ticks down each overlay redraw frame so the pulse ## `_ping_remaining` ticks down each overlay redraw frame so the pulse
## fades naturally without a dedicated tween. ## fades naturally without a dedicated tween.
const PING_DURATION_SEC: float = 1.6 const PING_DURATION_SEC: float = 1.6
const PING_RING_COLOR: Color = Color(1.0, 0.92, 0.45, 1.0)
const PING_RING_BASE_RADIUS: float = 4.0 const PING_RING_BASE_RADIUS: float = 4.0
const PING_RING_GROWTH: float = 8.0 const PING_RING_GROWTH: float = 8.0
const PING_RING_WIDTH: float = 2.0 const PING_RING_WIDTH: float = 2.0
@ -53,6 +55,14 @@ var _camera: Camera2D = null
var _ping_axial: Vector2i = Vector2i(-9999, -9999) var _ping_axial: Vector2i = Vector2i(-9999, -9999)
var _ping_started_at: float = -1.0 var _ping_started_at: float = -1.0
## Token-sourced colors, populated in _ready() (ThemeAssets.color isn't
## const-eval safe). fog/ping map to dedicated design tokens; the minimap
## backdrop uses the deepest background token.
var _fog_color: Color = Color(0.0, 0.0, 0.0, 0.70)
var _unexplored_color: Color = Color(0.0, 0.0, 0.0, 0.90)
var _ping_ring_color: Color = Color(1.0, 0.92, 0.45, 1.0)
var _backdrop_color: Color = Color(0.04, 0.04, 0.06)
@onready var _map_rect: TextureRect = %MapRect @onready var _map_rect: TextureRect = %MapRect
@onready var _overlay_rect: Control = %OverlayRect @onready var _overlay_rect: Control = %OverlayRect
@ -62,11 +72,16 @@ func _ready() -> void:
_overlay_rect.draw.connect(_draw_overlay) _overlay_rect.draw.connect(_draw_overlay)
gui_input.connect(_on_gui_input) gui_input.connect(_on_gui_input)
_fog_color = ThemeAssets.color("fog.explored")
_unexplored_color = ThemeAssets.color("fog.unexplored")
_ping_ring_color = ThemeAssets.color("accent.ping")
_backdrop_color = ThemeAssets.color("background.deepest")
var border_panel: Panel = get_node_or_null("Border") as Panel var border_panel: Panel = get_node_or_null("Border") as Panel
if border_panel != null: if border_panel != null:
var style: StyleBoxFlat = StyleBoxFlat.new() var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = Color(0.0, 0.0, 0.0, 0.0) style.bg_color = Color(0.0, 0.0, 0.0, 0.0)
style.border_color = Color(0.75, 0.65, 0.35, 0.95) style.border_color = ThemeAssets.color("border.panel")
style.set_border_width_all(2) style.set_border_width_all(2)
style.set_corner_radius_all(2) style.set_corner_radius_all(2)
border_panel.add_theme_stylebox_override("panel", style) border_panel.add_theme_stylebox_override("panel", style)
@ -141,7 +156,7 @@ func _rebuild_terrain() -> void:
var img_w: int = maxi(1, roundi(mm_size.x)) var img_w: int = maxi(1, roundi(mm_size.x))
var img_h: int = maxi(1, roundi(mm_size.y)) var img_h: int = maxi(1, roundi(mm_size.y))
_cached_image = Image.create(img_w, img_h, false, Image.FORMAT_RGB8) _cached_image = Image.create(img_w, img_h, false, Image.FORMAT_RGB8)
_cached_image.fill(Color(0.04, 0.04, 0.06)) _cached_image.fill(_backdrop_color)
for py: int in img_h: for py: int in img_h:
for px: int in img_w: for px: int in img_w:
@ -204,7 +219,7 @@ func _draw_ping() -> void:
return return
var t: float = clampf(elapsed / PING_DURATION_SEC, 0.0, 1.0) var t: float = clampf(elapsed / PING_DURATION_SEC, 0.0, 1.0)
var radius: float = PING_RING_BASE_RADIUS + PING_RING_GROWTH * t var radius: float = PING_RING_BASE_RADIUS + PING_RING_GROWTH * t
var color: Color = PING_RING_COLOR var color: Color = _ping_ring_color
color.a = 1.0 - t color.a = 1.0 - t
var pixel_pos: Vector2 = _world_to_mini( var pixel_pos: Vector2 = _world_to_mini(
HexUtilsScript.axial_to_pixel(_ping_axial) + Vector2( HexUtilsScript.axial_to_pixel(_ping_axial) + Vector2(
@ -224,7 +239,7 @@ func _draw_fog(game_map: RefCounted, player_index: int) -> void:
HexUtilsScript.HEX_WIDTH * 0.5, HexUtilsScript.HEX_HEIGHT * 0.5 HexUtilsScript.HEX_WIDTH * 0.5, HexUtilsScript.HEX_HEIGHT * 0.5
) )
) )
var fog_col: Color = FOG_COLOR if vis == 1 else UNEXPLORED_COLOR var fog_col: Color = _fog_color if vis == 1 else _unexplored_color
_overlay_rect.draw_rect( _overlay_rect.draw_rect(
Rect2(pixel_pos - Vector2(1.0, 1.0), Vector2(3.0, 3.0)), fog_col Rect2(pixel_pos - Vector2(1.0, 1.0), Vector2(3.0, 3.0)), fog_col
) )

View file

@ -213,8 +213,8 @@ func _build_patrol_banner() -> void:
_patrol_banner.offset_bottom = 162.0 _patrol_banner.offset_bottom = 162.0
_patrol_banner.visible = false _patrol_banner.visible = false
var style: StyleBoxFlat = StyleBoxFlat.new() var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = Color(0.05, 0.08, 0.14, 0.94) style.bg_color = ThemeAssets.color("background.panel")
style.border_color = Color(0.3, 0.55, 0.9, 0.9) style.border_color = ThemeAssets.color("accent.science")
style.set_border_width_all(2) style.set_border_width_all(2)
style.set_corner_radius_all(4) style.set_corner_radius_all(4)
style.content_margin_left = 12.0 style.content_margin_left = 12.0
@ -226,7 +226,7 @@ func _build_patrol_banner() -> void:
_patrol_banner_label = Label.new() _patrol_banner_label = Label.new()
_patrol_banner_label.name = "PatrolBannerLabel" _patrol_banner_label.name = "PatrolBannerLabel"
_patrol_banner_label.add_theme_font_size_override("font_size", 15) _patrol_banner_label.add_theme_font_size_override("font_size", 15)
_patrol_banner_label.add_theme_color_override("font_color", Color(0.75, 0.88, 1.0)) _patrol_banner_label.add_theme_color_override("font_color", ThemeAssets.color("accent.science"))
_patrol_banner_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _patrol_banner_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_patrol_banner_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _patrol_banner_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
_patrol_banner.add_child(_patrol_banner_label) _patrol_banner.add_child(_patrol_banner_label)
@ -245,8 +245,8 @@ func _build_rally_command_picker() -> void:
_rally_command_picker.offset_bottom = 48.0 _rally_command_picker.offset_bottom = 48.0
_rally_command_picker.visible = false _rally_command_picker.visible = false
var style: StyleBoxFlat = StyleBoxFlat.new() var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = Color(0.05, 0.08, 0.14, 0.96) style.bg_color = ThemeAssets.color("background.panel")
style.border_color = Color(0.55, 0.75, 0.35, 0.9) style.border_color = ThemeAssets.color("accent.sage")
style.set_border_width_all(2) style.set_border_width_all(2)
style.set_corner_radius_all(4) style.set_corner_radius_all(4)
style.content_margin_left = 14.0 style.content_margin_left = 14.0
@ -263,7 +263,7 @@ func _build_rally_command_picker() -> void:
var title: Label = Label.new() var title: Label = Label.new()
title.text = ThemeVocabulary.lookup("rally_command_pick_title") title.text = ThemeVocabulary.lookup("rally_command_pick_title")
title.add_theme_font_size_override("font_size", 13) title.add_theme_font_size_override("font_size", 13)
title.add_theme_color_override("font_color", Color(0.75, 0.88, 1.0)) title.add_theme_color_override("font_color", ThemeAssets.color("accent.science"))
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
vbox.add_child(title) vbox.add_child(title)
@ -360,8 +360,8 @@ func show_notification(text: String) -> void:
panel.mouse_filter = Control.MOUSE_FILTER_IGNORE panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
var style: StyleBoxFlat = StyleBoxFlat.new() var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = Color(0.08, 0.06, 0.04, 0.92) style.bg_color = ThemeAssets.color("background.panel")
style.border_color = Color(0.85, 0.7, 0.3, 0.85) style.border_color = ThemeAssets.color("accent.gold")
style.set_border_width_all(2) style.set_border_width_all(2)
style.set_corner_radius_all(4) style.set_corner_radius_all(4)
style.content_margin_left = 12.0 style.content_margin_left = 12.0
@ -373,7 +373,7 @@ func show_notification(text: String) -> void:
var label: Label = Label.new() var label: Label = Label.new()
label.text = text label.text = text
label.add_theme_font_size_override("font_size", 14) label.add_theme_font_size_override("font_size", 14)
label.add_theme_color_override("font_color", Color(1.0, 0.94, 0.78)) label.add_theme_color_override("font_color", ThemeAssets.color("text.primary"))
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
panel.add_child(label) panel.add_child(label)