- Consumable-System: Tränke (HP/Mana) mit Stacking, Rechtsklick-Benutzung, Aktionsleisten-Zuweisung - Klassen-Ressourcen: ResourceType (NONE/MANA/RAGE/ENERGY) pro Klasse statt universelles Mana - Hauptmenü: Einstellungen für Auflösung, Fenstermodus, VSync, MSAA - Item-Icons: SVG-Icons für alle Equipment-Items und Tränke - Character Panel: Icon-Grid mit Hover-Tooltips statt Textanzeige - HUD: Ressourcen-Leiste mit klassenabhängiger Farbe - Loot: Consumable-Support in LootTable/LootWindow - Dokumentation vollständig aktualisiert Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
255 lines
8.1 KiB
GDScript
255 lines
8.1 KiB
GDScript
# HUD.gd
|
|
# Verwaltet die Spieler-UI: HP-Leiste, XP-Leiste, Aktionsleiste (Slots 1-9)
|
|
extends CanvasLayer
|
|
|
|
signal slot_clicked(slot_index: int)
|
|
|
|
@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 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))
|
|
action_slots[i].add_child(button)
|
|
|
|
# Slot-Klick Handler
|
|
func _on_slot_clicked(slot_index: int):
|
|
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)
|