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 func _ready() -> void: 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", 9) # Establish anchor layout before applying offsets; absolute layout resets anchors. var equipment := $HUDRoot/ShipEquipmentPanel as Control equipment.set("layout_mode", 1) equipment.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT) equipment.offset_left = -270.0 equipment.offset_right = -10.0 inventory_panel.set("layout_mode", 1) inventory_panel.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM) inventory_panel.grow_vertical = Control.GROW_DIRECTION_BEGIN inventory_panel.offset_left = -390.0 inventory_panel.offset_right = 390.0 inventory_panel.offset_top = -330.0 inventory_panel.offset_bottom = -62.0 _create_ability_bar() debug_toggle.pressed.connect(_toggle_debug) inventory_button.pressed.connect(_toggle_inventory) _update_debug_toggle() $HUDRoot/CaptainPanel.resized.connect(func() -> void: _layout_sidebar.call_deferred()) _layout_sidebar.call_deferred() func _layout_sidebar() -> void: var captain_panel := $HUDRoot/CaptainPanel as Control var equipment_panel := $HUDRoot/ShipEquipmentPanel as Control captain_panel.grow_vertical = Control.GROW_DIRECTION_END equipment_panel.grow_vertical = Control.GROW_DIRECTION_END equipment_panel.position.y = captain_panel.position.y + captain_panel.size.y + 6.0 equipment_panel.size.y = equipment_panel.get_combined_minimum_size().y func _process(_delta: float) -> void: _update_ability_bar() if not _debug_expanded: return var ship_position := player.global_position var ship_velocity := player.velocity var angular_speed := float(player.get("angular_velocity")) var game := get_parent() var enemy_count: int = game.call("get_remaining_enemies") var goal: Node2D = game.get("protected_planet") var to_planet := goal.global_position - player.global_position debug_info.text = """STEROWANIE W: ciąg S: hamulec / wsteczny Q / E: strafe A / D: obrót LPM / PPM: broń 1 / broń 2 1–6: aktywne rozszerzenia UKŁAD: %d · PLANETY: %d WROGOWIE: %d CEL: %s PLANETA RASY: %.0f px · %.0f° STATEK KADŁUB: %d / %d TARCZE: %d / %d ENERGIA: %d / %d REGEN E / T: %.1f / %.1f /s RUCH PRĘDKOŚĆ: %.1f px/s WEKTOR: [%.1f, %.1f] px/s POZYCJA: [%.1f, %.1f] KIERUNEK: %.1f° OBRÓT: %.2f rad/s""" % [ GameState.system_level, int(game.get("system_data")["planets"].size()), enemy_count, "Odwiedź chronioną rasę" if bool(game.get("system_cleared")) else "Oczyść układ", to_planet.length(), wrapf(rad_to_deg(to_planet.angle()) + 90.0, 0.0, 360.0), roundi(float(player.get("hull"))), roundi(float(player.get("max_hull"))), roundi(float(player.get("shields"))), roundi(float(player.get("max_shields"))), roundi(float(player.get("energy"))), roundi(float(player.get("max_energy"))), float(player.get("energy_regeneration")) + float(player.call("get_solar_regeneration")), float(player.get("shield_regeneration")), ship_velocity.length(), ship_velocity.x, ship_velocity.y, ship_position.x, ship_position.y, rad_to_deg(player.rotation), angular_speed, ] debug_info.text += "\n\n" + _get_bonus_summary() func _toggle_debug() -> void: _debug_expanded = not _debug_expanded debug_info.visible = _debug_expanded _update_debug_toggle() func _update_debug_toggle() -> void: debug_toggle.text = "DEBUG [−]" if _debug_expanded else "DEBUG [+]" func _toggle_inventory() -> void: inventory_panel.visible = not inventory_panel.visible func _get_bonus_summary() -> String: # Read final ship values so gear and captain multipliers match actual combat. return """BONUSY ŁĄCZNE (MODUŁY + KAPITAN) Obrażenia broni: %+.1f%% (×%.3f) Szansa na krytyk: %.1f%% Obrażenia krytyczne: ×%.2f (%+.1f%% bonusu) Szansa na unik: %.1f%% Redukcja obrażeń: %.1f%% Regeneracja energii: %.1f pkt/s (%+.1f) Ładowanie solarne: +%.1f pkt/s Regeneracja tarcz: %.1f pkt/s (%+.1f) Maks. kadłub: %.1f (%+.1f pkt) Maks. tarcze: %.1f (%+.1f pkt) Maks. energia: %.1f (%+.1f pkt) Maks. prędkość: %.1f px/s (%+.1f%%) Zdobywane XP: %+.1f%% Zdobywane kredyty: %+.1f%%""" % [ (float(player.call("get_weapon_damage_multiplier")) - 1.0) * 100.0, float(player.call("get_weapon_damage_multiplier")), float(player.call("get_critical_chance")) * 100.0, float(player.call("get_critical_multiplier")), 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("energy_regeneration")), float(player.get("energy_regeneration")) - float(player.get("base_energy_regeneration")), float(player.call("get_solar_regeneration")), float(player.get("shield_regeneration")), float(player.get("shield_regeneration")) - float(player.get("base_shield_regeneration")), float(player.get("max_hull")), float(player.get("max_hull")) - float(player.get("base_max_hull")), float(player.get("max_shields")), float(player.get("max_shields")) - float(player.get("base_max_shields")), float(player.get("max_energy")), float(player.get("max_energy")) - float(player.get("base_max_energy")), float(player.get("max_speed")), (float(player.get("max_speed")) / float(player.get("base_max_speed")) - 1.0) * 100.0, (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_BOTTOM_LEFT) panel.offset_left = 12.0 panel.offset_right = 12.0 panel.offset_top = -54.0 panel.offset_bottom = -12.0 _ability_bar = HBoxContainer.new() _ability_bar.add_theme_constant_override("separation", 4) panel.add_child(_ability_bar) for index in 6: var button := Button.new() button.name = "Ability%d" % (index + 1) button.custom_minimum_size = Vector2(120, 40) 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() button.text = "[%d] %s\n%s" % [index + 1, ability.display_name, "GOTOWE" if reason.is_empty() else reason] 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 = 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