diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index c2ea73d..a90cc42 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -173,6 +173,10 @@ const HUD_UNIT_SUMMARY_SIZE := Vector2(320, 42) const HUD_UNIT_BADGE_ROW_SIZE := Vector2(320, 22) const HUD_UNIT_BADGE_SIZE := Vector2(26, 22) const HUD_UNIT_BAR_SIZE := Vector2(252, 12) +const HUD_UNIT_PORTRAIT_SEAL_SIZE := Vector2(26, 26) +const HUD_UNIT_PORTRAIT_CLASS_PANEL_SIZE := Vector2(30, 30) +const HUD_UNIT_PORTRAIT_CLASS_ICON_SIZE := Vector2(24, 24) +const HUD_UNIT_RESOURCE_ICON_SIZE := Vector2(16, 16) const TOP_HUD_CHIP_ICON_SIZE := Vector2(28, 28) const SIDE_INFO_CHIP_ICON_SIZE := Vector2(26, 26) const HUD_COMMAND_GRID_SIZE := Vector2(420, 46) @@ -408,6 +412,10 @@ var screen_backdrop: ColorRect var hud_unit_portrait_panel: PanelContainer var hud_unit_portrait_texture: TextureRect var hud_unit_portrait_label: Label +var hud_unit_team_seal_panel: PanelContainer +var hud_unit_team_seal_label: Label +var hud_unit_portrait_class_panel: PanelContainer +var hud_unit_portrait_class_icon: TextureRect var hud_unit_info_column: VBoxContainer var hud_unit_badge_row: HBoxContainer var hud_unit_hp_bar: ProgressBar @@ -1164,15 +1172,26 @@ func _configure_local_command_button(button: Button, icon_kind: String, label: S button.mouse_exited.connect(_on_local_command_button_mouse_exited) -func _make_hud_unit_resource_row(label_text: String, fill_color: Color) -> Dictionary: +func _make_hud_unit_resource_row(label_text: String, fill_color: Color, icon_kind: String) -> Dictionary: var row := HBoxContainer.new() row.custom_minimum_size = Vector2(HUD_UNIT_INFO_SIZE.x, 18.0) row.size_flags_horizontal = Control.SIZE_EXPAND_FILL - row.add_theme_constant_override("separation", 6) + row.add_theme_constant_override("separation", 5) + + var icon := TextureRect.new() + icon.name = "HudUnitResourceIcon" + icon.custom_minimum_size = HUD_UNIT_RESOURCE_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 = _make_toolbar_icon(icon_kind) + icon.tooltip_text = label_text + row.add_child(icon) var row_label := Label.new() - row_label.text = label_text - row_label.custom_minimum_size = Vector2(42, 16) + row_label.text = label_text.substr(0, 1) + row_label.tooltip_text = label_text + row_label.custom_minimum_size = Vector2(18, 16) row_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _apply_label_style(row_label, UI_OLD_BRONZE, 11) row.add_child(row_label) @@ -2901,6 +2920,42 @@ func _create_hud() -> void: _apply_label_style(hud_unit_portrait_label, UI_PARCHMENT_TEXT) hud_unit_portrait_stack.add_child(hud_unit_portrait_label) + hud_unit_team_seal_panel = PanelContainer.new() + hud_unit_team_seal_panel.name = "HudUnitTeamSeal" + hud_unit_team_seal_panel.position = Vector2(1, 1) + hud_unit_team_seal_panel.size = HUD_UNIT_PORTRAIT_SEAL_SIZE + hud_unit_team_seal_panel.custom_minimum_size = HUD_UNIT_PORTRAIT_SEAL_SIZE + hud_unit_team_seal_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE + _apply_panel_style(hud_unit_team_seal_panel, "command_seal") + hud_unit_portrait_stack.add_child(hud_unit_team_seal_panel) + + hud_unit_team_seal_label = Label.new() + hud_unit_team_seal_label.set_anchors_preset(Control.PRESET_FULL_RECT) + hud_unit_team_seal_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + hud_unit_team_seal_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + hud_unit_team_seal_label.custom_minimum_size = Vector2(HUD_UNIT_PORTRAIT_SEAL_SIZE.x - 6.0, HUD_UNIT_PORTRAIT_SEAL_SIZE.y - 6.0) + _apply_label_style(hud_unit_team_seal_label, UI_PARCHMENT_TEXT, 12) + hud_unit_team_seal_panel.add_child(hud_unit_team_seal_label) + + hud_unit_portrait_class_panel = PanelContainer.new() + hud_unit_portrait_class_panel.name = "HudUnitClassCrest" + hud_unit_portrait_class_panel.position = Vector2( + HUD_UNIT_PORTRAIT_STACK_SIZE.x - HUD_UNIT_PORTRAIT_CLASS_PANEL_SIZE.x + 2.0, + HUD_UNIT_PORTRAIT_STACK_SIZE.y - HUD_UNIT_PORTRAIT_CLASS_PANEL_SIZE.y + 2.0 + ) + hud_unit_portrait_class_panel.size = HUD_UNIT_PORTRAIT_CLASS_PANEL_SIZE + hud_unit_portrait_class_panel.custom_minimum_size = HUD_UNIT_PORTRAIT_CLASS_PANEL_SIZE + hud_unit_portrait_class_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE + _apply_panel_style(hud_unit_portrait_class_panel, "caption") + hud_unit_portrait_stack.add_child(hud_unit_portrait_class_panel) + + hud_unit_portrait_class_icon = TextureRect.new() + hud_unit_portrait_class_icon.name = "HudUnitClassIcon" + hud_unit_portrait_class_icon.custom_minimum_size = HUD_UNIT_PORTRAIT_CLASS_ICON_SIZE + hud_unit_portrait_class_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + hud_unit_portrait_class_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + hud_unit_portrait_class_panel.add_child(hud_unit_portrait_class_icon) + hud_unit_info_column = VBoxContainer.new() hud_unit_info_column.custom_minimum_size = HUD_UNIT_INFO_SIZE hud_unit_info_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL @@ -2920,11 +2975,11 @@ func _create_hud() -> void: hud_unit_badge_row.add_theme_constant_override("separation", 3) hud_unit_info_column.add_child(hud_unit_badge_row) - var hp_row := _make_hud_unit_resource_row("병력", Color(0.66, 0.12, 0.075, 1.0)) + var hp_row := _make_hud_unit_resource_row("병력", Color(0.66, 0.12, 0.075, 1.0), "status") hud_unit_hp_bar = hp_row["bar"] as ProgressBar hud_unit_info_column.add_child(hp_row["row"] as Control) - var mp_row := _make_hud_unit_resource_row("기력", Color(0.20, 0.42, 0.70, 1.0)) + var mp_row := _make_hud_unit_resource_row("기력", Color(0.20, 0.42, 0.70, 1.0), "tactic") hud_unit_mp_bar = mp_row["bar"] as ProgressBar hud_unit_info_column.add_child(mp_row["row"] as Control) @@ -9069,9 +9124,23 @@ func _update_hud_unit_portrait(unit: Dictionary) -> void: hud_unit_portrait_label.text = "" hud_unit_portrait_label.visible = false hud_unit_portrait_label.tooltip_text = "" + if hud_unit_team_seal_panel != null: + hud_unit_team_seal_panel.visible = false + hud_unit_team_seal_panel.tooltip_text = "" + if hud_unit_team_seal_label != null: + hud_unit_team_seal_label.text = "" + hud_unit_team_seal_label.tooltip_text = "" + if hud_unit_portrait_class_panel != null: + hud_unit_portrait_class_panel.visible = false + hud_unit_portrait_class_panel.tooltip_text = "" + if hud_unit_portrait_class_icon != null: + hud_unit_portrait_class_icon.texture = null + hud_unit_portrait_class_icon.tooltip_text = "" return hud_unit_portrait_panel.visible = true var focus_detail := _format_unit_focus_text(unit, "Hover") + var team_tooltip := _hud_unit_badge_tooltip(_unit_team_text(str(unit.get("team", ""))), focus_detail) + var class_tooltip := _hud_unit_badge_tooltip("%s · %s" % [_unit_class_display_name(unit), _unit_role_text(unit)], focus_detail) hud_unit_portrait_panel.tooltip_text = focus_detail var texture := _load_portrait_texture(str(unit.get("portrait", ""))) if texture == null: @@ -9083,6 +9152,18 @@ func _update_hud_unit_portrait(unit: Dictionary) -> void: hud_unit_portrait_label.text = _dialogue_portrait_initials(str(unit.get("name", ""))) hud_unit_portrait_label.visible = not has_portrait hud_unit_portrait_label.tooltip_text = focus_detail + if hud_unit_team_seal_panel != null: + hud_unit_team_seal_panel.visible = true + hud_unit_team_seal_panel.tooltip_text = team_tooltip + if hud_unit_team_seal_label != null: + hud_unit_team_seal_label.text = _hud_unit_team_badge_text(unit) + hud_unit_team_seal_label.tooltip_text = team_tooltip + if hud_unit_portrait_class_panel != null: + hud_unit_portrait_class_panel.visible = true + hud_unit_portrait_class_panel.tooltip_text = class_tooltip + if hud_unit_portrait_class_icon != null: + hud_unit_portrait_class_icon.texture = _load_unit_class_icon_texture(unit) + hud_unit_portrait_class_icon.tooltip_text = class_tooltip func _update_cell_info() -> void: diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 28b86ab..d2f410d 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3268,6 +3268,23 @@ func _check_hud_focus_text(failures: Array[String]) -> void: failures.append("selected HUD should expose Cao Cao MP through a resource bar") if not scene.hud_unit_hp_bar.tooltip_text.contains("병력") or not scene.hud_unit_hp_bar.tooltip_text.contains("군기: 조조"): failures.append("selected HP bar tooltip should preserve unit detail: %s" % scene.hud_unit_hp_bar.tooltip_text) + var portrait_team_seal := _find_descendant_by_name(scene.hud_unit_portrait_panel, "HudUnitTeamSeal") + if portrait_team_seal == null: + failures.append("selected unit portrait should overlay a generated team seal") + else: + _check_panel_uses_generated_texture(failures, portrait_team_seal, "selected portrait team seal") + if not _collect_child_text(portrait_team_seal).contains("아"): + failures.append("selected portrait team seal should show ally mark: %s" % _collect_child_text(portrait_team_seal)) + var portrait_class_icon := _find_descendant_by_name(scene.hud_unit_portrait_panel, "HudUnitClassIcon") + if portrait_class_icon == null or not portrait_class_icon is TextureRect or (portrait_class_icon as TextureRect).texture == null: + failures.append("selected unit portrait should overlay generated class crest artwork") + var portrait_class_panel := _find_descendant_by_name(scene.hud_unit_portrait_panel, "HudUnitClassCrest") + if portrait_class_panel == null: + failures.append("selected unit portrait class crest frame missing") + else: + _check_panel_uses_generated_texture(failures, portrait_class_panel, "selected portrait class crest frame") + if _count_descendants_by_name(scene.hud_unit_info_column, "HudUnitResourceIcon") < 2: + failures.append("selected unit resource rows should use generated HP/MP icons") if scene.selected_label.text.contains("병력") or scene.selected_label.text.contains("기력"): failures.append("selected label should not duplicate HP/MP bars: %s" % scene.selected_label.text) if scene.selected_label.text.contains("군령:"):