ui(world-map): 💄 Add distinct terrain colors and overlay styles (arena, courier routes, tile info) to world map and minimap scenes

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-06-04 21:45:40 -07:00
parent eb25722298
commit f12c2f3b4e
6 changed files with 55 additions and 36 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 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 = {
"ocean": Color(0.12, 0.24, 0.55),
"coast": Color(0.31, 0.71, 0.86),
@ -28,8 +33,6 @@ const TERRAIN_COLORS: Dictionary = {
"volcano": Color(0.55, 0.16, 0.12),
}
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 UNIT_DOT_RADIUS: float = 3.0
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
## fades naturally without a dedicated tween.
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_GROWTH: float = 8.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_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 _overlay_rect: Control = %OverlayRect
@ -62,11 +72,16 @@ func _ready() -> void:
_overlay_rect.draw.connect(_draw_overlay)
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
if border_panel != null:
var style: StyleBoxFlat = StyleBoxFlat.new()
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_corner_radius_all(2)
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_h: int = maxi(1, roundi(mm_size.y))
_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 px: int in img_w:
@ -204,7 +219,7 @@ func _draw_ping() -> void:
return
var t: float = clampf(elapsed / PING_DURATION_SEC, 0.0, 1.0)
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
var pixel_pos: Vector2 = _world_to_mini(
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
)
)
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(
Rect2(pixel_pos - Vector2(1.0, 1.0), Vector2(3.0, 3.0)), fog_col
)

View file

