Use generated surfaces for tactical badges

This commit is contained in:
2026-06-20 06:02:54 +09:00
parent bffb06ec25
commit 869700a5ff
4 changed files with 67 additions and 22 deletions

View File

@@ -36,7 +36,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
- Pre-battle briefing shows the campaign chapter, chapter battle number, location, victory/defeat summary, first-clear reward preview, generated battlefield thumbnail, allied portrait/enemy unit force markers, and image-first tactical marker cards for lure lines and supply points. - Pre-battle briefing shows the campaign chapter, chapter battle number, location, victory/defeat summary, first-clear reward preview, generated battlefield thumbnail, allied portrait/enemy unit force markers, and image-first tactical marker cards for lure lines and supply points.
- Pre-battle prep commands use compact icon buttons with active selection state and Korean hover labels for chapters, shop, talks, armory, roster, formation, and save records. - Pre-battle prep commands use compact icon buttons with active selection state and Korean hover labels for chapters, shop, talks, armory, roster, formation, and save records.
- Post-move tactic and item pickers use generated command/item icons, compact two-line option rows, and hover details instead of dense single-line text. - Post-move tactic and item pickers use generated command/item icons, compact two-line option rows, and hover details instead of dense single-line text.
- Target preview badges and battlefield target markers include generated action icons for move, attack, threat, support, and item outcomes. - Target preview badges, minimap hover badges, and battlefield target markers include generated jade panel surfaces plus action icons for move, attack, threat, support, objective, terrain, and item outcomes.
- Battlefield hover info badges use generated jade panel texture plus terrain, supply, tactic, objective, and threat icons, keeping raw grid coordinates out of visible play text. - Battlefield hover info badges use generated jade panel texture plus terrain, supply, tactic, objective, and threat icons, keeping raw grid coordinates out of visible play text.
- Battle, top-toolbar, and pre-battle command buttons now prefer generated high-resolution bronze/parchment icon art under `art/ui/icons` and generated lacquer/jade/cinnabar button surfaces under `art/ui/buttons`, with procedural icon drawing and flat button styles kept only as fallbacks. - Battle, top-toolbar, and pre-battle command buttons now prefer generated high-resolution bronze/parchment icon art under `art/ui/icons` and generated lacquer/jade/cinnabar button surfaces under `art/ui/buttons`, with procedural icon drawing and flat button styles kept only as fallbacks.
- Battle maps blend generated terrain texture tiles for plains, forests, hills, wasteland, roads, water, villages, and castles over the high-resolution battlefield backdrop, with generated transparent feature overlays for road connections, water, villages, castles, and wasteland accents. - Battle maps blend generated terrain texture tiles for plains, forests, hills, wasteland, roads, water, villages, and castles over the high-resolution battlefield backdrop, with generated transparent feature overlays for road connections, water, villages, castles, and wasteland accents.

View File

