204 lines
8.4 KiB
GDScript
204 lines
8.4 KiB
GDScript
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
|