Show unit portraits in battle HUD
This commit is contained in:
@@ -48,6 +48,8 @@ const DIALOGUE_PANEL_SIZE := Vector2(1040, 210)
|
||||
const DIALOGUE_PORTRAIT_SIZE := Vector2(164, 186)
|
||||
const DIALOGUE_COLUMN_SIZE := Vector2(820, 186)
|
||||
const DIALOGUE_TEXT_SIZE := Vector2(800, 104)
|
||||
const HUD_UNIT_PORTRAIT_SIZE := Vector2(92, 104)
|
||||
const HUD_UNIT_PORTRAIT_STACK_SIZE := Vector2(88, 100)
|
||||
|
||||
var state: BattleState = BattleStateScript.new()
|
||||
var campaign_state: CampaignState = CampaignStateScript.new()
|
||||
@@ -70,6 +72,9 @@ var campaign_complete_screen := false
|
||||
var status_label: Label
|
||||
var objective_label: Label
|
||||
var campaign_status_label: Label
|
||||
var hud_unit_portrait_panel: PanelContainer
|
||||
var hud_unit_portrait_texture: TextureRect
|
||||
var hud_unit_portrait_label: Label
|
||||
var selected_label: Label
|
||||
var cell_info_label: Label
|
||||
var forecast_label: Label
|
||||
@@ -144,6 +149,7 @@ var dialogue_queue: Array = []
|
||||
var active_dialogue_lines: Array = []
|
||||
var active_dialogue_index := 0
|
||||
var portrait_texture_cache: Dictionary = {}
|
||||
var missing_portrait_paths := {}
|
||||
var bgm_player: AudioStreamPlayer
|
||||
var current_bgm_key := ""
|
||||
var audio_stream_cache := {}
|
||||
@@ -252,10 +258,36 @@ func _create_hud() -> void:
|
||||
side_column.add_theme_constant_override("separation", 10)
|
||||
side_panel.add_child(side_column)
|
||||
|
||||
var selected_row := HBoxContainer.new()
|
||||
selected_row.add_theme_constant_override("separation", 8)
|
||||
side_column.add_child(selected_row)
|
||||
|
||||
hud_unit_portrait_panel = PanelContainer.new()
|
||||
hud_unit_portrait_panel.custom_minimum_size = HUD_UNIT_PORTRAIT_SIZE
|
||||
selected_row.add_child(hud_unit_portrait_panel)
|
||||
|
||||
var hud_unit_portrait_stack := Control.new()
|
||||
hud_unit_portrait_stack.custom_minimum_size = HUD_UNIT_PORTRAIT_STACK_SIZE
|
||||
hud_unit_portrait_panel.add_child(hud_unit_portrait_stack)
|
||||
|
||||
hud_unit_portrait_texture = TextureRect.new()
|
||||
hud_unit_portrait_texture.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
hud_unit_portrait_texture.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
hud_unit_portrait_texture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
hud_unit_portrait_stack.add_child(hud_unit_portrait_texture)
|
||||
|
||||
hud_unit_portrait_label = Label.new()
|
||||
hud_unit_portrait_label.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
hud_unit_portrait_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
hud_unit_portrait_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
hud_unit_portrait_label.add_theme_font_size_override("font_size", 24)
|
||||
hud_unit_portrait_stack.add_child(hud_unit_portrait_label)
|
||||
|
||||
selected_label = Label.new()
|
||||
selected_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
selected_label.custom_minimum_size = Vector2(420, 92)
|
||||
side_column.add_child(selected_label)
|
||||
selected_label.custom_minimum_size = Vector2(320, 104)
|
||||
selected_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
selected_row.add_child(selected_label)
|
||||
|
||||
cell_info_label = Label.new()
|
||||
cell_info_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
@@ -1435,7 +1467,21 @@ func _update_hud() -> void:
|
||||
|
||||
var selected := state.get_selected_unit()
|
||||
if selected.is_empty():
|
||||
selected_label.text = "No unit selected."
|
||||
var hovered_unit := _hovered_unit_for_hud_portrait()
|
||||
_update_hud_unit_portrait(hovered_unit)
|
||||
if hovered_unit.is_empty():
|
||||
selected_label.text = "No unit selected."
|
||||
else:
|
||||
selected_label.text = "Hover: %s\n%s Lv.%d %s\nHP %d/%d MP %d/%d" % [
|
||||
str(hovered_unit.get("name", "Unit")),
|
||||
str(hovered_unit.get("team", "")).capitalize(),
|
||||
int(hovered_unit.get("level", 1)),
|
||||
str(hovered_unit.get("class", "Unit")),
|
||||
int(hovered_unit.get("hp", 0)),
|
||||
int(hovered_unit.get("max_hp", 1)),
|
||||
int(hovered_unit.get("mp", 0)),
|
||||
int(hovered_unit.get("max_mp", 0))
|
||||
]
|
||||
wait_button.disabled = true
|
||||
_update_tactic_button({})
|
||||
_hide_tactic_menu()
|
||||
@@ -1444,6 +1490,7 @@ func _update_hud() -> void:
|
||||
_update_equip_button({})
|
||||
_hide_equip_menu()
|
||||
else:
|
||||
_update_hud_unit_portrait(selected)
|
||||
var roster_mark := " *" if selected.get("loaded_from_roster", false) else ""
|
||||
selected_label.text = "%s%s Lv.%d %s EXP %d\nHP %d/%d MP %d/%d ATK %d DEF %d INT %d\nMOV %d RNG %d-%d" % [
|
||||
selected["name"],
|
||||
@@ -1483,6 +1530,31 @@ func _update_hud() -> void:
|
||||
threat_button.disabled = not battle_started or state.battle_status != BattleState.STATUS_ACTIVE or campaign_complete_screen
|
||||
|
||||
|
||||
func _hovered_unit_for_hud_portrait() -> Dictionary:
|
||||
if not state.is_inside(hover_cell):
|
||||
return {}
|
||||
return state.get_unit_at(hover_cell)
|
||||
|
||||
|
||||
func _update_hud_unit_portrait(unit: Dictionary) -> void:
|
||||
if hud_unit_portrait_panel == null:
|
||||
return
|
||||
if unit.is_empty():
|
||||
hud_unit_portrait_panel.visible = false
|
||||
hud_unit_portrait_texture.texture = null
|
||||
hud_unit_portrait_texture.visible = false
|
||||
hud_unit_portrait_label.text = ""
|
||||
hud_unit_portrait_label.visible = false
|
||||
return
|
||||
hud_unit_portrait_panel.visible = true
|
||||
var texture := _load_portrait_texture(str(unit.get("portrait", "")))
|
||||
var has_portrait := texture != null
|
||||
hud_unit_portrait_texture.texture = texture
|
||||
hud_unit_portrait_texture.visible = has_portrait
|
||||
hud_unit_portrait_label.text = _dialogue_portrait_initials(str(unit.get("name", "")))
|
||||
hud_unit_portrait_label.visible = not has_portrait
|
||||
|
||||
|
||||
func _update_cell_info() -> void:
|
||||
if not state.is_inside(hover_cell):
|
||||
cell_info_label.text = "Tile: -"
|
||||
@@ -2032,7 +2104,7 @@ func _apply_dialogue_side(side: String) -> void:
|
||||
func _update_dialogue_portrait(speaker: String, portrait_path: String) -> void:
|
||||
if dialogue_portrait_panel == null:
|
||||
return
|
||||
var texture := _load_dialogue_portrait(portrait_path)
|
||||
var texture := _load_portrait_texture(portrait_path)
|
||||
var has_portrait := texture != null
|
||||
var has_placeholder := not speaker.is_empty()
|
||||
dialogue_portrait_panel.visible = has_portrait or has_placeholder
|
||||
@@ -2042,12 +2114,14 @@ func _update_dialogue_portrait(speaker: String, portrait_path: String) -> void:
|
||||
dialogue_portrait_label.visible = not has_portrait and has_placeholder
|
||||
|
||||
|
||||
func _load_dialogue_portrait(portrait_path: String) -> Texture2D:
|
||||
func _load_portrait_texture(portrait_path: String) -> Texture2D:
|
||||
var normalized_path := portrait_path.strip_edges()
|
||||
if normalized_path.is_empty():
|
||||
return null
|
||||
if not normalized_path.begins_with("res://") and not normalized_path.begins_with("user://"):
|
||||
normalized_path = "res://%s" % normalized_path
|
||||
if missing_portrait_paths.has(normalized_path):
|
||||
return null
|
||||
if portrait_texture_cache.has(normalized_path):
|
||||
return portrait_texture_cache[normalized_path] as Texture2D
|
||||
if ResourceLoader.exists(normalized_path, "Texture2D"):
|
||||
@@ -2056,9 +2130,11 @@ func _load_dialogue_portrait(portrait_path: String) -> Texture2D:
|
||||
portrait_texture_cache[normalized_path] = imported_texture
|
||||
return imported_texture
|
||||
if not FileAccess.file_exists(normalized_path):
|
||||
missing_portrait_paths[normalized_path] = true
|
||||
return null
|
||||
var image := Image.new()
|
||||
if image.load(normalized_path) != OK:
|
||||
missing_portrait_paths[normalized_path] = true
|
||||
return null
|
||||
var raw_texture := ImageTexture.create_from_image(image)
|
||||
portrait_texture_cache[normalized_path] = raw_texture
|
||||
|
||||
Reference in New Issue
Block a user