extends CanvasLayer const RENDER_LAYERS := preload("res://scripts/ui/render_layers.gd") @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 _window_manager: Node var _energy_bank_button: Button var _debug_expanded := false var _ability_buttons: Dictionary = {} var _ability_bar: HBoxContainer var _passive_skills: GridContainer var _passive_captain_panel: PanelContainer var _passive_ship_panel: PanelContainer var _passive_ship_icons: GridContainer var _passive_ship_buttons: Dictionary = {} var _ship_passive_reserved_height := 0.0 var _inventory_clip: Control var _sidebar: PanelContainer var _sidebar_toggle: Button var _sidebar_open := false var _sidebar_tween: Tween const SIDEBAR_WIDTH := 286.0 const EQUIPMENT_UI_SCALE := 0.8 var _debug_scroll: ScrollContainer var _debug_actions: VBoxContainer var _debug_item_button: Button var _debug_item_status: Label var _captain_panel: PanelContainer var _cockpit: Control var _captain_skills_ui: CanvasLayer var _system_badge: PanelContainer var _system_title: Label var _system_mode: Label var _ship_statistics: PanelContainer func _create_navigation() -> void: var navigation := HBoxContainer.new() navigation.name = "TopNavigation" $HUDRoot.add_child(navigation) navigation.set_anchors_and_offsets_preset(Control.PRESET_CENTER_TOP) navigation.offset_left = -32 navigation.offset_right = 32 navigation.offset_top = 44 navigation.offset_bottom = 124 navigation.add_theme_constant_override("separation", 10) _captain_skills_ui = CanvasLayer.new() _captain_skills_ui.set_script(preload("res://scripts/ui/captain_skills_ui.gd")) _captain_skills_ui.set("player", player) add_child(_captain_skills_ui) var button := Button.new() button.name = "OpenCaptainSkills" button.tooltip_text = "Umiejętności kapitana" button.set_script(preload("res://scripts/ui/navigation_button.gd")) button.configure_navigation(load("res://assets/icons/ui/captain_skills_art.png"),Color("b9a0ef"),"KAPITAN") navigation.add_child(button) button.pressed.connect(func(): _captain_skills_ui.open_management()) func _ready() -> void: layer = RENDER_LAYERS.HUD var resource_layer := CanvasLayer.new() resource_layer.name = "ResourceLedger" resource_layer.layer = 70 resource_layer.process_mode = Node.PROCESS_MODE_ALWAYS add_child(resource_layer) var resource_bar := Control.new() resource_bar.set_script(preload("res://scripts/ui/resource_bar.gd")) resource_layer.add_child(resource_bar) var cursor := Node.new() cursor.name = "FlightCursor" cursor.set_script(preload("res://scripts/ui/flight_cursor.gd")) cursor.set("player", player) add_child(cursor) _sidebar_open = bool(GameState.hud_panel_state.get("ship_open", false)) _debug_expanded = bool(GameState.hud_panel_state.get("debug_expanded", false)) 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)))) _window_manager = Node.new() _window_manager.name = "FloatingWindows" _window_manager.set_script(preload("res://scripts/ui/floating_window_manager.gd")) $HUDRoot.add_child(_window_manager) _window_manager.connect("interaction_started", player.get_node("WeaponController").call.bind("block_ui_mouse_input")) _create_sidebar() _create_ship_statistics() _inventory_clip = Control.new() _inventory_clip.name = "InventoryDrawer" _inventory_clip.clip_contents = false _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) _window_manager.call("register_window", _inventory_clip, inventory_panel, inventory_panel.get_node("Contents/Header")) $HUDRoot.resized.connect(func() -> void: _layout_sidebar.call_deferred()) _create_ability_bar() var objectives := PanelContainer.new() objectives.name = "MissionObjectives" objectives.set_script(preload("res://scripts/ui/mission_objectives.gd")) objectives.set("game", get_parent()) $HUDRoot.add_child(objectives) 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, 44) _captain_panel.size = _captain_panel.get_combined_minimum_size() _captain_panel.resized.connect(_layout_debug) _create_system_badge() var captain_gear := PanelContainer.new() captain_gear.name = "CaptainEquipment" captain_gear.set_script(preload("res://scripts/ui/captain_equipment_panel.gd")) $HUDRoot.add_child(captain_gear) _captain_panel.equipment_requested.connect(captain_gear._toggle_wardrobe) _passive_captain_panel = _create_passive_panel("PassiveCaptainPanel", "KAPITAN · PASYWNE") _passive_skills = _passive_captain_panel.get_node("Contents/Icons") as GridContainer _passive_skills.name = "PassiveCaptainSkills" _passive_ship_panel = _create_passive_panel("PassiveShipPanel", "STATEK · PASYWNE") _passive_ship_icons = _passive_ship_panel.get_node("Contents/Icons") as GridContainer _passive_ship_panel.hide() GameState.captain.changed.connect(_refresh_passive_skills) _refresh_passive_skills() _debug_actions = VBoxContainer.new() _debug_actions.name = "DebugActions" _debug_actions.add_theme_constant_override("separation", 6) $HUDRoot.add_child(_debug_actions) _debug_item_button = Button.new() _debug_item_button.name = "GenerateItem" _debug_item_button.custom_minimum_size.y = 36 preload("res://scripts/ui/hud_button_style.gd").apply(_debug_item_button, Color("a9ef83")) _debug_item_button.pressed.connect(_give_debug_item) _debug_actions.add_child(_debug_item_button) var resources_button := Button.new() resources_button.name = "AddResources" resources_button.text = "Dodaj wszystko" resources_button.tooltip_text = preload("res://scripts/game/debug_rewards.gd").TOOLTIP resources_button.custom_minimum_size.y = 36 preload("res://scripts/ui/hud_button_style.gd").apply(resources_button, Color("79cba8")) resources_button.pressed.connect(_give_debug_resources) _debug_actions.add_child(resources_button) var ammunition_button := Button.new() ammunition_button.name = "AddAmmunition" ammunition_button.text = "Dodaj amunicję" ammunition_button.tooltip_text = "+10 zwykłej amunicji, +10 radiacyjnej amunicji i +10 rakiet" ammunition_button.custom_minimum_size.y = 36 preload("res://scripts/ui/hud_button_style.gd").apply(ammunition_button, Color("efb568")) ammunition_button.pressed.connect(_give_debug_ammunition) _debug_actions.add_child(ammunition_button) _debug_item_status = Label.new() _debug_item_status.add_theme_font_size_override("font_size", 10) _debug_item_status.add_theme_color_override("font_color", Color("a9ef83")) _debug_item_status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _debug_item_status.hide() _debug_actions.add_child(_debug_item_status) _debug_actions.minimum_size_changed.connect(func() -> void: _layout_debug.call_deferred()) _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.48) 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()) contents.minimum_size_changed.connect(func() -> void: _layout_sidebar.call_deferred()) _sidebar_toggle = Button.new() _sidebar_toggle.set_script(preload("res://scripts/ui/ship_profile.gd")) _sidebar_toggle.set("player", player) _sidebar_toggle.name = "ShipToggle" _sidebar_toggle.tooltip_text = "Zamknij okno statku" if _sidebar_open else "Otwórz statek i sloty wyposażenia" _sidebar_toggle.add_theme_font_size_override("font_size", 12) preload("res://scripts/ui/hud_button_style.gd").apply(_sidebar_toggle) preload("res://scripts/ui/hud_button_style.gd").apply(inventory_button, Color("b9a0ef")) $HUDRoot.add_child(_sidebar_toggle) _sidebar_toggle.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT) _sidebar_toggle.offset_left = -233 _sidebar_toggle.offset_right = -12 _sidebar_toggle.offset_top = 44 _sidebar_toggle.offset_bottom = 115.5 _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) _window_manager.call("register_window", _sidebar, _sidebar, equipment.get_node("Contents/Title")) _create_navigation() _layout_debug() func _create_passive_panel(node_name: String, caption: String) -> PanelContainer: var panel := PanelContainer.new() panel.name = node_name panel.set_script(preload("res://scripts/ui/passive_status_panel.gd")) panel.set("caption", caption) $HUDRoot.add_child(panel) return panel func _layout_passive_panels() -> void: if not is_instance_valid(_cockpit): return var width := clampf(_cockpit.position.x - 24.0, 180.0, 260.0) for panel in [_passive_captain_panel, _passive_ship_panel]: if not is_instance_valid(panel): continue var grid: GridContainer = _passive_skills if panel == _passive_captain_panel else _passive_ship_icons grid.columns = maxi(1, floori((width - 14.0) / 42.0)) panel.size = Vector2(width, panel.get_combined_minimum_size().y) var x: float = 12.0 if panel == _passive_captain_panel else $HUDRoot.size.x - width - 12.0 panel.position = Vector2(x, $HUDRoot.size.y - panel.size.y - 12.0) var reserved := _passive_ship_panel.size.y + 12.0 if _passive_ship_panel.visible else 0.0 if not is_equal_approx(reserved, _ship_passive_reserved_height): _ship_passive_reserved_height = reserved _layout_sidebar.call_deferred() 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 var objectives := $HUDRoot.get_node_or_null("MissionObjectives") as Control if is_instance_valid(objectives): top = maxf(top, objectives.position.y + objectives.size.y + 8.0) debug_toggle.position = Vector2(12, top) _debug_actions.position = Vector2(18, top + 30) _debug_actions.size = Vector2(310, _debug_actions.get_combined_minimum_size().y) _debug_actions.visible = _debug_expanded var scroll_top := _debug_actions.position.y + _debug_actions.size.y + 8.0 _debug_scroll.position = Vector2(18, scroll_top) _debug_scroll.size = Vector2(310, maxf(40, (_passive_captain_panel.position.y - 8.0 if _passive_captain_panel.visible else $HUDRoot.size.y - 12.0) - scroll_top)) _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.tooltip_text = "Zamknij okno statku" if _sidebar_open else "Otwórz statek i sloty wyposażenia" if _sidebar_open: _sidebar.show() else: close_inventory() _sidebar.visible = _sidebar_open if _sidebar_open: _window_manager.call("focus_window", _sidebar) _layout_sidebar() 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() var scroll := _sidebar.get_node("Scroll") as ScrollContainer scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED var content_height: float = scroll.get_node("Contents").get_combined_minimum_size().y + 16.0 var available_height := maxf(100.0, $HUDRoot.size.y - 180.0 - _ship_passive_reserved_height) var panel_scale := minf(EQUIPMENT_UI_SCALE, available_height / content_height) _sidebar.scale = Vector2.ONE * panel_scale _sidebar.size = Vector2(maxf(SIDEBAR_WIDTH, _sidebar.get_combined_minimum_size().x), content_height) var cockpit_scale := minf(0.85, ($HUDRoot.size.x - 24.0) / 850.0) _cockpit.scale = Vector2.ONE * cockpit_scale _cockpit.size = Vector2(850, 144) _cockpit.position = Vector2(($HUDRoot.size.x - 850.0 * cockpit_scale) * 0.5, $HUDRoot.size.y - 144.0 * cockpit_scale - 12) if _ability_bar != null: var abilities := _ability_bar.get_parent() as Control abilities.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT) abilities.position = Vector2(_cockpit.position.x + _cockpit.size.x * _cockpit.scale.x - 204.0, _cockpit.position.y - 40.0) abilities.size = Vector2(204, 36) if is_instance_valid(_captain_skills_ui): _captain_skills_ui.call_deferred("_layout") _layout_debug() if not _sidebar.has_meta("floating_window_dragged"): _sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * _sidebar.scale.x - 8, 168) else: _window_manager.call("clamp_window", _sidebar) _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 if _inventory_clip.has_meta("floating_window_dragged"): _window_manager.call("clamp_window", _inventory_clip) 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 _update_energy_bank_button() -> void: if _energy_bank_button == null: _energy_bank_button = Button.new() _energy_bank_button.name = "EnergyBankButton" _energy_bank_button.set_script(preload("res://scripts/ui/captain_skill_button.gd")) _energy_bank_button.call("configure", preload("res://assets/icons/resources/energy_bank.svg"), Color("70f5df")) _energy_bank_button.custom_minimum_size = Vector2(48, 48) _energy_bank_button.size = Vector2(48, 48) $HUDRoot.add_child(_energy_bank_button) var key := Label.new() key.text = "R" key.mouse_filter = Control.MOUSE_FILTER_IGNORE key.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT key.vertical_alignment = VERTICAL_ALIGNMENT_TOP key.add_theme_font_size_override("font_size", 11) key.add_theme_color_override("font_color", Color.WHITE) key.add_theme_color_override("font_outline_color", Color("07141e")) key.add_theme_constant_override("outline_size", 7) _energy_bank_button.add_child(key) key.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) key.offset_right = -5 key.offset_top = 2 var amount := Label.new() amount.name = "Amount" amount.mouse_filter = Control.MOUSE_FILTER_IGNORE amount.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER amount.vertical_alignment = VERTICAL_ALIGNMENT_CENTER amount.add_theme_font_size_override("font_size", 22) amount.add_theme_color_override("font_color", Color.WHITE) amount.add_theme_color_override("font_outline_color", Color("07141e")) amount.add_theme_constant_override("outline_size", 7) _energy_bank_button.add_child(amount) amount.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) _energy_bank_button.pressed.connect(func() -> void: player.call("use_energy_bank")) _energy_bank_button.position = Vector2(_cockpit.position.x + _cockpit.size.x * _cockpit.scale.x * 0.5 - 24.0, _cockpit.position.y - 56.0) var inventory := player.get_node("ShipInventory") as ShipInventory var count := inventory.get_energy_bank_count() (_energy_bank_button.get_node("Amount") as Label).text = str(count) _energy_bank_button.tooltip_text = "Bank energii: %d\nR • Przywraca %d%% maksymalnej energii" % [count, roundi(GameState.captain.get_energy_bank_fraction() * 100.0)] _energy_bank_button.disabled = count == 0 or float(player.get("energy")) >= float(player.get("max_energy")) or bool(player.get("_is_destroyed")) or bool(player.get("is_docked")) _energy_bank_button.call("set_visual_state", 0.0, false, _energy_bank_button.disabled) func _process(_delta: float) -> void: _update_energy_bank_button() _update_system_badge() _layout_inventory() _update_ability_bar() _layout_passive_panels() _layout_debug() if not _debug_expanded: return var inventory := player.get_node("ShipInventory") as ShipInventory _debug_item_button.text = "LOSUJ PRZEDMIOT · POZ. %d" % GameState.system_level _debug_item_button.disabled = inventory.get_cargo_used() >= inventory.get_cargo_capacity() _debug_item_button.tooltip_text = "Magazyn jest pełny — zwolnij miejsce." if _debug_item_button.disabled else "Losuje przedmiot i addony dla poziomu aktualnego układu." var game := get_parent() debug_info.text = """POZIOM UKŁADU: %d / 120 · %s WROGOWIE: %d PRĘDKOŚĆ: %.1f px/s AKTUALNA REGENERACJA Energia: %.1f pkt/s Tarcze: %.1f pkt/s %s""".replace("\r", "") % [ GameState.system_level, "HARD" if preload("res://scripts/levels/level_balance.gd").is_hard(GameState.system_level) else "NORMALNY", int(game.call("get_remaining_enemies")), player.velocity.length(), float(player.call("get_energy_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 _debug_actions.visible = _debug_expanded _update_debug_toggle() _layout_debug() func _give_debug_resources() -> void: var inventory := player.get_node("ShipInventory") as ShipInventory var result := preload("res://scripts/game/debug_rewards.gd").grant(GameState.captain,inventory,GameState.system_level) _debug_item_status.show() _debug_item_status.text = "Dodano pełny pakiet zasobów, amunicję, wszystkie orby i %d map.\n%d przedmiotów czeka w magazynie fregaty; dodano też sprzęt kapitana i punkty nauki." % [result.maps_added,result.equipment_added] _debug_item_status.add_theme_color_override("font_color",Color("a9ef83")) _layout_debug.call_deferred() func _give_debug_ammunition() -> void: var inventory := player.get_node("ShipInventory") as ShipInventory inventory.ammunition += 10 inventory.irradiated_ammunition += 10 inventory.missile_ammunition += 10 inventory.ammunition_changed.emit() inventory.inventory_changed.emit() func _give_debug_item() -> bool: var inventory := player.get_node("ShipInventory") as ShipInventory _debug_item_status.show() if inventory.get_cargo_used() >= inventory.get_cargo_capacity(): _debug_item_status.text = "Magazyn pełny — zwolnij miejsce." _debug_item_status.add_theme_color_override("font_color", Color("ff8989")) _layout_debug.call_deferred() return false var item := preload("res://scripts/items/item_generator.gd").generate(GameState.system_level) if not inventory.add_item(item): return false _debug_item_status.text = "Dodano: %s · Poz. %d" % [item.definition.display_name, item.item_level] _debug_item_status.add_theme_color_override("font_color", Color("a9ef83")) _layout_debug.call_deferred() return true 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%% Szczęście: %.1f%% / 60%%""".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, float(player.call("get_luck_chance")) * 100.0, ] func _create_ability_bar() -> void: 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 = -220.0 panel.offset_bottom = -176.0 panel.add_theme_stylebox_override("panel", StyleBoxEmpty.new()) _ability_bar = HBoxContainer.new() _ability_bar.name = "AbilityButtons" _ability_bar.alignment = BoxContainer.ALIGNMENT_END _ability_bar.add_theme_constant_override("separation", 6) panel.add_child(_ability_bar) for index in ShipInventory.EXTENSION_SLOT_COUNT: var button := Button.new() button.set_script(preload("res://scripts/ui/captain_skill_button.gd")) button.name = "Ability%d" % (index + 1) button.call("configure", null, Color("91dbea")) var shortcut := Label.new() shortcut.name = "Shortcut" shortcut.text = str(index + 1) shortcut.position = Vector2(4, 2) shortcut.mouse_filter = Control.MOUSE_FILTER_IGNORE shortcut.add_theme_font_size_override("font_size", 9) shortcut.add_theme_font_override("font", ThemeDB.fallback_font) shortcut.add_theme_color_override("font_outline_color", Color("030810")) shortcut.add_theme_constant_override("outline_size", 4) button.add_child(shortcut) button.add_theme_font_size_override("font_size", 11) button.pressed.connect(func() -> void: player.call("activate_ability", index)) var timer := Label.new() timer.name = "Cooldown" timer.mouse_filter = Control.MOUSE_FILTER_IGNORE timer.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER timer.vertical_alignment = VERTICAL_ALIGNMENT_CENTER timer.add_theme_font_size_override("font_size", 13) timer.add_theme_font_override("font", ThemeDB.fallback_font) timer.add_theme_color_override("font_outline_color", Color("030810")) timer.add_theme_constant_override("outline_size", 4) button.add_child(timer) timer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) _ability_bar.add_child(button) _ability_buttons[index] = button var passive := Button.new() passive.name = "PassiveSlot%d" % (index + 1) passive.set_script(preload("res://scripts/ui/captain_skill_button.gd")) passive.call("configure", null, Color("85e5b6")) passive.disabled = true passive.mouse_default_cursor_shape = Control.CURSOR_HELP _passive_ship_icons.add_child(passive) _passive_ship_buttons[index] = passive _update_ability_bar() func _update_ability_bar() -> void: var any_equipped := false var any_passive := false for index in _ability_buttons: var button: Button = _ability_buttons[index] var equipped: ItemInstance = player.get_node("ShipInventory").get_item("extension", index) var passive: Button = _passive_ship_buttons[index] passive.visible = equipped != null and equipped.is_passive_extension() if passive.visible: any_passive = true button.hide() passive.call("configure", equipped.definition.icon, Color("85e5b6")) passive.call("set_visual_state", 0.0, false, false) passive.tooltip_text = equipped.get_tooltip_text() continue (button.get_node("Shortcut") as Label).text = str(index + 1) 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.call("configure", item.definition.icon, Color("91dbea")) var cooldown := float(player.call("get_ability_cooldown", index)) button.call("set_visual_state", cooldown / maxf(0.01, ability.cooldown), false, not reason.is_empty()) (button.get_node("Cooldown") as Label).text = str(ceili(cooldown)) if cooldown > 0.0 else "" 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 _passive_ship_panel.visible = any_passive func _refresh_passive_skills() -> void: if not is_instance_valid(_passive_skills): return for child in _passive_skills.get_children(): _passive_skills.remove_child(child) child.queue_free() var catalog := preload("res://scripts/captain/captain_skills.gd") for skill in catalog.IDS: var rank := GameState.captain.get_skill_rank(skill) if rank <= 0 or bool(catalog.DATA[skill]["active"]): continue var icon := Button.new() icon.name = skill icon.set_script(preload("res://scripts/ui/captain_skill_button.gd")) icon.call("configure", catalog.icon(skill), Color(catalog.DATA[skill]["color"])) icon.disabled = true icon.mouse_default_cursor_shape = Control.CURSOR_HELP icon.tooltip_text = "%s · poziom %d / 3\n%s" % [catalog.DATA[skill]["name"], rank, catalog.description(skill, rank)] _passive_skills.add_child(icon) _passive_captain_panel.visible = _passive_skills.get_child_count() > 0 _layout_passive_panels.call_deferred() _layout_debug.call_deferred() func _create_system_badge() -> void: var ui := preload("res://scripts/planets/freighter_menu.gd") _system_badge = PanelContainer.new() _system_badge.name = "SystemIndicator" _system_badge.custom_minimum_size.x = 86 ui.style(_system_badge, 6, 0.30) _system_badge.mouse_filter = Control.MOUSE_FILTER_IGNORE $HUDRoot.add_child(_system_badge) var stack := VBoxContainer.new() stack.mouse_filter = Control.MOUSE_FILTER_IGNORE stack.add_theme_constant_override("separation", 0) _system_badge.add_child(stack) _system_title = ui.label(stack, "", 13, ui.GOLD) _system_mode = ui.label(stack, "", 11, ui.MUTED) _system_title.autowrap_mode = TextServer.AUTOWRAP_OFF _system_mode.autowrap_mode = TextServer.AUTOWRAP_OFF _system_mode.add_theme_font_size_override("font_size", 8) _system_title.mouse_filter = Control.MOUSE_FILTER_IGNORE _system_mode.mouse_filter = Control.MOUSE_FILTER_IGNORE _update_system_badge() func _update_system_badge() -> void: if not is_instance_valid(_system_badge): return _system_badge.position = Vector2(_captain_panel.position.x + _captain_panel.size.x + 10, _captain_panel.position.y) _system_title.text = ("Wyprawa %d" if not GameState.active_distant_map.is_empty() else "System %d") % GameState.system_level _system_mode.text = "HARD" if GameState.system_level > 60 else "NORMALNY" _system_mode.add_theme_color_override("font_color", Color("e88972") if GameState.system_level > 60 else Color("88a7ae")) func _create_ship_statistics() -> void: _ship_statistics = preload("res://scripts/ui/ship_statistics.gd").new() _ship_statistics.player = player _ship_statistics.inventory = player.get_node("ShipInventory") $HUDRoot.add_child(_ship_statistics) _window_manager.register_window(_ship_statistics, _ship_statistics, _ship_statistics._header) var contents := _sidebar.get_node("Scroll/Contents/ShipEquipmentPanel/Contents") var button := Button.new() button.name = "StatisticsButton" button.text = "Statystyki" button.custom_minimum_size.y = 36 button.tooltip_text = "Parametry statku, uzbrojenie, pancerz i systemy" preload("res://scripts/ui/hud_button_style.gd").apply(button, Color("83c7e6")) contents.add_child(button) contents.move_child(button, inventory_button.get_index() + 1) button.pressed.connect(_ship_statistics.open)