Randoloot/scripts/game/flight_hud.gd
2026-10-12 23:35:17 +02:00

319 lines
14 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends CanvasLayer
@onready var debug_toggle: Button = $HUDRoot/DebugToggle
@onready var debug_info: Label = $HUDRoot/DebugInfo
@onready var player: CharacterBody2D = get_parent().get_node("PlayerShip")
@onready var inventory_button: Button = $HUDRoot/ShipEquipmentPanel/Contents/InventoryButton
@onready var inventory_panel: Control = $HUDRoot/ShipInventoryPanel
var _debug_expanded := true
var _ability_buttons: Dictionary = {}
var _ability_bar: HBoxContainer
var _ability_ready_style: StyleBoxFlat
var _ability_blocked_style: StyleBoxFlat
var _inventory_clip: Control
var _sidebar: PanelContainer
var _sidebar_toggle: Button
var _sidebar_open := true
var _sidebar_tween: Tween
const SIDEBAR_WIDTH := 306.0
const EQUIPMENT_UI_SCALE := 0.8
var _debug_scroll: ScrollContainer
var _captain_panel: PanelContainer
var _cockpit: Control
func _ready() -> void:
_sidebar_open = bool(GameState.hud_panel_state.get("ship_open", true))
_debug_expanded = bool(GameState.hud_panel_state.get("debug_expanded", true))
debug_info.visible = _debug_expanded
var energy_indicator := Node2D.new()
energy_indicator.name = "EnergyIndicator"
energy_indicator.set_script(preload("res://scripts/game/ship_energy_indicator.gd"))
energy_indicator.set("player", player)
player.add_child(energy_indicator)
var feedback := Control.new()
feedback.name = "CombatFeedback"
feedback.set_script(preload("res://scripts/ui/combat_feedback.gd"))
feedback.set("player", player)
$HUDRoot.add_child(feedback)
debug_info.add_theme_font_size_override("font_size", 10)
# The display font has unusually tall vertical metrics; keep debug rows compact.
debug_info.add_theme_constant_override("line_spacing", mini(0, 13 - ceili(debug_info.get_theme_font("font").get_height(10))))
_create_sidebar()
_inventory_clip = Control.new()
_inventory_clip.name = "InventoryDrawer"
_inventory_clip.clip_contents = true
_inventory_clip.mouse_filter = Control.MOUSE_FILTER_IGNORE
$HUDRoot.add_child(_inventory_clip)
inventory_panel.reparent(_inventory_clip, false)
inventory_panel.set("layout_mode", 0)
inventory_panel.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
inventory_panel.size = Vector2(560, inventory_panel.get_combined_minimum_size().y)
inventory_panel.scale = Vector2.ONE * EQUIPMENT_UI_SCALE
inventory_panel.set("drawer_width", inventory_panel.size.x * EQUIPMENT_UI_SCALE)
$HUDRoot.resized.connect(func() -> void: _layout_sidebar.call_deferred())
_create_ability_bar()
debug_toggle.pressed.connect(_toggle_debug)
inventory_button.pressed.connect(_toggle_inventory)
_update_debug_toggle()
_layout_sidebar.call_deferred()
if _sidebar_open and bool(GameState.hud_panel_state.get("inventory_open", false)):
inventory_panel.call("_set_drawer_open", true)
func _create_sidebar() -> void:
_captain_panel = $HUDRoot/CaptainPanel
_captain_panel.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
_captain_panel.position = Vector2(12, 12)
_captain_panel.size = Vector2(320, _captain_panel.get_combined_minimum_size().y)
_captain_panel.resized.connect(_layout_debug)
_debug_scroll = ScrollContainer.new()
_debug_scroll.name = "DebugScroll"
_debug_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
$HUDRoot.add_child(_debug_scroll)
debug_info.reparent(_debug_scroll, false)
debug_info.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
debug_info.size_flags_horizontal = Control.SIZE_EXPAND_FILL
debug_info.mouse_filter = Control.MOUSE_FILTER_IGNORE
_sidebar = PanelContainer.new()
_sidebar.name = "ShipDrawer"
var style := StyleBoxFlat.new()
style.bg_color = Color(0.025, 0.042, 0.064, 0.97)
style.border_color = Color("376779")
style.set_border_width_all(1)
style.set_corner_radius_all(8)
style.set_content_margin_all(8)
_sidebar.add_theme_stylebox_override("panel", style)
$HUDRoot.add_child(_sidebar)
var scroll := ScrollContainer.new()
scroll.name = "Scroll"
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
_sidebar.add_child(scroll)
var contents := VBoxContainer.new()
contents.name = "Contents"
contents.size_flags_horizontal = Control.SIZE_EXPAND_FILL
scroll.add_child(contents)
var equipment := $HUDRoot/ShipEquipmentPanel as PanelContainer
equipment.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
equipment.reparent(contents, false)
equipment.size_flags_horizontal = Control.SIZE_EXPAND_FILL
equipment.add_theme_stylebox_override("panel", StyleBoxEmpty.new())
var crafting := PanelContainer.new()
crafting.name = "CraftingPanel"
crafting.set_script(preload("res://scripts/ui/ship_crafting_panel.gd"))
equipment.get_node("Contents").add_child(crafting)
contents.minimum_size_changed.connect(func() -> void: _layout_sidebar.call_deferred())
_sidebar_toggle = Button.new()
_sidebar_toggle.name = "ShipToggle"
_sidebar_toggle.text = "STATEK ▾" if _sidebar_open else "STATEK ◀"
_sidebar_toggle.add_theme_font_size_override("font_size", 12)
_sidebar_toggle.focus_mode = Control.FOCUS_NONE
$HUDRoot.add_child(_sidebar_toggle)
_sidebar_toggle.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT)
_sidebar_toggle.offset_left = -180
_sidebar_toggle.offset_right = -12
_sidebar_toggle.offset_top = 12
_sidebar_toggle.offset_bottom = 40
_sidebar_toggle.pressed.connect(_toggle_sidebar)
_cockpit = Control.new()
_cockpit.name = "Cockpit"
_cockpit.set_script(preload("res://scripts/ui/flight_cockpit.gd"))
_cockpit.set("player", player)
_cockpit.set("game", get_parent())
$HUDRoot.add_child(_cockpit)
_layout_debug()
func _layout_debug() -> void:
if not is_instance_valid(_debug_scroll) or not is_instance_valid(_captain_panel):
return
var top := _captain_panel.position.y + _captain_panel.size.y + 6.0
debug_toggle.position = Vector2(12, top)
_debug_scroll.position = Vector2(18, top + 30)
_debug_scroll.size = Vector2(310, maxf(40, $HUDRoot.size.y - top - 42))
_debug_scroll.visible = _debug_expanded
func _toggle_sidebar() -> void:
_sidebar_open = not _sidebar_open
GameState.hud_panel_state["ship_open"] = _sidebar_open
if _sidebar_tween != null and _sidebar_tween.is_running():
_sidebar_tween.kill()
_sidebar_toggle.text = "STATEK ▾" if _sidebar_open else "STATEK ◀"
if _sidebar_open:
_sidebar.show()
else:
close_inventory()
_sidebar_tween = create_tween()
_sidebar_tween.tween_property(_sidebar, "position:x", $HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8 if _sidebar_open else $HUDRoot.size.x, 0.25).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT if _sidebar_open else Tween.EASE_IN)
if not _sidebar_open:
_sidebar_tween.tween_callback(_sidebar.hide)
func _layout_sidebar() -> void:
if not is_instance_valid(_sidebar) or not is_instance_valid(_cockpit):
return
if _sidebar_tween != null and _sidebar_tween.is_running():
_sidebar_tween.kill()
_sidebar.scale = Vector2.ONE * EQUIPMENT_UI_SCALE
_sidebar.size = Vector2(maxf(SIDEBAR_WIDTH, _sidebar.get_combined_minimum_size().x), minf(_sidebar.get_node("Scroll/Contents").get_combined_minimum_size().y + 16.0, maxf(100.0, ($HUDRoot.size.y - 64.0) / EQUIPMENT_UI_SCALE)))
_cockpit.scale = Vector2.ONE * 0.85
_cockpit.size = Vector2(minf(640.0, ($HUDRoot.size.x - 24) / 0.85), 126)
_cockpit.position = Vector2(($HUDRoot.size.x - _cockpit.size.x * 0.85) * 0.5, $HUDRoot.size.y - _cockpit.size.y * 0.85 - 12)
_layout_debug()
_sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8 if _sidebar_open else $HUDRoot.size.x, 44)
_sidebar.visible = _sidebar_open
_inventory_clip.size = inventory_panel.size * EQUIPMENT_UI_SCALE
_layout_inventory()
func _layout_inventory() -> void:
if not is_instance_valid(_inventory_clip) or not is_instance_valid(_sidebar):
return
var equipment := _sidebar.get_node("Scroll/Contents/ShipEquipmentPanel") as Control
_inventory_clip.position = Vector2(maxf(12.0, _sidebar.position.x - _inventory_clip.size.x - 8), minf(equipment.global_position.y, maxf(8.0, $HUDRoot.size.y - inventory_panel.size.y * EQUIPMENT_UI_SCALE - 8)))
func _process(_delta: float) -> void:
_layout_inventory()
_update_ability_bar()
if not _debug_expanded:
return
var game := get_parent()
debug_info.text = """POZIOM UKŁADU: %d
WROGOWIE: %d
PRĘDKOŚĆ: %.1f px/s
AKTUALNA REGENERACJA
Energia: %.1f pkt/s
Tarcze: %.1f pkt/s
%s""".replace("\r", "") % [
GameState.system_level,
int(game.call("get_remaining_enemies")),
player.velocity.length(),
float(player.get("energy_regeneration")) + float(player.call("get_solar_regeneration")),
float(player.get("shield_regeneration")),
_get_bonus_summary(),
]
func _toggle_debug() -> void:
_debug_expanded = not _debug_expanded
GameState.hud_panel_state["debug_expanded"] = _debug_expanded
debug_info.visible = _debug_expanded
_debug_scroll.visible = _debug_expanded
_update_debug_toggle()
func _update_debug_toggle() -> void:
debug_toggle.text = "DEBUG [−]" if _debug_expanded else "DEBUG [+]"
func _toggle_inventory() -> void:
if not _sidebar_open:
_toggle_sidebar()
inventory_panel.call("toggle_drawer")
func close_inventory() -> void:
inventory_panel.call("close_drawer", true)
func _get_bonus_summary() -> String:
var cooldown_bonus := 0.0
var inventory := player.get_node("ShipInventory") as ShipInventory
for item in inventory.weapon_slots + inventory.module_slots:
if item != null:
cooldown_bonus += item.get_stat_total("missile_cooldown_reduction")
return """BONUSY (ADDONY + KAPITAN)
Obrażenia broni: %+.1f%%
Szansa na krytyk: %+.1f%%
Obrażenia krytyczne: %+.1f%%
Szansa na unik: %+.1f%%
Redukcja obrażeń: %+.1f%%
Odzysk tarcz z obrażeń: %.1f%%
Regeneracja energii: %+.1f pkt/s
Regeneracja tarcz: %+.1f pkt/s
Kadłub: %+.1f pkt
Tarcze: %+.1f pkt
Energia: %+.1f pkt
Prędkość maks.: %+.1f%%
Skrócenie cooldownu rakiet: %.2f s
Doświadczenie: %+.1f%%
Kredyty: %+.1f%%""".replace("\r", "") % [
(float(player.call("get_weapon_damage_multiplier")) - 1.0) * 100.0,
float(player.call("get_critical_chance")) * 100.0,
float(player.get("critical_damage_bonus")) * 100.0,
float(player.call("get_evasion_chance")) * 100.0,
float(player.call("get_damage_reduction")) * 100.0,
float(player.get("shield_on_damage_percent")),
float(player.get("energy_regeneration")) - float(player.get("base_energy_regeneration")),
float(player.get("shield_regeneration")) - float(player.get("base_shield_regeneration")),
float(player.get("max_hull")) - float(player.get("base_max_hull")),
float(player.get("max_shields")) - float(player.get("base_max_shields")),
float(player.get("max_energy")) - float(player.get("base_max_energy")),
(float(player.get("max_speed")) / float(player.get("base_max_speed")) - 1.0) * 100.0,
cooldown_bonus,
(float(player.get("experience_multiplier")) - 1.0) * 100.0,
(float(player.get("credits_multiplier")) - 1.0) * 100.0,
]
func _create_ability_bar() -> void:
_ability_ready_style = _make_ability_style(true)
_ability_blocked_style = _make_ability_style(false)
var panel := PanelContainer.new()
panel.name = "ActiveAbilities"
$HUDRoot.add_child(panel)
panel.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM)
panel.offset_left = -120.0
panel.offset_right = 120.0
panel.offset_top = -176.0
panel.offset_bottom = -132.0
panel.add_theme_stylebox_override("panel", StyleBoxEmpty.new())
_ability_bar = HBoxContainer.new()
_ability_bar.name = "AbilityButtons"
_ability_bar.alignment = BoxContainer.ALIGNMENT_CENTER
_ability_bar.add_theme_constant_override("separation", 4)
panel.add_child(_ability_bar)
for index in ShipInventory.EXTENSION_SLOT_COUNT:
var button := Button.new()
button.name = "Ability%d" % (index + 1)
button.custom_minimum_size = Vector2(44, 44)
button.expand_icon = true
button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
button.add_theme_constant_override("icon_max_width", 30)
button.focus_mode = Control.FOCUS_NONE
var shortcut := Label.new()
shortcut.text = str(index + 1)
shortcut.position = Vector2(3, 1)
shortcut.mouse_filter = Control.MOUSE_FILTER_IGNORE
shortcut.add_theme_font_size_override("font_size", 9)
button.add_child(shortcut)
button.add_theme_font_size_override("font_size", 11)
button.pressed.connect(func() -> void: player.call("activate_ability", index))
_ability_bar.add_child(button)
_ability_buttons[index] = button
_update_ability_bar()
func _update_ability_bar() -> void:
var any_equipped := false
for index in _ability_buttons:
var button: Button = _ability_buttons[index]
var ability: ActiveAbility = player.call("get_active_ability", index)
button.visible = ability != null
if ability == null:
continue
any_equipped = true
var reason: String = player.call("ability_unavailable_reason", index)
button.disabled = not reason.is_empty()
var item: ItemInstance = player.get_node("ShipInventory").get_item("extension", index)
button.icon = item.definition.icon
button.text = ""
button.add_theme_color_override("icon_disabled_color", Color(0.45, 0.5, 0.55, 0.65))
var style := _ability_ready_style if reason.is_empty() else _ability_blocked_style
button.add_theme_stylebox_override("normal", style)
button.add_theme_stylebox_override("disabled", style)
button.add_theme_stylebox_override("hover", style)
button.add_theme_color_override("font_color", Color("a5ffda"))
button.add_theme_color_override("font_disabled_color", Color("a3aebb"))
button.tooltip_text = "[%d] %s\n" % [index + 1, ability.display_name] + ability.description + ("\n" + reason if not reason.is_empty() else "\nGotowe")
_ability_bar.get_parent().visible = any_equipped
func _make_ability_style(ready: bool) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color("174237") if ready else Color("202731")
style.border_color = Color("77e8bc") if ready else Color("526071")
style.set_border_width_all(2 if ready else 1)
style.set_content_margin_all(6)
return style