Compact selected unit HUD with hover badges
This commit is contained in:
@@ -103,7 +103,9 @@ const SHOP_ITEM_ICON_SIZE := Vector2(48, 48)
|
||||
const HUD_UNIT_PORTRAIT_SIZE := Vector2(92, 104)
|
||||
const HUD_UNIT_PORTRAIT_STACK_SIZE := Vector2(88, 100)
|
||||
const HUD_UNIT_INFO_SIZE := Vector2(320, 126)
|
||||
const HUD_UNIT_SUMMARY_SIZE := Vector2(320, 58)
|
||||
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_COMMAND_GRID_SIZE := Vector2(420, 46)
|
||||
const HUD_COMMAND_BUTTON_SIZE := Vector2(72, 42)
|
||||
@@ -323,6 +325,7 @@ var hud_unit_portrait_panel: PanelContainer
|
||||
var hud_unit_portrait_texture: TextureRect
|
||||
var hud_unit_portrait_label: Label
|
||||
var hud_unit_info_column: VBoxContainer
|
||||
var hud_unit_badge_row: HBoxContainer
|
||||
var hud_unit_hp_bar: ProgressBar
|
||||
var hud_unit_mp_bar: ProgressBar
|
||||
var selected_label: Label
|
||||
@@ -887,6 +890,210 @@ func _make_hud_unit_resource_row(label_text: String, fill_color: Color) -> Dicti
|
||||
}
|
||||
|
||||
|
||||
func _clear_hud_unit_badges() -> void:
|
||||
if hud_unit_badge_row == null:
|
||||
return
|
||||
for child in hud_unit_badge_row.get_children():
|
||||
hud_unit_badge_row.remove_child(child)
|
||||
child.queue_free()
|
||||
|
||||
|
||||
func _update_hud_unit_badges(unit: Dictionary, detail_text: String) -> void:
|
||||
if hud_unit_badge_row == null:
|
||||
return
|
||||
_clear_hud_unit_badges()
|
||||
hud_unit_badge_row.tooltip_text = detail_text
|
||||
hud_unit_badge_row.visible = not unit.is_empty()
|
||||
if unit.is_empty():
|
||||
return
|
||||
|
||||
hud_unit_badge_row.add_child(_make_hud_unit_badge(
|
||||
_hud_unit_team_badge_text(unit),
|
||||
_hud_unit_badge_tooltip(_unit_team_text(str(unit.get("team", ""))), detail_text),
|
||||
_hud_unit_team_badge_color(unit)
|
||||
))
|
||||
hud_unit_badge_row.add_child(_make_hud_unit_badge(
|
||||
_hud_unit_class_badge_text(unit),
|
||||
_hud_unit_badge_tooltip("%s · %s" % [_unit_class_display_name(unit), _unit_role_text(unit)], detail_text),
|
||||
_hud_unit_class_badge_color(unit)
|
||||
))
|
||||
hud_unit_badge_row.add_child(_make_hud_unit_badge(
|
||||
"동" if _unit_pixel_facing(unit) >= 0 else "서",
|
||||
_hud_unit_badge_tooltip("시선 %s\n등 뒤를 잡히면 피해가 커집니다." % _unit_facing_text(unit), detail_text),
|
||||
Color(0.24, 0.20, 0.12, 0.98)
|
||||
))
|
||||
|
||||
var hp_max := maxi(1, int(unit.get("max_hp", 1)))
|
||||
var hp_ratio := float(clampi(int(unit.get("hp", 0)), 0, hp_max)) / float(hp_max)
|
||||
if hp_ratio <= LOW_HP_WARNING_RATIO:
|
||||
hud_unit_badge_row.add_child(_make_hud_unit_badge(
|
||||
"위",
|
||||
_hud_unit_badge_tooltip("병력이 크게 낮습니다.", detail_text),
|
||||
Color(0.56, 0.055, 0.028, 0.99)
|
||||
))
|
||||
|
||||
var marker_kinds := _unit_status_marker_kinds(unit)
|
||||
if not marker_kinds.is_empty():
|
||||
var marker_kind := marker_kinds[0]
|
||||
hud_unit_badge_row.add_child(_make_hud_unit_badge(
|
||||
_unit_status_marker_glyph(marker_kind),
|
||||
_hud_unit_badge_tooltip("상태 표식\n자세한 효과는 아래 상세를 확인합니다.", detail_text),
|
||||
_unit_status_marker_color(marker_kind),
|
||||
"",
|
||||
_unit_status_marker_text_color(marker_kind)
|
||||
))
|
||||
|
||||
for action_badge in _hud_unit_action_badges(unit, detail_text):
|
||||
var action_color: Color = action_badge.get("color", Color(0.18, 0.10, 0.04, 0.98))
|
||||
hud_unit_badge_row.add_child(_make_hud_unit_badge(
|
||||
str(action_badge.get("text", "")),
|
||||
str(action_badge.get("tooltip", "")),
|
||||
action_color,
|
||||
str(action_badge.get("icon", ""))
|
||||
))
|
||||
|
||||
|
||||
func _make_hud_unit_badge(
|
||||
label_text: String,
|
||||
tooltip: String,
|
||||
fill_color: Color,
|
||||
icon_kind: String = "",
|
||||
text_color: Color = UI_PARCHMENT_TEXT
|
||||
) -> PanelContainer:
|
||||
var panel := PanelContainer.new()
|
||||
panel.custom_minimum_size = HUD_UNIT_BADGE_SIZE
|
||||
panel.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
panel.tooltip_text = tooltip
|
||||
panel.add_theme_stylebox_override(
|
||||
"panel",
|
||||
_make_panel_style(fill_color, fill_color.lightened(0.34), 1, 2, 2, 0)
|
||||
)
|
||||
if not icon_kind.strip_edges().is_empty():
|
||||
var icon := TextureRect.new()
|
||||
icon.custom_minimum_size = Vector2(22, 18)
|
||||
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 = tooltip
|
||||
panel.add_child(icon)
|
||||
return panel
|
||||
|
||||
var label := Label.new()
|
||||
label.text = label_text
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.custom_minimum_size = Vector2(HUD_UNIT_BADGE_SIZE.x - 4.0, HUD_UNIT_BADGE_SIZE.y - 4.0)
|
||||
label.tooltip_text = tooltip
|
||||
_apply_label_style(label, text_color, 11)
|
||||
panel.add_child(label)
|
||||
return panel
|
||||
|
||||
|
||||
func _hud_unit_badge_tooltip(primary_text: String, detail_text: String) -> String:
|
||||
if detail_text.strip_edges().is_empty():
|
||||
return primary_text
|
||||
return "%s\n%s" % [primary_text, detail_text]
|
||||
|
||||
|
||||
func _hud_unit_team_badge_text(unit: Dictionary) -> String:
|
||||
var team := str(unit.get("team", ""))
|
||||
if team == BattleState.TEAM_PLAYER:
|
||||
return "아"
|
||||
if team == BattleState.TEAM_ENEMY:
|
||||
return "적"
|
||||
return "중"
|
||||
|
||||
|
||||
func _hud_unit_team_badge_color(unit: Dictionary) -> Color:
|
||||
var team := str(unit.get("team", ""))
|
||||
if team == BattleState.TEAM_PLAYER:
|
||||
return Color(0.11, 0.30, 0.22, 0.99)
|
||||
if team == BattleState.TEAM_ENEMY:
|
||||
return Color(0.48, 0.045, 0.028, 0.99)
|
||||
return Color(0.24, 0.22, 0.16, 0.99)
|
||||
|
||||
|
||||
func _hud_unit_class_badge_text(unit: Dictionary) -> String:
|
||||
var class_id := str(unit.get("class_id", "")).to_lower()
|
||||
if class_id.contains("cavalry"):
|
||||
return "기"
|
||||
if class_id.contains("archer") or class_id.contains("marksman"):
|
||||
return "궁"
|
||||
if class_id.contains("strategist") or class_id.contains("advisor"):
|
||||
return "책"
|
||||
if class_id.contains("hero") or class_id.contains("commander"):
|
||||
return "장"
|
||||
if class_id.contains("warrior") or class_id.contains("champion") or class_id.contains("bandit"):
|
||||
return "무"
|
||||
return "보"
|
||||
|
||||
|
||||
func _hud_unit_class_badge_color(unit: Dictionary) -> Color:
|
||||
var class_id := str(unit.get("class_id", "")).to_lower()
|
||||
if class_id.contains("cavalry"):
|
||||
return Color(0.52, 0.24, 0.070, 0.99)
|
||||
if class_id.contains("archer") or class_id.contains("marksman"):
|
||||
return Color(0.18, 0.31, 0.14, 0.99)
|
||||
if class_id.contains("strategist") or class_id.contains("advisor"):
|
||||
return Color(0.15, 0.22, 0.38, 0.99)
|
||||
if class_id.contains("hero") or class_id.contains("commander"):
|
||||
return Color(0.44, 0.055, 0.032, 0.99)
|
||||
if class_id.contains("warrior") or class_id.contains("champion") or class_id.contains("bandit"):
|
||||
return Color(0.37, 0.15, 0.055, 0.99)
|
||||
return Color(0.24, 0.19, 0.10, 0.99)
|
||||
|
||||
|
||||
func _hud_unit_action_badges(unit: Dictionary, detail_text: String) -> Array:
|
||||
var result := []
|
||||
var unit_id := str(unit.get("id", ""))
|
||||
if unit_id.is_empty():
|
||||
result.append(_hud_unit_action_badge("봉", "", "봉인", "명령할 수 없는 표식입니다.", detail_text, Color(0.20, 0.16, 0.11, 0.98)))
|
||||
return result
|
||||
var team := str(unit.get("team", ""))
|
||||
if team != BattleState.TEAM_PLAYER:
|
||||
result.append(_hud_unit_action_badge("적", "threat", "적군", "직접 명령할 수 없습니다.", detail_text, Color(0.48, 0.045, 0.028, 0.99)))
|
||||
return result
|
||||
if not bool(unit.get("controllable", true)):
|
||||
result.append(_hud_unit_action_badge("호", "", "호위", "호위 대상은 직접 명령할 수 없습니다.", detail_text, Color(0.31, 0.23, 0.12, 0.98)))
|
||||
return result
|
||||
if not bool(unit.get("alive", true)) or not bool(unit.get("deployed", true)) or bool(unit.get("acted", false)):
|
||||
result.append(_hud_unit_action_badge("봉", "", "봉인", "이 부대는 이미 행동했거나 출진하지 않았습니다.", detail_text, Color(0.20, 0.16, 0.11, 0.98)))
|
||||
return result
|
||||
var phase_block := _action_phase_block_reason()
|
||||
if not phase_block.is_empty():
|
||||
result.append(_hud_unit_action_badge("대", "", str(phase_block.get("label", "대기")), str(phase_block.get("tooltip", "")), detail_text, Color(0.20, 0.16, 0.11, 0.98)))
|
||||
return result
|
||||
if not bool(unit.get("moved", false)) and not state.get_movement_range(unit_id).is_empty():
|
||||
result.append(_hud_unit_action_badge("행", "move", "행군", "이동 가능한 칸이 있습니다.", detail_text, Color(0.34, 0.23, 0.070, 0.99)))
|
||||
if not state.get_attack_cells(unit_id).is_empty():
|
||||
result.append(_hud_unit_action_badge("타", "attack", "타격", "무기로 공격할 수 있습니다.", detail_text, Color(0.46, 0.055, 0.032, 0.99)))
|
||||
var skill_id := state.get_first_usable_skill_id(unit_id)
|
||||
if not skill_id.is_empty() and _unit_has_usable_skill_target(unit_id):
|
||||
result.append(_hud_unit_action_badge("책", "tactic", "책략", "쓸 수 있는 책략 표식이 있습니다.", detail_text, Color(0.12, 0.24, 0.36, 0.99)))
|
||||
if _unit_has_usable_item_target(unit_id):
|
||||
result.append(_hud_unit_action_badge("도", "item", "도구", "사용 가능한 도구 대상이 있습니다.", detail_text, Color(0.18, 0.31, 0.16, 0.99)))
|
||||
if not bool(unit.get("moved", false)):
|
||||
result.append(_hud_unit_action_badge("병", "equip", "병장", "행군 전 병장을 바꿀 수 있습니다.", detail_text, Color(0.33, 0.18, 0.060, 0.99)))
|
||||
result.append(_hud_unit_action_badge("대", "wait", "대기", "이 부대의 행동을 마칠 수 있습니다.", detail_text, Color(0.22, 0.16, 0.09, 0.99)))
|
||||
return result
|
||||
|
||||
|
||||
func _hud_unit_action_badge(
|
||||
text: String,
|
||||
icon_kind: String,
|
||||
label: String,
|
||||
hint: String,
|
||||
detail_text: String,
|
||||
color: Color
|
||||
) -> Dictionary:
|
||||
return {
|
||||
"text": text,
|
||||
"icon": icon_kind,
|
||||
"color": color,
|
||||
"tooltip": _hud_unit_badge_tooltip("%s\n%s" % [label, hint], detail_text)
|
||||
}
|
||||
|
||||
|
||||
func _format_local_command_tooltip(label: String, hint: String) -> String:
|
||||
var normalized_label := label.strip_edges()
|
||||
var normalized_hint := hint.strip_edges()
|
||||
@@ -904,6 +1111,13 @@ func _make_toolbar_icon(kind: String) -> Texture2D:
|
||||
var shadow := Color(0.07, 0.028, 0.012, 0.82)
|
||||
var red := Color(UI_SEAL_RED.r, UI_SEAL_RED.g, UI_SEAL_RED.b, 1.0)
|
||||
match kind:
|
||||
"move":
|
||||
_icon_line(image, Vector2i(8, 22), Vector2i(16, 9), shadow, 5)
|
||||
_icon_line(image, Vector2i(16, 9), Vector2i(24, 22), shadow, 5)
|
||||
_icon_line(image, Vector2i(8, 22), Vector2i(16, 9), ink, 3)
|
||||
_icon_line(image, Vector2i(16, 9), Vector2i(24, 22), ink, 3)
|
||||
_icon_line(image, Vector2i(11, 22), Vector2i(21, 22), ink, 3)
|
||||
_icon_circle(image, Vector2i(16, 9), 3, red)
|
||||
"end_turn":
|
||||
_icon_line(image, Vector2i(7, 10), Vector2i(22, 10), shadow, 4)
|
||||
_icon_line(image, Vector2i(21, 10), Vector2i(21, 22), shadow, 4)
|
||||
@@ -1631,6 +1845,12 @@ func _create_hud() -> void:
|
||||
_apply_label_style(selected_label, UI_PARCHMENT_TEXT, 13)
|
||||
hud_unit_info_column.add_child(selected_label)
|
||||
|
||||
hud_unit_badge_row = HBoxContainer.new()
|
||||
hud_unit_badge_row.custom_minimum_size = HUD_UNIT_BADGE_ROW_SIZE
|
||||
hud_unit_badge_row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
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))
|
||||
hud_unit_hp_bar = hp_row["bar"] as ProgressBar
|
||||
hud_unit_info_column.add_child(hp_row["row"] as Control)
|
||||
@@ -6131,11 +6351,13 @@ func _update_hud() -> void:
|
||||
if hovered_unit.is_empty():
|
||||
selected_label.text = "전장 관망"
|
||||
selected_label.tooltip_text = "마우스를 부대 위에 올리면 상세 군세가 표시됩니다."
|
||||
_update_hud_unit_badges({}, selected_label.tooltip_text)
|
||||
_update_hud_unit_resource_bars({}, selected_label.tooltip_text)
|
||||
else:
|
||||
var hover_detail := _format_unit_focus_text(hovered_unit, "Hover")
|
||||
selected_label.text = _format_unit_focus_summary_text(hovered_unit, "Hover")
|
||||
selected_label.tooltip_text = hover_detail
|
||||
_update_hud_unit_badges(hovered_unit, hover_detail)
|
||||
_update_hud_unit_resource_bars(hovered_unit, hover_detail)
|
||||
_update_wait_button({})
|
||||
_update_tactic_button({})
|
||||
@@ -6149,6 +6371,7 @@ func _update_hud() -> void:
|
||||
var selected_detail := _format_unit_focus_text(selected, "Selected")
|
||||
selected_label.text = _format_unit_focus_summary_text(selected, "Selected")
|
||||
selected_label.tooltip_text = selected_detail
|
||||
_update_hud_unit_badges(selected, selected_detail)
|
||||
_update_hud_unit_resource_bars(selected, selected_detail)
|
||||
_update_wait_button(selected)
|
||||
_update_tactic_button(selected)
|
||||
@@ -6264,21 +6487,13 @@ func _format_unit_focus_summary_text(unit: Dictionary, prefix: String) -> String
|
||||
return "표식 없는 빈 전장입니다."
|
||||
var name := str(unit.get("name", "부대"))
|
||||
var control_label := " 호위" if not bool(unit.get("controllable", true)) else ""
|
||||
var status_text := _unit_action_status_text(unit).replace("군령: ", "")
|
||||
var status_parts := status_text.split(",", false)
|
||||
if status_parts.size() > 3:
|
||||
var visible_parts: Array[String] = []
|
||||
for index in range(3):
|
||||
visible_parts.append(str(status_parts[index]).strip_edges())
|
||||
status_text = "%s 외 %d" % [_join_strings(visible_parts, ", "), status_parts.size() - 3]
|
||||
return "%s: %s%s\n%s 품계 %d · %s\n군령: %s" % [
|
||||
return "%s: %s%s\n%s 품계 %d · %s" % [
|
||||
_unit_focus_prefix_text(prefix),
|
||||
name,
|
||||
control_label,
|
||||
_unit_team_text(str(unit.get("team", ""))),
|
||||
int(unit.get("level", 1)),
|
||||
_unit_class_display_name(unit),
|
||||
status_text
|
||||
_unit_class_display_name(unit)
|
||||
]
|
||||
|
||||
|
||||
@@ -10013,14 +10228,14 @@ func _format_inventory_status_summary_text() -> String:
|
||||
other_count += count
|
||||
var parts: Array[String] = []
|
||||
if consumable_count > 0:
|
||||
parts.append("소모 %d" % consumable_count)
|
||||
parts.append("소 %d" % consumable_count)
|
||||
if equipment_count > 0:
|
||||
parts.append("병장 %d" % equipment_count)
|
||||
parts.append("병 %d" % equipment_count)
|
||||
if other_count > 0:
|
||||
parts.append("잡물 %d" % other_count)
|
||||
parts.append("잡 %d" % other_count)
|
||||
if parts.is_empty():
|
||||
return "군수: 없음"
|
||||
return "군수: %s" % _join_strings(parts, " · ")
|
||||
return "군수 없음"
|
||||
return _join_strings(parts, " · ")
|
||||
|
||||
|
||||
func _format_reward_items(items: Array) -> String:
|
||||
|
||||
Reference in New Issue
Block a user