@ -12,11 +12,11 @@ extends RefCounted
const ARROW: String = ""
const DIM_ALPHA: float = 0.42
## Player 0 and player 1 label colors. Sourced from GameState.PLAYER_COLORS
## by index; kept as constants here for fast access and to avoid churn if
## PLAYER_COLORS changes length.
const P0_COLOR: Color = Color(0.30, 0.62, 1.00) ## calm blue
const P1_COLOR: Color = Color(1.00, 0.52, 0.28) ## warm orange
## Player 0 and player 1 label colors. Token-sourced (ThemeAssets.color isn't
## const-eval safe), populated in build(). Calm blue / warm orange map to the
## player palette tokens.
var _p0_color: Color = Color(0.30, 0.62, 1.00)
var _p1_color: Color = Color(1.00, 0.52, 0.28)
var _p1_label: Label = null
var _p2_label: Label = null
@ -38,6 +38,8 @@ func build(
) -> Label:
_p1_name = p1
_p2_name = p2
_p0_color = ThemeAssets.color("player.blue")
_p1_color = ThemeAssets.color("player.orange")
var layer: CanvasLayer = CanvasLayer.new()
layer.name = "ArenaOverlay"
@ -51,14 +53,14 @@ func build(
var title: Label = Label.new()
title.text = "%s · seed %d" % [match_id, seed_value]
title.add_theme_color_override("font_color", Color(1.0, 0.85, 0.4))
title.add_theme_color_override("font_color", ThemeAssets.color("accent.gold"))
title.add_theme_font_size_override("font_size", 13)
_p1_label = _make_player_label(p1, P0_COLOR)
_p1_label = _make_player_label(p1, _p0_color)
var vs: Label = Label.new()
vs.text = " vs "
vs.add_theme_font_size_override("font_size", 12)
_p2_label = _make_player_label(p2, P1_COLOR)
_p2_label = _make_player_label(p2, _p1_color)
var versus_row: HBoxContainer = HBoxContainer.new()
versus_row.add_child(_p1_label)
@ -68,7 +70,7 @@ func build(
_turn_label = Label.new()
_turn_label.text = "turn 1 / %d" % turn_limit
_turn_label.add_theme_color_override(
"font_color", Color(0.6, 0.85, 1.0)
"font_color", ThemeAssets.color("accent.science")
)
_turn_label.add_theme_font_size_override("font_size", 11)

View file

@ -9,11 +9,16 @@ extends Node2D
## Data source: EventBus courier diplomacy signals. No polling.
const HEX_PIXEL_SIZE: float = 64.0
const COLOR_IN_TRANSIT: Color = Color(0.65, 0.90, 0.65, 0.40)
const COLOR_DELIVERED: Color = Color(0.65, 0.90, 0.65, 0.90)
const IN_TRANSIT_ALPHA: float = 0.40
const DELIVERED_ALPHA: float = 0.90
const COURIER_ICON_RADIUS: float = 6.0
const ROUTE_LINE_WIDTH: float = 2.5
## Courier-route green, token-sourced in _ready() (ThemeAssets.color isn't
## const-eval safe). Same hue at two alphas: faded in transit, solid delivered.
var _color_in_transit: Color = Color(0.65, 0.90, 0.65, IN_TRANSIT_ALPHA)
var _color_delivered: Color = Color(0.65, 0.90, 0.65, DELIVERED_ALPHA)
## Keyed by agreement_id (int). Each value is a Dictionary with keys:
## agreement_id, from_player, to_player, from_capital, to_capital,
## courier_pos (Vector2i), eta_turn (int), delivered (bool), intercepted (bool).
@ -21,6 +26,9 @@ var _routes: Dictionary = {}
func _ready() -> void:
var base: Color = ThemeAssets.color("accent.sage")
_color_in_transit = Color(base.r, base.g, base.b, IN_TRANSIT_ALPHA)
_color_delivered = Color(base.r, base.g, base.b, DELIVERED_ALPHA)
EventBus.courier_dispatched.connect(_on_courier_dispatched)
EventBus.courier_intercepted.connect(_on_courier_intercepted)
EventBus.shared_map_delivered.connect(_on_shared_map_delivered)
@ -86,11 +94,11 @@ func _draw() -> void:
var from_px: Vector2 = _axial_to_pixel(route["from_capital"])
var to_px: Vector2 = _axial_to_pixel(route["to_capital"])
var delivered: bool = bool(route.get("delivered", false))
var line_color: Color = COLOR_DELIVERED if delivered else COLOR_IN_TRANSIT
var line_color: Color = _color_delivered if delivered else _color_in_transit
draw_line(from_px, to_px, line_color, ROUTE_LINE_WIDTH)
if not delivered:
var courier_px: Vector2 = _axial_to_pixel(route["courier_pos"])
draw_circle(courier_px, COURIER_ICON_RADIUS, COLOR_DELIVERED)
draw_circle(courier_px, COURIER_ICON_RADIUS, _color_delivered)
draw_arc(courier_px, COURIER_ICON_RADIUS + 2.0, 0.0, TAU, 16, Color(1, 1, 1, 0.7), 1.5)

View file

@ -29,16 +29,8 @@ var _fauna_ecology: GdFaunaEcology = null
func _ready() -> void:
visible = false
mouse_filter = Control.MOUSE_FILTER_IGNORE
_apply_panel_style()
func _apply_panel_style() -> void:
var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = Color(0.02, 0.02, 0.05, 0.92)
style.border_color = Color(0.4, 0.35, 0.2, 0.8)
style.set_border_width_all(1)
style.set_corner_radius_all(3)
add_theme_stylebox_override("panel", style)
# Panel background inherits the global theme's PanelContainer style
# (opaque dark panel + gold border) — no per-instance stylebox override.
func show_tile(tile: RefCounted, axial: Vector2i) -> void:
@ -203,7 +195,7 @@ func show_tile_with_placement_preview(
var reason: String = str(preview.get("reason", ""))
_worked_yield_label.text = ThemeVocabulary.lookup("placement_invalid") % reason
_worked_yield_label.visible = true
_worked_yield_label.modulate = Color(0.85, 0.3, 0.3, 1)
_worked_yield_label.modulate = ThemeAssets.color("semantic.negative")
return
_worked_yield_label.modulate = Color(1, 1, 1, 1)
@ -251,7 +243,7 @@ func _populate_ecology_species(axial: Vector2i) -> void:
pop_lbl.custom_minimum_size.x = 60
pop_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
pop_lbl.add_theme_font_size_override("font_size", 12)
pop_lbl.add_theme_color_override("font_color", Color(0.7, 0.85, 0.7))
pop_lbl.add_theme_color_override("font_color", ThemeAssets.color("accent.sage"))
row.add_child(pop_lbl)
_ecology_list.add_child(row)

View file

@ -1913,7 +1913,8 @@ func _refresh_placement_overlay() -> void:
for v: Vector2 in hex_poly:
shifted.append(origin + v)
poly.polygon = shifted
poly.color = Color(0.6, 0.85, 1.0, 0.22)
var hl: Color = ThemeAssets.color("accent.science")
poly.color = Color(hl.r, hl.g, hl.b, 0.22)
_placement_overlay_node.add_child(poly)

View file

@ -82,7 +82,8 @@ func draw_waypoint_overlay(parent_node: Node2D, waypoints: Array) -> void:
line.name = "PatrolPolyline"
line.add_to_group("patrol_overlay")
line.width = 2.0
line.default_color = Color(0.3, 0.6, 1.0, 0.75)
var patrol_hl: Color = ThemeAssets.color("accent.science")
line.default_color = Color(patrol_hl.r, patrol_hl.g, patrol_hl.b, 0.75)
for wp: Variant in waypoints:
var pixel: Vector2 = HexUtilsScript.axial_to_pixel(wp as Vector2i)
line.add_point(pixel)
@ -95,7 +96,7 @@ func draw_waypoint_overlay(parent_node: Node2D, waypoints: Array) -> void:
flag_label.add_to_group("patrol_overlay")
flag_label.text = str(i + 1)
flag_label.add_theme_font_size_override("font_size", 13)
flag_label.add_theme_color_override("font_color", Color(0.2, 0.8, 1.0))
flag_label.add_theme_color_override("font_color", ThemeAssets.color("accent.science"))
flag_label.position = pixel + Vector2(-6.0, -20.0)
parent_node.add_child(flag_label)