feat(@projects/@magic-civilization): lair overlay sprite-capable + larger legible fallback

- lair_overlay_renderer now loads sprites/lairs/<type_id>.png (via
  DrawHelpers.scaled_sprite_size, POI fraction 0.45) and draws it in place of
  the diamond marker when the asset is present.
- Enlarge + keep-labeled the diamond fallback (radius 18→26) so a lair is at
  least legible until the art lands.
- Sprite path is a no-op until the standin pipeline emits sprites/lairs/*
  (tracked as a follow-up objective).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-18 19:47:38 -05:00
parent 5d5fda4127
commit 7df76174cb

View file

@ -4,9 +4,17 @@ extends Node2D
## Subscribes to lair-related EventBus signals and redraws on state changes.
const HexUtilsScript: GDScript = preload("res://engine/src/map/hex_utils.gd")
const DrawHelpers: GDScript = preload("res://engine/src/rendering/unit_renderer_draw.gd")
## Lair marker geometry
const MARKER_RADIUS: float = 18.0
## Lair sprite path convention (sprites/lairs/<type_id>.png). When the art is
## present it replaces the diamond marker; generated by the standin pipeline.
const LAIR_SPRITE_FORMAT: String = "sprites/lairs/%s.png"
## Sprite size as a fraction of hex width — POI scale, smaller than a unit (0.6)
## but clearly readable. Mirrors the unit/city sprite-fraction convention.
const LAIR_SPRITE_TILE_FRACTION: float = 0.45
## Lair marker geometry (fallback when no sprite asset is present)
const MARKER_RADIUS: float = 26.0
const MARKER_OUTLINE_WIDTH: float = 2.0
const MARKER_OUTLINE_COLOR: Color = Color(0.1, 0.1, 0.1, 0.85)
@ -26,6 +34,8 @@ const EXCLUDED_TYPES: Array[String] = ["village", "ruin"]
## Cached lair display data: position (Vector2i) -> { "type_id": String, "tier": int }
var _lairs: Dictionary = {}
## Cached lair textures by type_id (null when no sprite asset exists yet).
var _sprite_cache: Dictionary = {}
func _ready() -> void:
@ -80,10 +90,14 @@ func _draw() -> void:
var tier: int = data.get("tier", 1)
var type_id: String = data.get("type_id", "")
var pixel: Vector2 = HexUtilsScript.axial_to_pixel(pos) + HexUtilsScript.hex_center
var color: Color = _tier_color(tier)
# Draw diamond marker (rotated square)
_draw_diamond(pixel, MARKER_RADIUS, color)
# Real lair sprite when present; diamond marker as missing-asset fallback.
var sprite: Texture2D = _lair_sprite(type_id)
if sprite != null:
var draw_size: Vector2 = DrawHelpers.scaled_sprite_size(sprite, LAIR_SPRITE_TILE_FRACTION)
draw_texture_rect(sprite, Rect2(pixel - draw_size * 0.5, draw_size), false)
else:
_draw_diamond(pixel, MARKER_RADIUS, _tier_color(tier))
# Draw tier label above marker
var tier_name: String = _get_tier_name(tier)
@ -92,6 +106,13 @@ func _draw() -> void:
_draw_tier_label(pixel, tier_name)
func _lair_sprite(type_id: String) -> Texture2D:
## Load + cache the lair sprite by type_id; null until the art asset exists.
if not _sprite_cache.has(type_id):
_sprite_cache[type_id] = ThemeAssets.load_sprite(LAIR_SPRITE_FORMAT % type_id, false)
return _sprite_cache[type_id] as Texture2D
func _draw_diamond(center: Vector2, radius: float, color: Color) -> void:
## Draw a diamond (rotated square) as the lair marker.
var points: PackedVector2Array = PackedVector2Array([