Add visual crests to battle unit list

This commit is contained in:
2026-06-20 06:50:11 +09:00
parent 0337e0cc62
commit b84cadc904
2 changed files with 138 additions and 7 deletions

View File

@@ -183,7 +183,10 @@ const PREP_MENU_BUTTON_SIZE := Vector2(48, 38)
const BATTLE_UNIT_LIST_PANEL_SIZE := Vector2(420, 360) const BATTLE_UNIT_LIST_PANEL_SIZE := Vector2(420, 360)
const BATTLE_UNIT_LIST_ROW_SIZE := Vector2(372, 58) const BATTLE_UNIT_LIST_ROW_SIZE := Vector2(372, 58)
const BATTLE_UNIT_LIST_ICON_SIZE := Vector2(46, 46) const BATTLE_UNIT_LIST_ICON_SIZE := Vector2(46, 46)
const BATTLE_UNIT_LIST_CLASS_CREST_PANEL_SIZE := Vector2(22, 22)
const BATTLE_UNIT_LIST_CLASS_CREST_ICON_SIZE := Vector2(17, 17)
const BATTLE_UNIT_LIST_BADGE_SIZE := Vector2(72, 22) const BATTLE_UNIT_LIST_BADGE_SIZE := Vector2(72, 22)
const BATTLE_UNIT_LIST_BADGE_ICON_SIZE := Vector2(15, 15)
const SETTINGS_PANEL_SIZE := Vector2(420, 330) const SETTINGS_PANEL_SIZE := Vector2(420, 330)
const LOCAL_COMMAND_BUTTON_SIZE := Vector2(92, 38) const LOCAL_COMMAND_BUTTON_SIZE := Vector2(92, 38)
const LOCAL_COMMAND_CANCEL_BUTTON_SIZE := Vector2(196, 30) const LOCAL_COMMAND_CANCEL_BUTTON_SIZE := Vector2(196, 30)
@@ -2590,9 +2593,26 @@ func _create_hud() -> void:
battle_unit_header_row.add_theme_constant_override("separation", 8) battle_unit_header_row.add_theme_constant_override("separation", 8)
battle_unit_column.add_child(battle_unit_header_row) battle_unit_column.add_child(battle_unit_header_row)
var battle_unit_header_tooltip := "군세\n전장에 있는 아군과 적군의 상태를 봅니다."
var battle_unit_header_seal := _make_seal_tile("", 26)
battle_unit_header_seal.name = "BattleUnitListHeaderSeal"
battle_unit_header_seal.tooltip_text = battle_unit_header_tooltip
battle_unit_header_row.add_child(battle_unit_header_seal)
var battle_unit_header_icon := TextureRect.new()
battle_unit_header_icon.name = "BattleUnitListHeaderIcon"
battle_unit_header_icon.custom_minimum_size = Vector2(24, 24)
battle_unit_header_icon.size_flags_vertical = Control.SIZE_SHRINK_CENTER
battle_unit_header_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
battle_unit_header_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
battle_unit_header_icon.texture = _make_toolbar_icon("unit_list")
battle_unit_header_icon.tooltip_text = battle_unit_header_tooltip
battle_unit_header_row.add_child(battle_unit_header_icon)
battle_unit_list_title_label = Label.new() battle_unit_list_title_label = Label.new()
battle_unit_list_title_label.text = "군세" battle_unit_list_title_label.text = "군세"
battle_unit_list_title_label.custom_minimum_size = Vector2(232, 28) battle_unit_list_title_label.tooltip_text = battle_unit_header_tooltip
battle_unit_list_title_label.custom_minimum_size = Vector2(174, 28)
battle_unit_list_title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL battle_unit_list_title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_apply_label_style(battle_unit_list_title_label, UI_OLD_BRONZE, 16) _apply_label_style(battle_unit_list_title_label, UI_OLD_BRONZE, 16)
battle_unit_header_row.add_child(battle_unit_list_title_label) battle_unit_header_row.add_child(battle_unit_list_title_label)
@@ -12403,11 +12423,17 @@ func _make_battle_unit_list_row(unit: Dictionary) -> HBoxContainer:
badge_row.tooltip_text = tooltip_text badge_row.tooltip_text = tooltip_text
column.add_child(badge_row) column.add_child(badge_row)
for badge_text in _format_battle_unit_list_row_badges(unit): var badge_texts := _format_battle_unit_list_row_badges(unit)
for badge_index in range(badge_texts.size()):
var badge_text = badge_texts[badge_index]
var text := str(badge_text).strip_edges() var text := str(badge_text).strip_edges()
if text.is_empty(): if text.is_empty():
continue continue
badge_row.add_child(_make_battle_unit_list_badge(text, tooltip_text)) badge_row.add_child(_make_battle_unit_list_badge(
text,
tooltip_text,
_battle_unit_list_badge_icon(unit, badge_index, text)
))
return row return row
@@ -12443,6 +12469,30 @@ func _make_battle_unit_list_icon(unit: Dictionary, tooltip_text: String) -> Pane
initials_label.tooltip_text = tooltip_text initials_label.tooltip_text = tooltip_text
_apply_label_style(initials_label, UI_PARCHMENT_TEXT, 14) _apply_label_style(initials_label, UI_PARCHMENT_TEXT, 14)
stack.add_child(initials_label) stack.add_child(initials_label)
var class_icon := _load_unit_class_icon_texture(unit)
if class_icon != null:
var crest_panel := PanelContainer.new()
crest_panel.name = "BattleUnitListClassCrest"
crest_panel.position = Vector2(
BATTLE_UNIT_LIST_ICON_SIZE.x - BATTLE_UNIT_LIST_CLASS_CREST_PANEL_SIZE.x + 1.0,
BATTLE_UNIT_LIST_ICON_SIZE.y - BATTLE_UNIT_LIST_CLASS_CREST_PANEL_SIZE.y + 1.0
)
crest_panel.size = BATTLE_UNIT_LIST_CLASS_CREST_PANEL_SIZE
crest_panel.custom_minimum_size = BATTLE_UNIT_LIST_CLASS_CREST_PANEL_SIZE
crest_panel.tooltip_text = "%s\n%s" % [_unit_class_display_name(unit), tooltip_text]
_apply_panel_style(crest_panel, "caption")
var crest_icon := TextureRect.new()
crest_icon.name = "BattleUnitListClassIcon"
crest_icon.custom_minimum_size = BATTLE_UNIT_LIST_CLASS_CREST_ICON_SIZE
crest_icon.size = BATTLE_UNIT_LIST_CLASS_CREST_ICON_SIZE
crest_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
crest_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
crest_icon.texture = class_icon
crest_icon.tooltip_text = crest_panel.tooltip_text
crest_panel.add_child(crest_icon)
stack.add_child(crest_panel)
return panel return panel
@@ -12462,23 +12512,81 @@ func _format_battle_unit_list_row_badges(unit: Dictionary) -> Array:
] ]
func _make_battle_unit_list_badge(text: String, tooltip_text: String) -> PanelContainer: func _make_battle_unit_list_badge(text: String, tooltip_text: String, icon_texture: Texture2D = null) -> PanelContainer:
var panel := PanelContainer.new() var panel := PanelContainer.new()
panel.custom_minimum_size = BATTLE_UNIT_LIST_BADGE_SIZE panel.custom_minimum_size = BATTLE_UNIT_LIST_BADGE_SIZE
panel.tooltip_text = tooltip_text panel.tooltip_text = tooltip_text
_apply_panel_style(panel, "caption") _apply_panel_style(panel, "caption")
if icon_texture != null:
panel.set_meta("battle_unit_list_badge_icon", true)
var row := HBoxContainer.new()
row.custom_minimum_size = Vector2(BATTLE_UNIT_LIST_BADGE_SIZE.x - 8.0, BATTLE_UNIT_LIST_BADGE_SIZE.y - 4.0)
row.add_theme_constant_override("separation", 3)
row.tooltip_text = tooltip_text
panel.add_child(row)
if icon_texture != null:
var icon := TextureRect.new()
icon.name = "BattleUnitListBadgeIcon"
icon.custom_minimum_size = BATTLE_UNIT_LIST_BADGE_ICON_SIZE
icon.size_flags_vertical = Control.SIZE_SHRINK_CENTER
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.texture = icon_texture
icon.tooltip_text = tooltip_text
row.add_child(icon)
var label := Label.new() var label := Label.new()
label.set_anchors_preset(Control.PRESET_FULL_RECT) label.custom_minimum_size = Vector2(44, BATTLE_UNIT_LIST_BADGE_SIZE.y - 4.0)
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT if icon_texture != null else HORIZONTAL_ALIGNMENT_CENTER
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
label.text = text label.text = text
label.tooltip_text = tooltip_text label.tooltip_text = tooltip_text
_apply_label_style(label, UI_PARCHMENT_TEXT, 10) _apply_label_style(label, UI_PARCHMENT_TEXT, 10)
panel.add_child(label) row.add_child(label)
return panel return panel
func _battle_unit_list_badge_icon(unit: Dictionary, badge_index: int, badge_text: String) -> Texture2D:
match badge_index:
0:
return _load_unit_class_icon_texture(unit)
1:
return _make_toolbar_icon("status")
2:
return _make_toolbar_icon(_battle_unit_action_icon_kind(unit))
3:
return _make_toolbar_icon(_battle_zone_icon_kind(badge_text))
_:
return null
func _battle_unit_action_icon_kind(unit: Dictionary) -> String:
if not bool(unit.get("alive", true)):
return "cancel"
if str(unit.get("team", "")) == BattleState.TEAM_ENEMY:
return "threat"
if not bool(unit.get("controllable", true)):
return "formation"
if bool(unit.get("acted", false)):
return "wait"
return "move"
func _battle_zone_icon_kind(zone_text: String) -> String:
match zone_text:
"성채":
return "objective"
"마을":
return "shop"
"위치 미상":
return "status"
_:
return "terrain"
func _format_battle_unit_list_row_tooltip(unit: Dictionary) -> String: func _format_battle_unit_list_row_tooltip(unit: Dictionary) -> String:
var hint := "클릭하면 위치로 이동합니다." var hint := "클릭하면 위치로 이동합니다."
if str(unit.get("team", "")) == BattleState.TEAM_PLAYER and _can_select(unit) and _can_select_from_battle_unit_list(): if str(unit.get("team", "")) == BattleState.TEAM_PLAYER and _can_select(unit) and _can_select_from_battle_unit_list():

