- Zauberstab als eigener Fernkampf-Skill (20m, magisch, exklusiv mit Autoattack) - Frostblitz mit 1.5s Castzeit und Castbar (mittig über Aktionsleiste) - Cast wird durch Bewegung, Springen oder Schaden unterbrochen - Holzstab als Magier-Startwaffe (+3 INT) - Frostblitz-Icon (SVG) - Skills klassenabhängig: Magier=Zauberstab+Frostblitz, Krieger/Schurke=Heavy Strike - Inventar: Drag & Drop zum Umordnen mit gelbem Highlight - Gegner aggrot sofort bei Schadenstreffer (nicht nur in Aggro-Range) - Inventar: swap_items/move_items Funktionen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
327 lines
10 KiB
GDScript
327 lines
10 KiB
GDScript
# InventoryPanel.gd
|
|
# UI für das Spieler-Inventar (Equipment + Consumables)
|
|
extends CanvasLayer
|
|
|
|
signal item_selected(item, index: int)
|
|
|
|
var panel_visible = false
|
|
var player = null
|
|
|
|
# Drag & Drop
|
|
var dragging = false
|
|
var drag_item = null # Equipment oder Consumable
|
|
var drag_from_index: int = -1 # Inventar-Index von dem gedraggt wird
|
|
var drag_icon: TextureRect = null
|
|
var drag_highlight_slot: int = -1 # Aktuell hervorgehobener Inventar-Slot
|
|
|
|
@onready var panel = $Panel
|
|
@onready var gold_label = $Panel/VBoxContainer/Header/GoldLabel
|
|
@onready var item_grid = $Panel/VBoxContainer/ScrollContainer/ItemGrid
|
|
|
|
const SLOT_SIZE = 50
|
|
|
|
func _ready():
|
|
panel.visible = false
|
|
|
|
func setup(p):
|
|
player = p
|
|
if player and player.inventory:
|
|
player.inventory.inventory_changed.connect(_on_inventory_changed)
|
|
player.inventory.gold_changed.connect(_on_gold_changed)
|
|
_refresh_inventory()
|
|
|
|
func toggle():
|
|
panel_visible = !panel_visible
|
|
panel.visible = panel_visible
|
|
if panel_visible:
|
|
_refresh_inventory()
|
|
|
|
func _on_inventory_changed():
|
|
if panel_visible:
|
|
_refresh_inventory()
|
|
|
|
func _on_gold_changed(new_amount: int):
|
|
gold_label.text = str(new_amount) + " Gold"
|
|
|
|
func _refresh_inventory():
|
|
if player == null or player.inventory == null:
|
|
return
|
|
|
|
# Gold aktualisieren
|
|
gold_label.text = str(player.inventory.gold) + " Gold"
|
|
|
|
# Alte Slots entfernen
|
|
for child in item_grid.get_children():
|
|
child.queue_free()
|
|
|
|
# Slots erstellen (immer MAX_SLOTS anzeigen)
|
|
for i in range(Inventory.MAX_SLOTS):
|
|
var slot = _create_slot(i)
|
|
item_grid.add_child(slot)
|
|
|
|
func _create_slot(index: int) -> Panel:
|
|
var slot = Panel.new()
|
|
slot.custom_minimum_size = Vector2(SLOT_SIZE, SLOT_SIZE)
|
|
|
|
# Slot-Hintergrund stylen
|
|
var style = StyleBoxFlat.new()
|
|
style.bg_color = Color(0.15, 0.15, 0.15)
|
|
style.border_color = Color(0.3, 0.3, 0.3)
|
|
style.set_border_width_all(1)
|
|
slot.add_theme_stylebox_override("panel", style)
|
|
|
|
# Alle Slots brauchen gui_input für Drop-Erkennung
|
|
slot.gui_input.connect(_on_slot_input.bind(index))
|
|
|
|
# Item vorhanden?
|
|
if player.inventory and index < player.inventory.item_count():
|
|
var item = player.inventory.get_item(index)
|
|
if item:
|
|
var item_icon = null
|
|
if item is Consumable:
|
|
item_icon = item.icon
|
|
elif item is Equipment:
|
|
item_icon = item.icon
|
|
|
|
if item_icon:
|
|
var icon = TextureRect.new()
|
|
icon.texture = item_icon
|
|
icon.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL
|
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
icon.custom_minimum_size = Vector2(SLOT_SIZE - 4, SLOT_SIZE - 4)
|
|
icon.position = Vector2(2, 2)
|
|
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
slot.add_child(icon)
|
|
else:
|
|
# Fallback: Text
|
|
var label = Label.new()
|
|
label.text = item.item_name.substr(0, 3)
|
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
|
label.add_theme_font_size_override("font_size", 10)
|
|
if item is Equipment:
|
|
label.modulate = Equipment.get_rarity_color(item.rarity)
|
|
label.anchors_preset = Control.PRESET_FULL_RECT
|
|
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
slot.add_child(label)
|
|
|
|
# Stack-Count für Consumables
|
|
if item is Consumable and item.stack_size > 1:
|
|
var stack_label = Label.new()
|
|
stack_label.text = str(item.stack_size)
|
|
stack_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
stack_label.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
|
|
stack_label.add_theme_font_size_override("font_size", 11)
|
|
stack_label.size = Vector2(SLOT_SIZE - 4, SLOT_SIZE - 4)
|
|
stack_label.position = Vector2(0, 0)
|
|
stack_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
slot.add_child(stack_label)
|
|
|
|
# Rahmen
|
|
if item is Equipment:
|
|
style.border_color = Equipment.get_rarity_color(item.rarity)
|
|
elif item is Consumable:
|
|
style.border_color = Color(0.3, 0.7, 0.3)
|
|
style.set_border_width_all(2)
|
|
|
|
# Tooltip
|
|
slot.tooltip_text = _get_item_tooltip(item)
|
|
|
|
return slot
|
|
|
|
func _on_slot_input(event: InputEvent, index: int):
|
|
if event is InputEventMouseButton and event.pressed:
|
|
var item = null
|
|
if player.inventory and index < player.inventory.item_count():
|
|
item = player.inventory.get_item(index)
|
|
|
|
if item == null:
|
|
return
|
|
|
|
if item is Equipment:
|
|
if event.button_index == MOUSE_BUTTON_RIGHT:
|
|
# Rechtsklick auf Equipment: Anlegen
|
|
if player:
|
|
var old_item = player.equip_item(item)
|
|
player.inventory.remove_item(item)
|
|
if old_item:
|
|
player.inventory.add_item(old_item)
|
|
_refresh_inventory()
|
|
elif event.button_index == MOUSE_BUTTON_LEFT:
|
|
# Linksklick: Drag starten
|
|
_start_drag(item, index)
|
|
elif item is Consumable:
|
|
if event.button_index == MOUSE_BUTTON_RIGHT:
|
|
# Rechtsklick auf Consumable: Direkt benutzen
|
|
if player:
|
|
if player.use_consumable(item):
|
|
if item.stack_size <= 0:
|
|
player.inventory.remove_item(item)
|
|
_refresh_inventory()
|
|
player._update_action_bar_stacks()
|
|
elif event.button_index == MOUSE_BUTTON_LEFT:
|
|
# Linksklick: Drag starten
|
|
_start_drag(item, index)
|
|
|
|
# Drag & Drop System
|
|
func _start_drag(item, index: int):
|
|
dragging = true
|
|
drag_item = item
|
|
drag_from_index = index
|
|
# Icon am Mauszeiger erstellen
|
|
var tex = item.icon if item.icon else null
|
|
drag_icon = TextureRect.new()
|
|
if tex:
|
|
drag_icon.texture = tex
|
|
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
|
|
# Eigene CanvasLayer damit Icon über allem anderen liegt
|
|
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)
|
|
# Highlight auf HUD aktivieren
|
|
if player and player.hud:
|
|
player.hud.set_drag_active(true)
|
|
|
|
func _process(_delta):
|
|
if dragging and drag_icon:
|
|
drag_icon.position = get_viewport().get_mouse_position() - Vector2(20, 20)
|
|
var mouse_pos = get_viewport().get_mouse_position()
|
|
# HUD Slots highlighten
|
|
if player and player.hud:
|
|
player.hud.update_drag_hover(mouse_pos)
|
|
# Inventar-Slots highlighten
|
|
_update_inventory_hover(mouse_pos)
|
|
|
|
func _input(event):
|
|
if not dragging:
|
|
return
|
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and not event.pressed:
|
|
# Maus losgelassen - Drop prüfen
|
|
_end_drag()
|
|
|
|
func _end_drag():
|
|
if not dragging:
|
|
return
|
|
var mouse_pos = get_viewport().get_mouse_position()
|
|
|
|
# Prüfen ob über einem Inventar-Slot
|
|
var inv_slot = _get_inventory_slot_at(mouse_pos)
|
|
if inv_slot >= 0 and inv_slot != drag_from_index:
|
|
# Innerhalb Inventar verschieben/tauschen
|
|
player.inventory.move_item(drag_from_index, inv_slot)
|
|
else:
|
|
# Prüfen ob über einem Action-Slot (nur Consumables)
|
|
if player and player.hud:
|
|
var action_slot = player.hud.get_slot_at_position(mouse_pos)
|
|
if action_slot >= 0 and action_slot <= 8 and drag_item:
|
|
if drag_item is Consumable:
|
|
player.assign_to_action_bar(action_slot, drag_item)
|
|
print(drag_item.item_name + " auf Aktionsleiste Slot " + str(action_slot + 1) + " gelegt")
|
|
|
|
# HUD Highlight deaktivieren
|
|
if player and player.hud:
|
|
player.hud.set_drag_active(false)
|
|
# Inventar Highlight entfernen
|
|
_clear_inventory_highlight()
|
|
# Aufräumen
|
|
if drag_icon:
|
|
var drag_layer = drag_icon.get_parent()
|
|
drag_layer.queue_free()
|
|
drag_icon = null
|
|
dragging = false
|
|
drag_item = null
|
|
drag_from_index = -1
|
|
|
|
# Inventar-Slot unter Mausposition finden
|
|
func _get_inventory_slot_at(mouse_pos: Vector2) -> int:
|
|
for i in range(item_grid.get_child_count()):
|
|
var slot = item_grid.get_child(i)
|
|
var rect = slot.get_global_rect()
|
|
if rect.has_point(mouse_pos):
|
|
return i
|
|
return -1
|
|
|
|
# Inventar-Slot Highlight während Drag
|
|
func _update_inventory_hover(mouse_pos: Vector2):
|
|
var hovered = _get_inventory_slot_at(mouse_pos)
|
|
if hovered == drag_highlight_slot:
|
|
return
|
|
_clear_inventory_highlight()
|
|
if hovered >= 0 and hovered != drag_from_index:
|
|
drag_highlight_slot = hovered
|
|
var slot = item_grid.get_child(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)
|
|
slot.add_theme_stylebox_override("panel", style)
|
|
|
|
func _clear_inventory_highlight():
|
|
if drag_highlight_slot >= 0 and drag_highlight_slot < item_grid.get_child_count():
|
|
# Style zurücksetzen
|
|
var slot = item_grid.get_child(drag_highlight_slot)
|
|
var item = null
|
|
if player.inventory and drag_highlight_slot < player.inventory.item_count():
|
|
item = player.inventory.get_item(drag_highlight_slot)
|
|
var style = StyleBoxFlat.new()
|
|
style.bg_color = Color(0.15, 0.15, 0.15)
|
|
if item is Equipment:
|
|
style.border_color = Equipment.get_rarity_color(item.rarity)
|
|
style.set_border_width_all(2)
|
|
elif item is Consumable:
|
|
style.border_color = Color(0.3, 0.7, 0.3)
|
|
style.set_border_width_all(2)
|
|
else:
|
|
style.border_color = Color(0.3, 0.3, 0.3)
|
|
style.set_border_width_all(1)
|
|
slot.add_theme_stylebox_override("panel", style)
|
|
drag_highlight_slot = -1
|
|
|
|
func _get_item_tooltip(item) -> String:
|
|
if item is Consumable:
|
|
return _get_consumable_tooltip(item)
|
|
elif item is Equipment:
|
|
return _get_equipment_tooltip(item)
|
|
return item.item_name
|
|
|
|
func _get_consumable_tooltip(item: Consumable) -> String:
|
|
var tooltip = item.item_name + "\n"
|
|
tooltip += "Verbrauchbar\n"
|
|
tooltip += "---\n"
|
|
tooltip += item.get_effect_text() + "\n"
|
|
tooltip += "Cooldown: " + str(item.cooldown) + "s\n"
|
|
tooltip += "Anzahl: " + str(item.stack_size) + "/" + str(item.max_stack) + "\n"
|
|
tooltip += "\n[Rechtsklick: Benutzen]\n[Linksklick: Ziehen]"
|
|
return tooltip
|
|
|
|
func _get_equipment_tooltip(item: Equipment) -> String:
|
|
var tooltip = item.item_name + "\n"
|
|
tooltip += Equipment.get_slot_name(item.slot) + "\n"
|
|
tooltip += "---\n"
|
|
|
|
if item.slot == Equipment.Slot.WEAPON:
|
|
tooltip += "Schaden: " + str(item.min_damage) + "-" + str(item.max_damage) + "\n"
|
|
tooltip += "Tempo: " + str(item.attack_speed) + "s\n"
|
|
|
|
if item.armor > 0:
|
|
tooltip += "Rüstung: +" + str(item.armor) + "\n"
|
|
if item.strength > 0:
|
|
tooltip += "Stärke: +" + str(item.strength) + "\n"
|
|
if item.agility > 0:
|
|
tooltip += "Beweglichkeit: +" + str(item.agility) + "\n"
|
|
if item.intelligence > 0:
|
|
tooltip += "Intelligenz: +" + str(item.intelligence) + "\n"
|
|
if item.stamina > 0:
|
|
tooltip += "Ausdauer: +" + str(item.stamina) + "\n"
|
|
if item.haste > 0:
|
|
tooltip += "Tempo: +" + str(int(item.haste * 100)) + "%\n"
|
|
|
|
tooltip += "\n[Rechtsklick zum Anlegen]"
|
|
return tooltip
|