@@ -5010,20 +5010,26 @@ func _draw_minimap_hover_badge() -> void:
if badge_rect.size.x <= 0.0 or badge_rect.size.y <= 0.0: if badge_rect.size.x <= 0.0 or badge_rect.size.y <= 0.0:
return return
var color := _hover_info_badge_color(str(badge.get("kind", "terrain"))) var color := _hover_info_badge_color(str(badge.get("kind", "terrain")))
var background := Color(0.035, 0.022, 0.014, 0.93) var background := Color(0.035, 0.022, 0.014, 0.74)
var font := _draw_ui_font(true) var font := _draw_ui_font(true)
draw_rect(badge_rect, background) _draw_generated_badge_panel(badge_rect, color, background, str(badge.get("panel", "hud_jade")), 0.12, 2.0)
draw_rect(badge_rect.grow(-2.0), Color(color.r, color.g, color.b, 0.12)) var icon_texture := _hover_info_badge_icon_texture(str(badge.get("icon", "")))
draw_rect(badge_rect, color, false, 2.0) if icon_texture != null:
var icon_background := Rect2(badge_rect.position + Vector2(5.0, 4.0), HOVER_INFO_BADGE_ICON_SIZE + Vector2(4.0, 4.0))
var icon_rect := Rect2(badge_rect.position + Vector2(7.0, 6.0), HOVER_INFO_BADGE_ICON_SIZE)
draw_rect(icon_background, Color(0.020, 0.018, 0.015, 0.62))
draw_rect(icon_background, Color(color.r, color.g, color.b, 0.44), false, 1.0)
draw_texture_rect(icon_texture, icon_rect, false, Color(1.0, 1.0, 1.0, 0.94))
for index in range(lines.size()): for index in range(lines.size()):
var line_color := color if index == 0 else UI_PARCHMENT_TEXT var line_color := color if index == 0 else UI_PARCHMENT_TEXT
var x_offset := 33.0 if index == 0 and icon_texture != null else 8.0
_draw_ink_text( _draw_ink_text(
font, font,
badge_rect.position + Vector2(8.0, 16.0 + float(index) * 15.0), badge_rect.position + Vector2(x_offset, 16.0 + float(index) * 15.0),
lines[index], lines[index],
HORIZONTAL_ALIGNMENT_LEFT, HORIZONTAL_ALIGNMENT_LEFT,
badge_rect.size.x - 16.0, badge_rect.size.x - x_offset - 8.0,
12, 12,
line_color line_color
) )
@@ -7176,19 +7182,24 @@ func _draw_target_preview_badge() -> void:
var badge_rect := _target_preview_badge_rect(cell, size) var badge_rect := _target_preview_badge_rect(cell, size)
var kind := str(badge.get("kind", "")) var kind := str(badge.get("kind", ""))
var color := _target_preview_badge_color(kind) var color := _target_preview_badge_color(kind)
var background := Color(0.025, 0.028, 0.034, 0.88) var background := Color(0.025, 0.028, 0.034, 0.68)
var font := _draw_ui_font(true) var font := _draw_ui_font(true)
var text := str(badge.get("text", "")) var text := str(badge.get("text", ""))
var icon_texture := _target_preview_badge_icon_texture(kind) var icon_texture := _target_preview_badge_icon_texture(kind)
var icon_space := TARGET_PREVIEW_BADGE_ICON_SIZE.x + 8.0 if icon_texture != null else 0.0 var icon_space := TARGET_PREVIEW_BADGE_ICON_SIZE.x + 8.0 if icon_texture != null else 0.0
draw_rect(badge_rect, background) _draw_generated_badge_panel(badge_rect, color, background, str(badge.get("panel", "hud_jade")), 0.08, 2.0)
draw_rect(badge_rect, color, false, 2.0)
if icon_texture != null: if icon_texture != null:
var icon_background := Rect2(
badge_rect.position + Vector2(3.0, (badge_rect.size.y - TARGET_PREVIEW_BADGE_ICON_SIZE.y) * 0.5 - 2.0),
TARGET_PREVIEW_BADGE_ICON_SIZE + Vector2(4.0, 4.0)
)
var icon_rect := Rect2( var icon_rect := Rect2(
badge_rect.position + Vector2(5.0, (badge_rect.size.y - TARGET_PREVIEW_BADGE_ICON_SIZE.y) * 0.5), badge_rect.position + Vector2(5.0, (badge_rect.size.y - TARGET_PREVIEW_BADGE_ICON_SIZE.y) * 0.5),
TARGET_PREVIEW_BADGE_ICON_SIZE TARGET_PREVIEW_BADGE_ICON_SIZE
) )
draw_rect(icon_background, Color(0.020, 0.018, 0.015, 0.58))
draw_rect(icon_background, Color(color.r, color.g, color.b, 0.40), false, 1.0)
draw_texture_rect(icon_texture, icon_rect, false, Color(1.0, 1.0, 1.0, 0.96)) draw_texture_rect(icon_texture, icon_rect, false, Color(1.0, 1.0, 1.0, 0.96))
_draw_ink_text( _draw_ink_text(
font, font,
@@ -7201,6 +7212,18 @@ func _draw_target_preview_badge() -> void:
) )
func _draw_generated_badge_panel(rect: Rect2, color: Color, background: Color, texture_key: String = "hud_jade", accent_alpha: float = 0.10, border_width: float = 2.0) -> void:
var normalized_key := texture_key.strip_edges().to_lower()
if normalized_key.is_empty():
normalized_key = "hud_jade"
var panel_texture := _load_art_texture(PANEL_TEXTURE_PATH_TEMPLATE % normalized_key)
if panel_texture != null:
draw_texture_rect(panel_texture, rect, false, Color(0.70, 0.72, 0.66, 0.90))
draw_rect(rect, background)
draw_rect(rect.grow(-2.0), Color(color.r, color.g, color.b, accent_alpha))
draw_rect(rect, color, false, border_width)
func _draw_hover_info_badge() -> void: func _draw_hover_info_badge() -> void:
var badge := _hover_info_badge() var badge := _hover_info_badge()
if badge.is_empty(): if badge.is_empty():
@@ -7213,13 +7236,8 @@ func _draw_hover_info_badge() -> void:
var color := _hover_info_badge_color(str(badge.get("kind", "terrain"))) var color := _hover_info_badge_color(str(badge.get("kind", "terrain")))
var background := Color(0.035, 0.022, 0.014, 0.72) var background := Color(0.035, 0.022, 0.014, 0.72)
var font := _draw_ui_font(true) var font := _draw_ui_font(true)
var panel_texture := _load_art_texture(PANEL_TEXTURE_PATH_TEMPLATE % "hud_jade")
if panel_texture != null:
draw_texture_rect(panel_texture, badge_rect, false, Color(0.70, 0.72, 0.66, 0.92))
draw_rect(badge_rect, background) _draw_generated_badge_panel(badge_rect, color, background, str(badge.get("panel", "hud_jade")), 0.10, 2.0)
draw_rect(badge_rect.grow(-2.0), Color(color.r, color.g, color.b, 0.10))
draw_rect(badge_rect, color, false, 2.0)
var icon_texture := _hover_info_badge_icon_texture(str(badge.get("icon", ""))) var icon_texture := _hover_info_badge_icon_texture(str(badge.get("icon", "")))
if icon_texture != null: if icon_texture != null:
var icon_background := Rect2(badge_rect.position + Vector2(5.0, 4.0), HOVER_INFO_BADGE_ICON_SIZE + Vector2(4.0, 4.0)) var icon_background := Rect2(badge_rect.position + Vector2(5.0, 4.0), HOVER_INFO_BADGE_ICON_SIZE + Vector2(4.0, 4.0))
@@ -7268,6 +7286,7 @@ func _hover_info_badge() -> Dictionary:
"cell": hover_cell, "cell": hover_cell,
"kind": _hover_info_badge_kind(hover_cell), "kind": _hover_info_badge_kind(hover_cell),
"icon": _hover_info_badge_icon_kind(hover_cell), "icon": _hover_info_badge_icon_kind(hover_cell),
"panel": "hud_jade",
"lines": lines "lines": lines
} }
@@ -7700,7 +7719,8 @@ func _make_target_preview_badge_at(text: String, kind: String, cell: Vector2i) -
"text": text, "text": text,
"kind": kind, "kind": kind,
"cell": cell, "cell": cell,
"icon": _target_preview_badge_icon_kind(kind) "icon": _target_preview_badge_icon_kind(kind),
"panel": "hud_jade"
} }
@@ -7972,6 +7992,8 @@ func _minimap_hover_badge(screen_position: Vector2) -> Dictionary:
return { return {
"cell": cell, "cell": cell,
"kind": _hover_info_badge_kind(cell), "kind": _hover_info_badge_kind(cell),
"icon": _hover_info_badge_icon_kind(cell),
"panel": "hud_jade",
"lines": lines, "lines": lines,
"rect": _minimap_hover_badge_rect(screen_position, lines) "rect": _minimap_hover_badge_rect(screen_position, lines)
} }
@@ -7984,17 +8006,26 @@ func _minimap_hover_badge_lines(cell: Vector2i) -> Array[String]:
var terrain_name := _terrain_display_name(str(summary.get("terrain", ""))) var terrain_name := _terrain_display_name(str(summary.get("terrain", "")))
var lines: Array[String] = [] var lines: Array[String] = []
var unit: Dictionary = summary.get("unit", {}) var unit: Dictionary = summary.get("unit", {})
var objective_label := state.get_objective_cell_label(cell)
var event_label := state.get_event_marker_label(cell)
var objective_used_as_title := false
var event_used_as_title := false
if not unit.is_empty(): if not unit.is_empty():
lines.append("%s · %s" % [_short_name(str(unit.get("name", "부대"))), _unit_team_text(str(unit.get("team", "")))]) lines.append("%s · %s" % [_short_name(str(unit.get("name", "부대"))), _unit_team_text(str(unit.get("team", "")))])
lines.append("%s · %s" % [_unit_class_display_name(unit), terrain_name]) lines.append("%s · %s" % [_unit_class_display_name(unit), terrain_name])
else: else:
lines.append("전장도 %s · %s" % [_format_cell_label(cell), terrain_name]) if not objective_label.is_empty():
lines.append("목표 %s" % objective_label)
objective_used_as_title = true
elif not event_label.is_empty():
lines.append("표식 %s" % event_label)
event_used_as_title = true
else:
lines.append("전장도 %s" % terrain_name)
var markers: Array[String] = [] var markers: Array[String] = []
var objective_label := state.get_objective_cell_label(cell) if not objective_label.is_empty() and not objective_used_as_title:
if not objective_label.is_empty():
markers.append("목표 %s" % objective_label) markers.append("목표 %s" % objective_label)
var event_label := state.get_event_marker_label(cell) if not event_label.is_empty() and not event_used_as_title:
if not event_label.is_empty():
markers.append("표식 %s" % event_label) markers.append("표식 %s" % event_label)
if show_threat_overlay: if show_threat_overlay:
var threat_names := state.get_threatening_unit_names(cell, BattleState.TEAM_ENEMY) var threat_names := state.get_threatening_unit_names(cell, BattleState.TEAM_ENEMY)