View File

@@ -3947,6 +3947,15 @@ func _find_descendant_by_name(root: Node, node_name: String) -> Node:
return null return null
func _count_descendants_by_name(root: Node, node_name: String) -> int:
if root == null:
return 0
var count := 1 if str(root.name).begins_with(node_name) else 0
for child in root.get_children():
count += _count_descendants_by_name(child, node_name)
return count
func _is_descendant_of(node: Node, ancestor: Node) -> bool: func _is_descendant_of(node: Node, ancestor: Node) -> bool:
var current := node var current := node
while current != null: while current != null:
@@ -4171,6 +4180,12 @@ func _check_battle_unit_list_panel(failures: Array[String]) -> void:
else: else:
_check_panel_style_fill(failures, scene.battle_unit_list_panel, "battle unit list panel", Color(0.045, 0.048, 0.044, 0.93)) _check_panel_style_fill(failures, scene.battle_unit_list_panel, "battle unit list panel", Color(0.045, 0.048, 0.044, 0.93))
_check_panel_uses_generated_texture(failures, scene.battle_unit_list_panel, "battle unit list generated frame") _check_panel_uses_generated_texture(failures, scene.battle_unit_list_panel, "battle unit list generated frame")
var header_icon := _find_descendant_by_name(scene.battle_unit_list_panel, "BattleUnitListHeaderIcon")
if header_icon == null or not header_icon is TextureRect or (header_icon as TextureRect).texture == null:
failures.append("battle unit list header should carry generated unit-list icon artwork")
var header_seal := _find_descendant_by_name(scene.battle_unit_list_panel, "BattleUnitListHeaderSeal")
if header_seal == null:
failures.append("battle unit list header should use a compact Korean seal mark")
if scene.battle_unit_list_title_label == null or not scene.battle_unit_list_title_label.text.contains("아군"): if scene.battle_unit_list_title_label == null or not scene.battle_unit_list_title_label.text.contains("아군"):
failures.append("battle unit list should open on the ally tab") failures.append("battle unit list should open on the ally tab")
if scene.battle_unit_list_rows == null or scene.battle_unit_list_rows.get_child_count() < 2: if scene.battle_unit_list_rows == null or scene.battle_unit_list_rows.get_child_count() < 2:
@@ -4189,6 +4204,10 @@ func _check_battle_unit_list_panel(failures: Array[String]) -> void:
failures.append("ally battle unit list tooltips should retain selection detail: %s" % ally_tooltips) failures.append("ally battle unit list tooltips should retain selection detail: %s" % ally_tooltips)
if not _has_descendant_texture(scene.battle_unit_list_rows): if not _has_descendant_texture(scene.battle_unit_list_rows):
failures.append("ally battle unit list should show officer portrait artwork") failures.append("ally battle unit list should show officer portrait artwork")
if _find_descendant_by_name(scene.battle_unit_list_rows, "BattleUnitListClassIcon") == null:
failures.append("ally battle unit list rows should overlay generated class crest artwork")
if _count_descendants_by_name(scene.battle_unit_list_rows, "BattleUnitListBadgeIcon") < 4:
failures.append("ally battle unit list compact badges should include icon artwork for class, HP, action, and zone")
scene._on_battle_unit_list_row_pressed("cao_cao") scene._on_battle_unit_list_row_pressed("cao_cao")
var selected: Dictionary = scene.state.get_selected_unit() var selected: Dictionary = scene.state.get_selected_unit()
@@ -4218,6 +4237,10 @@ func _check_battle_unit_list_panel(failures: Array[String]) -> void:
failures.append("enemy battle unit list tooltips should retain enemy detail: %s" % enemy_tooltips) failures.append("enemy battle unit list tooltips should retain enemy detail: %s" % enemy_tooltips)
if not _has_descendant_texture(scene.battle_unit_list_rows): if not _has_descendant_texture(scene.battle_unit_list_rows):
failures.append("enemy battle unit list should show unit sprite artwork") failures.append("enemy battle unit list should show unit sprite artwork")
if _find_descendant_by_name(scene.battle_unit_list_rows, "BattleUnitListClassIcon") == null:
failures.append("enemy battle unit list rows should overlay generated class crest artwork")
if _count_descendants_by_name(scene.battle_unit_list_rows, "BattleUnitListBadgeIcon") < 4:
failures.append("enemy battle unit list compact badges should include icon artwork for class, HP, action, and zone")
scene._on_battle_unit_list_row_pressed("yellow_turban_1") scene._on_battle_unit_list_row_pressed("yellow_turban_1")
selected = scene.state.get_selected_unit() selected = scene.state.get_selected_unit()
if selected.is_empty() or str(selected.get("id", "")) != "cao_cao": if selected.is_empty() or str(selected.get("id", "")) != "cao_cao":