# HUD.gd # Verwaltet die Spieler-UI: HP-Leiste, XP-Leiste, Aktionsleiste (Slots 1-9) extends CanvasLayer signal slot_clicked(slot_index: int) signal slot_drag_removed(slot_index: int) signal slot_drag_swapped(from_slot: int, to_slot: int) # Drag & Drop State var drag_active = false var drag_highlight_slot = -1 var drag_from_slot = -1 # Slot von dem aus gedraggt wird var drag_icon: TextureRect = null var drag_item = null # Das gedraggte Consumable @onready var health_bar = $Control/HealthBar @onready var health_label = $Control/HealthBar/HealthLabel # Level/XP UI (wird dynamisch erstellt) var level_label: Label var xp_bar: ProgressBar var gold_label: Label @onready var action_slots = [ $Control/ActionBar/A1, $Control/ActionBar/A2, $Control/ActionBar/A3, $Control/ActionBar/A4, $Control/ActionBar/A5, $Control/ActionBar/A6, $Control/ActionBar/A7, $Control/ActionBar/A8, $Control/ActionBar/A9 ] var active_slot = 0 var slot_icons = [] # TextureRect nodes für Icons var slot_cooldown_overlays = [] # ColorRect für Cooldown-Anzeige var slot_cooldown_labels = [] # Label für Cooldown-Text var slot_stack_labels = [] # Label für Stack-Anzahl # Ressourcen-Bar (Mana/Energie/Wut) var resource_bar: ProgressBar var resource_label: Label func _ready(): _create_level_ui() for i in range(9): # Icon erstellen var icon = TextureRect.new() icon.name = "Icon" icon.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED icon.custom_minimum_size = Vector2(40, 40) icon.position = Vector2(5, 5) action_slots[i].add_child(icon) slot_icons.append(icon) # Cooldown-Overlay erstellen (dunkle Überlagerung) var cooldown_overlay = ColorRect.new() cooldown_overlay.name = "CooldownOverlay" cooldown_overlay.color = Color(0, 0, 0, 0.7) cooldown_overlay.size = Vector2(50, 50) cooldown_overlay.position = Vector2(0, 0) cooldown_overlay.visible = false cooldown_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE action_slots[i].add_child(cooldown_overlay) slot_cooldown_overlays.append(cooldown_overlay) # Cooldown-Text erstellen var cooldown_label = Label.new() cooldown_label.name = "CooldownLabel" cooldown_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER cooldown_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER cooldown_label.size = Vector2(50, 50) cooldown_label.position = Vector2(0, 0) cooldown_label.add_theme_font_size_override("font_size", 16) cooldown_label.visible = false cooldown_label.mouse_filter = Control.MOUSE_FILTER_IGNORE action_slots[i].add_child(cooldown_label) slot_cooldown_labels.append(cooldown_label) # Stack-Count Label (unten rechts) var stack_label = Label.new() stack_label.name = "StackLabel" stack_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT stack_label.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM stack_label.size = Vector2(50, 50) stack_label.position = Vector2(-4, -2) stack_label.add_theme_font_size_override("font_size", 11) stack_label.visible = false stack_label.mouse_filter = Control.MOUSE_FILTER_IGNORE action_slots[i].add_child(stack_label) slot_stack_labels.append(stack_label) # Button für Klicks und Drag erstellen var button = Button.new() button.name = "SlotButton" button.flat = true button.size = Vector2(50, 50) button.position = Vector2(0, 0) button.modulate = Color(1, 1, 1, 0) # Unsichtbar var slot_index = i button.pressed.connect(func(): _on_slot_clicked(slot_index)) button.button_down.connect(func(): _on_slot_drag_start(slot_index)) action_slots[i].add_child(button) # Drag aus Aktionsleiste starten func _on_slot_drag_start(slot_index: int): # Prüfe ob ein Consumable im Slot liegt (wird vom Player gesetzt) # Signal an Player senden um Item abzufragen _start_actionbar_drag(slot_index) func _start_actionbar_drag(slot_index: int): drag_from_slot = slot_index drag_active = true # Icon am Cursor erstellen aus dem aktuellen Slot-Icon var current_texture = slot_icons[slot_index].texture if current_texture == null: drag_active = false drag_from_slot = -1 return drag_icon = TextureRect.new() drag_icon.texture = current_texture drag_icon.custom_minimum_size = Vector2(40, 40) drag_icon.size = Vector2(40, 40) drag_icon.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL drag_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED drag_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE var drag_layer = CanvasLayer.new() drag_layer.name = "DragLayer" drag_layer.layer = 200 drag_layer.add_child(drag_icon) get_tree().root.add_child(drag_layer) drag_icon.position = get_viewport().get_mouse_position() - Vector2(20, 20) func _process(_delta): if drag_from_slot >= 0 and drag_icon: drag_icon.position = get_viewport().get_mouse_position() - Vector2(20, 20) update_drag_hover(get_viewport().get_mouse_position()) func _input(event): if not drag_active or drag_from_slot < 0: return if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and not event.pressed: _end_actionbar_drag() func _end_actionbar_drag(): var drop_slot = get_slot_at_position(get_viewport().get_mouse_position()) if drop_slot >= 2 and drop_slot <= 8 and drop_slot != drag_from_slot: # Auf anderen Slot verschoben -> swap slot_drag_swapped.emit(drag_from_slot, drop_slot) elif drop_slot < 0 or drop_slot < 2 or drop_slot > 8: # Außerhalb gedroppt -> aus Leiste entfernen slot_drag_removed.emit(drag_from_slot) # Aufräumen _clear_drag_highlight() if drag_icon: var drag_layer = drag_icon.get_parent() drag_layer.queue_free() drag_icon = null drag_active = false drag_from_slot = -1 # Slot-Klick Handler func _on_slot_clicked(slot_index: int): if drag_active: return # Während Drag keine Klicks set_active_slot(slot_index) slot_clicked.emit(slot_index) # Icon für einen Slot setzen func set_slot_icon(slot_index: int, icon_path: String): if slot_index >= 0 and slot_index < 9: var texture = load(icon_path) if texture: slot_icons[slot_index].texture = texture else: print("Icon nicht gefunden: ", icon_path) # Cooldown für einen Slot anzeigen (remaining_time in Sekunden) func set_slot_cooldown(slot_index: int, remaining_time: float): if slot_index < 0 or slot_index >= 9: return if remaining_time > 0: slot_cooldown_overlays[slot_index].visible = true slot_cooldown_labels[slot_index].visible = true slot_cooldown_labels[slot_index].text = "%.1f" % remaining_time else: slot_cooldown_overlays[slot_index].visible = false slot_cooldown_labels[slot_index].visible = false # Level/XP UI erstellen func _create_level_ui(): var control = $Control # Level Label level_label = Label.new() level_label.name = "LevelLabel" level_label.position = Vector2(20, 55) level_label.add_theme_font_size_override("font_size", 14) level_label.text = "Level 1" control.add_child(level_label) # XP Bar xp_bar = ProgressBar.new() xp_bar.name = "XPBar" xp_bar.position = Vector2(80, 55) xp_bar.size = Vector2(140, 18) xp_bar.show_percentage = false xp_bar.value = 0 # XP Bar Farbe (blau) var xp_style = StyleBoxFlat.new() xp_style.bg_color = Color(0.2, 0.4, 0.9, 1.0) xp_bar.add_theme_stylebox_override("fill", xp_style) control.add_child(xp_bar) # Ressourcen-Bar (Mana/Energie/Wut) - unter HP-Bar resource_bar = ProgressBar.new() resource_bar.name = "ResourceBar" resource_bar.position = Vector2(20, 50) resource_bar.size = Vector2(200, 20) resource_bar.show_percentage = false resource_bar.value = 0 resource_bar.visible = false # Nur sichtbar wenn Klasse Ressource hat var resource_style = StyleBoxFlat.new() resource_style.bg_color = Color(0.2, 0.3, 0.9, 1.0) # Blau für Mana (Standard) resource_bar.add_theme_stylebox_override("fill", resource_style) resource_label = Label.new() resource_label.name = "ResourceLabel" resource_label.size = Vector2(200, 20) resource_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER resource_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER resource_bar.add_child(resource_label) control.add_child(resource_bar) # Level/XP etwas runter verschieben wegen Ressourcen-Bar level_label.position = Vector2(20, 75) xp_bar.position = Vector2(80, 75) # Gold Label gold_label = Label.new() gold_label.name = "GoldLabel" gold_label.position = Vector2(20, 98) gold_label.add_theme_font_size_override("font_size", 14) gold_label.add_theme_color_override("font_color", Color(1, 0.85, 0, 1)) gold_label.text = "0 Gold" control.add_child(gold_label) # Gold aktualisieren func update_gold(amount: int): if gold_label: gold_label.text = str(amount) + " Gold" # Ressourcen-Leiste aktualisieren (Mana/Energie/Wut) func update_resource(current: int, maximum: int, resource_name: String): if resource_bar == null: return if maximum <= 0: resource_bar.visible = false return resource_bar.visible = true resource_bar.max_value = maximum resource_bar.value = current resource_label.text = str(current) + " / " + str(maximum) # Farbe je nach Ressourcen-Typ var style = resource_bar.get_theme_stylebox("fill") as StyleBoxFlat if style: match resource_name: "Mana": style.bg_color = Color(0.2, 0.3, 0.9, 1.0) # Blau "Energie": style.bg_color = Color(0.9, 0.8, 0.1, 1.0) # Gelb "Wut": style.bg_color = Color(0.8, 0.15, 0.1, 1.0) # Rot # Icon-Textur direkt setzen (für Consumables) func set_slot_icon_texture(slot_index: int, texture: Texture2D): if slot_index >= 0 and slot_index < 9: slot_icons[slot_index].texture = texture # Slot-Icon entfernen func clear_slot_icon(slot_index: int): if slot_index >= 0 and slot_index < 9: slot_icons[slot_index].texture = null # Stack-Anzahl auf Slot anzeigen func set_slot_stack_count(slot_index: int, count: int): if slot_index < 0 or slot_index >= 9: return if count > 1: slot_stack_labels[slot_index].text = str(count) slot_stack_labels[slot_index].visible = true else: slot_stack_labels[slot_index].visible = false # HP-Leiste und Text aktualisieren func update_health(current_hp, max_hp): health_bar.max_value = max_hp health_bar.value = current_hp health_label.text = str(current_hp) + " / " + str(max_hp) # Level und XP aktualisieren func update_level(level: int, current_xp: int, xp_to_next: int): if level_label: level_label.text = "Lv " + str(level) if xp_bar: xp_bar.max_value = xp_to_next xp_bar.value = current_xp # Aktions-Slot kurz golden hervorheben (0.1s) func set_active_slot(index): action_slots[active_slot].self_modulate = Color(1, 1, 1) active_slot = index action_slots[active_slot].self_modulate = Color(1, 0.8, 0) await get_tree().create_timer(0.1).timeout action_slots[active_slot].self_modulate = Color(1, 1, 1) # Drag & Drop: Highlight aktivieren/deaktivieren func set_drag_active(active: bool): drag_active = active if not active: # Alle Highlights entfernen _clear_drag_highlight() # Drag & Drop: Hover über Slots prüfen und gelben Rand setzen func update_drag_hover(mouse_pos: Vector2): if not drag_active: return var hovered = get_slot_at_position(mouse_pos) if hovered == drag_highlight_slot: return # Keine Änderung # Alten Highlight entfernen _clear_drag_highlight() # Neuen Highlight setzen (nur Slots 2-8) if hovered >= 2 and hovered <= 8: drag_highlight_slot = hovered var style = StyleBoxFlat.new() style.bg_color = Color(0.15, 0.15, 0.15) style.border_color = Color(1, 0.9, 0, 1) # Gelber Rand style.set_border_width_all(3) action_slots[hovered].add_theme_stylebox_override("panel", style) func _clear_drag_highlight(): if drag_highlight_slot >= 0 and drag_highlight_slot < 9: action_slots[drag_highlight_slot].remove_theme_stylebox_override("panel") drag_highlight_slot = -1 # Gibt den Slot-Index zurück wenn mouse_pos über einem Action-Slot liegt, sonst -1 func get_slot_at_position(mouse_pos: Vector2) -> int: for i in range(9): var slot = action_slots[i] var rect = slot.get_global_rect() if rect.has_point(mouse_pos): return i return -1