View File

@@ -275,6 +275,12 @@ func _check_minimap_navigation_contract(failures: Array[String]) -> void:
var first_hover_text := "\n".join(first_hover_lines) var first_hover_text := "\n".join(first_hover_lines)
if not first_hover_text.contains("클릭/드래그"): if not first_hover_text.contains("클릭/드래그"):
failures.append("minimap hover should explain click-drag navigation: %s" % first_hover_text) failures.append("minimap hover should explain click-drag navigation: %s" % first_hover_text)
if first_hover_text.contains("1,1"):
failures.append("minimap hover should not expose raw grid coordinates in visible text: %s" % first_hover_text)
if str(first_hover_badge.get("icon", "")).strip_edges().is_empty():
failures.append("minimap hover should carry generated icon metadata: %s" % str(first_hover_badge))
if str(first_hover_badge.get("panel", "")) != "hud_jade":
failures.append("minimap hover should use generated jade panel metadata: %s" % str(first_hover_badge))
var first_hover_rect: Rect2 = first_hover_badge.get("rect", Rect2()) var first_hover_rect: Rect2 = first_hover_badge.get("rect", Rect2())
if first_hover_rect.size.x <= 0.0 or first_hover_rect.size.y <= 0.0 or not view_rect.encloses(first_hover_rect): if first_hover_rect.size.x <= 0.0 or first_hover_rect.size.y <= 0.0 or not view_rect.encloses(first_hover_rect):
failures.append("minimap hover badge should stay inside the visible map view: %s within %s" % [str(first_hover_rect), str(view_rect)]) failures.append("minimap hover badge should stay inside the visible map view: %s within %s" % [str(first_hover_rect), str(view_rect)])

View File

@@ -4262,6 +4262,8 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
failures.append("reachable empty tile should show MOVE badge: %s" % str(move_badge)) failures.append("reachable empty tile should show MOVE badge: %s" % str(move_badge))
if str(move_badge.get("icon", "")) != "move": if str(move_badge.get("icon", "")) != "move":
failures.append("MOVE badge should carry a generated icon hint: %s" % str(move_badge)) failures.append("MOVE badge should carry a generated icon hint: %s" % str(move_badge))
if str(move_badge.get("panel", "")) != "hud_jade":
failures.append("MOVE badge should use generated jade panel metadata: %s" % str(move_badge))
if not scene._hover_info_badge().is_empty(): if not scene._hover_info_badge().is_empty():
failures.append("hover info badge should yield to active move intent badge") failures.append("hover info badge should yield to active move intent badge")
@@ -4271,6 +4273,8 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
failures.append("friendly selectable unit should show SELECT badge: %s" % str(select_badge)) failures.append("friendly selectable unit should show SELECT badge: %s" % str(select_badge))
if str(select_badge.get("icon", "")).strip_edges().is_empty(): if str(select_badge.get("icon", "")).strip_edges().is_empty():
failures.append("SELECT badge should carry a generated icon hint: %s" % str(select_badge)) failures.append("SELECT badge should carry a generated icon hint: %s" % str(select_badge))
if str(select_badge.get("panel", "")) != "hud_jade":
failures.append("SELECT badge should use generated jade panel metadata: %s" % str(select_badge))
scene.hover_cell = Vector2i(1, 5) scene.hover_cell = Vector2i(1, 5)
var attack_badge := scene._target_preview_badge() var attack_badge := scene._target_preview_badge()
@@ -4282,6 +4286,8 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
failures.append("counterable attack badge should use a counter danger color kind: %s" % str(attack_badge)) failures.append("counterable attack badge should use a counter danger color kind: %s" % str(attack_badge))
if str(attack_badge.get("icon", "")) != "threat": if str(attack_badge.get("icon", "")) != "threat":
failures.append("counterable attack badge should carry a threat icon hint: %s" % str(attack_badge)) failures.append("counterable attack badge should carry a threat icon hint: %s" % str(attack_badge))
if str(attack_badge.get("panel", "")) != "hud_jade":
failures.append("counterable attack badge should use generated jade panel metadata: %s" % str(attack_badge))
var cao_cao: Dictionary = scene.state.get_unit("cao_cao") var cao_cao: Dictionary = scene.state.get_unit("cao_cao")
cao_cao["moved"] = true cao_cao["moved"] = true
@@ -4322,6 +4328,8 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
failures.append("enemy hover info badge should expose enemy identity: %s" % str(enemy_hover_badge)) failures.append("enemy hover info badge should expose enemy identity: %s" % str(enemy_hover_badge))
if str(enemy_hover_badge.get("icon", "")) != "threat": if str(enemy_hover_badge.get("icon", "")) != "threat":
failures.append("enemy hover info badge should use generated threat icon metadata: %s" % str(enemy_hover_badge)) failures.append("enemy hover info badge should use generated threat icon metadata: %s" % str(enemy_hover_badge))
if str(enemy_hover_badge.get("panel", "")) != "hud_jade":
failures.append("enemy hover info badge should use generated jade panel metadata: %s" % str(enemy_hover_badge))
scene.hover_cell = Vector2i(4, 2) scene.hover_cell = Vector2i(4, 2)
var event_hover_badge := scene._hover_info_badge() var event_hover_badge := scene._hover_info_badge()
if str(event_hover_badge.get("kind", "")) != "event" or not str(event_hover_badge.get("lines", [])).contains("표식 북숲 보급고"): if str(event_hover_badge.get("kind", "")) != "event" or not str(event_hover_badge.get("lines", [])).contains("표식 북숲 보급고"):