diff --git a/PROJEKTDOKU.md b/PROJEKTDOKU.md index cfcf0d4..f7178c1 100644 --- a/PROJEKTDOKU.md +++ b/PROJEKTDOKU.md @@ -23,11 +23,10 @@ Gegner bekämpfen und ihre Charaktere mit verschiedenen Klassen und Ausrüstunge | RMB gehalten | Kamera drehen, Spieler schaut mit | | Linksklick auf Gegner | Ziel markieren | | Rechtsklick auf Gegner | Ziel markieren + Autoattack starten | -| 1 | Autoattack manuell starten | -| 2 | Heavy Strike (starke Attacke) | -| 3 – 9 | Aktionsleiste Slots (Consumables/Items) | +| 1 – 9 | Aktionsleiste Slots (Skills + Consumables, frei belegbar) | | C | Charakter-Panel (Stats + Equipment) | | I | Inventar öffnen/schließen | +| P | Fähigkeiten-Panel (alle Skills, Drag auf Aktionsleiste) | | Leertaste | Springen | | T | (Test) 10 Schaden am Spieler | @@ -306,10 +305,12 @@ Beispiel: Waffe mit 1.5s + 50% Haste → `1.5 / 1.5 = 1.0s` - Aktiviert automatisch Autoattack danach ### UI & Icons -- Aktionsleiste mit 9 Slots (Taste 1-9) +- Aktionsleiste mit 9 Slots (Taste 1-9), frei belegbar mit Skills und Consumables +- Drag & Drop: Skills/Tränke zwischen Slots verschieben, aus Leiste rausziehen zum Entfernen +- Fähigkeiten-Panel (P-Taste): Listet alle verfügbaren Skills, per Drag auf Aktionsleiste ziehen - Icons werden beim Spielstart geladen - Cooldown-Anzeige: Dunkle Überlagerung + verbleibende Zeit -- Alle Slots per Maus klickbar +- Gelber Highlight-Rand beim Drag über Slots --- @@ -424,6 +425,8 @@ DungeonCrawler/ ├── main_menu.tscn # Hauptmenü Scene ├── player.gd # Spieler-Script (inkl. Ressourcen, Aktionsleiste) ├── player.tscn # Spieler-Scene +├── skill_panel.gd # Fähigkeiten-Panel Script +├── skill_panel.tscn # Fähigkeiten-Panel Scene ├── world.gd # Welt-Script ├── world.tscn # Hauptszene └── PROJEKTDOKU.md # Diese Dokumentation diff --git a/hud.gd b/hud.gd index ee4aa1c..cd8afa2 100644 --- a/hud.gd +++ b/hud.gd @@ -3,6 +3,15 @@ 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 @@ -84,7 +93,7 @@ func _ready(): action_slots[i].add_child(stack_label) slot_stack_labels.append(stack_label) - # Button für Klicks erstellen + # Button für Klicks und Drag erstellen var button = Button.new() button.name = "SlotButton" button.flat = true @@ -93,10 +102,70 @@ func _ready(): 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) @@ -253,3 +322,42 @@ func set_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 diff --git a/inventory_panel.gd b/inventory_panel.gd index 480531f..fc31462 100644 --- a/inventory_panel.gd +++ b/inventory_panel.gd @@ -7,6 +7,11 @@ signal item_selected(item, index: int) var panel_visible = false var player = null +# Drag & Drop +var dragging = false +var drag_item: Consumable = null +var drag_icon: TextureRect = null + @onready var panel = $Panel @onready var gold_label = $Panel/VBoxContainer/Header/GoldLabel @onready var item_grid = $Panel/VBoxContainer/ScrollContainer/ItemGrid @@ -139,21 +144,64 @@ func _on_slot_clicked(event: InputEvent, index: int, item): player.inventory.remove_item(item) _refresh_inventory() player._update_action_bar_stacks() - elif event.button_index == MOUSE_BUTTON_LEFT and event.shift_pressed: - # Shift+Linksklick: Auf nächsten freien Aktionsleisten-Slot legen - if player: - _assign_consumable_to_bar(item) + elif event.button_index == MOUSE_BUTTON_LEFT: + # Linksklick: Drag starten + _start_drag(item) -func _assign_consumable_to_bar(item: Consumable): - # Nächsten freien Slot ab 2 finden - for i in range(2, 9): - if player.action_bar_items[i] == null: - player.assign_to_action_bar(i, item) - print(item.item_name + " auf Slot " + str(i + 1) + " gelegt") - return - # Kein freier Slot - auf Slot 2 legen - player.assign_to_action_bar(2, item) - print(item.item_name + " auf Slot 3 gelegt (überschrieben)") +# Drag & Drop System +func _start_drag(item: Consumable): + dragging = true + drag_item = item + # Icon am Mauszeiger erstellen + drag_icon = TextureRect.new() + drag_icon.texture = item.icon + 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) + # HUD Slots highlighten + if player and player.hud: + player.hud.update_drag_hover(get_viewport().get_mouse_position()) + +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 + # Prüfen ob über einem Action-Slot + if player and player.hud: + var slot_index = player.hud.get_slot_at_position(get_viewport().get_mouse_position()) + if slot_index >= 2 and slot_index <= 8 and drag_item: + player.assign_to_action_bar(slot_index, drag_item) + print(drag_item.item_name + " auf Slot " + str(slot_index + 1) + " gelegt") + player.hud.set_drag_active(false) + # Aufräumen + if drag_icon: + var drag_layer = drag_icon.get_parent() + drag_layer.queue_free() # Entfernt DragLayer + Icon + drag_icon = null + dragging = false + drag_item = null func _get_item_tooltip(item) -> String: if item is Consumable: diff --git a/loot_window.gd b/loot_window.gd index 9497200..f3decf2 100644 --- a/loot_window.gd +++ b/loot_window.gd @@ -50,7 +50,7 @@ func _refresh_display(): for i in range(items.size()): var item = items[i] var hbox = HBoxContainer.new() - hbox.theme_override_constants = {} + hbox.add_theme_constant_override("separation", 4) # Icon wenn vorhanden if item.icon: @@ -115,7 +115,10 @@ func _on_loot_all(): _refresh_display() -func _get_item_tooltip(item: Equipment) -> String: +func _get_item_tooltip(item) -> String: + if item is Consumable: + return item.item_name + "\n" + item.get_effect_text() + var tooltip = item.item_name + "\n" tooltip += Equipment.get_slot_name(item.slot) + "\n" diff --git a/player.gd b/player.gd index 143296e..62db527 100644 --- a/player.gd +++ b/player.gd @@ -29,9 +29,15 @@ var max_resource = 0 # Klassen-Ressource (Mana/Energie/Wut), 0 = keine var current_resource = 0 var target = null # Aktuell markierter Gegner -# Aktionsleiste: Items/Consumables zugewiesen zu Slots (0-8) -# Slot 0 = Autoattack, Slot 1 = Heavy Strike, Rest frei für Items -var action_bar_items: Array = [null, null, null, null, null, null, null, null, null] +# Aktionsleiste: Skills (String) oder Consumables in Slots (0-8) +# Skills: "autoattack", "heavy_strike" — frei verschiebbar +var action_bar_items: Array = ["autoattack", "heavy_strike", null, null, null, null, null, null, null] + +# Alle verfügbaren Skills (für Fähigkeiten-Panel) +var available_skills: Array = [ + {"id": "autoattack", "name": "Autoattack", "icon": "res://icons/autoattack_icon.svg", "description": "Greift das Ziel automatisch an.\nSchaden: Waffenschaden + Main-Stat"}, + {"id": "heavy_strike", "name": "Heavy Strike", "icon": "res://icons/heavy_strike_icon.svg", "description": "Starker Hieb mit 3s Cooldown.\nSchaden: 10-15 + Main-Stat"}, +] var potion_cooldown: float = 0.0 const POTION_COOLDOWN_TIME = 1.0 @@ -70,6 +76,7 @@ const HEAVY_STRIKE_RANGE = 4.0 @onready var character_panel = $CharacterPanel @onready var inventory_panel = $InventoryPanel @onready var loot_window = $LootWindow +@onready var skill_panel = $SkillPanel func _ready(): # Stats aus Klasse berechnen @@ -81,12 +88,14 @@ func _ready(): hud.update_resource(current_resource, max_resource, get_resource_name()) hud.update_level(level, current_xp, xp_to_next_level) hud.set_active_slot(0) - # Icons für Skills setzen - hud.set_slot_icon(0, "res://icons/autoattack_icon.svg") # Slot 1: Autoattack - hud.set_slot_icon(1, "res://icons/heavy_strike_icon.svg") # Slot 2: Heavy Strike + # Aktionsleiste initialisieren (Skills + Items) + for i in range(9): + _refresh_action_slot(i) - # HUD-Klicks verbinden + # HUD-Klicks und Drag verbinden hud.slot_clicked.connect(_on_slot_clicked) + hud.slot_drag_removed.connect(_on_slot_drag_removed) + hud.slot_drag_swapped.connect(_on_slot_drag_swapped) # Inventar Panel initialisieren inventory_panel.setup(self) @@ -94,6 +103,9 @@ func _ready(): # Loot Window initialisieren loot_window.setup(self) + # Skill Panel initialisieren + skill_panel.setup(self) + # Gold im HUD aktualisieren wenn sich Gold ändert inventory.gold_changed.connect(func(amount): hud.update_gold(amount)) @@ -257,22 +269,80 @@ func _calculate_xp_for_level(target_level: int) -> int: func _on_slot_clicked(slot_index: int): _use_action_slot(slot_index) +# Slot aus Aktionsleiste entfernen (rausgezogen) - Item/Skill bleibt verfügbar +func _on_slot_drag_removed(slot_index: int): + action_bar_items[slot_index] = null + hud.clear_slot_icon(slot_index) + hud.set_slot_stack_count(slot_index, 0) + print("Slot " + str(slot_index + 1) + " geleert") + +# Zwei Slots tauschen +func _on_slot_drag_swapped(from_slot: int, to_slot: int): + var temp = action_bar_items[from_slot] + action_bar_items[from_slot] = action_bar_items[to_slot] + action_bar_items[to_slot] = temp + _refresh_action_slot(from_slot) + _refresh_action_slot(to_slot) + +# Skill per ID auf Slot legen +func assign_skill_to_action_bar(slot_index: int, skill_id: String): + action_bar_items[slot_index] = skill_id + _refresh_action_slot(slot_index) + print(skill_id + " auf Slot " + str(slot_index + 1) + " gelegt") + +# Skill-Info anhand ID holen +func get_skill_info(skill_id: String) -> Dictionary: + for skill in available_skills: + if skill["id"] == skill_id: + return skill + return {} + +# Cooldown für einen Slot ermitteln +func _get_slot_cooldown(slot_index: int) -> float: + var entry = action_bar_items[slot_index] + if entry is String: + match entry: + "autoattack": + return global_cooldown + "heavy_strike": + return heavy_strike_cooldown + elif entry is Consumable: + return potion_cooldown + return 0.0 + +func _refresh_action_slot(slot_index: int): + var entry = action_bar_items[slot_index] + if entry is String: + # Skill + var info = get_skill_info(entry) + if info.size() > 0: + hud.set_slot_icon(slot_index, info["icon"]) + else: + hud.clear_slot_icon(slot_index) + hud.set_slot_stack_count(slot_index, 0) + elif entry is Consumable and entry.icon: + hud.set_slot_icon_texture(slot_index, entry.icon) + hud.set_slot_stack_count(slot_index, entry.stack_size) + else: + hud.clear_slot_icon(slot_index) + hud.set_slot_stack_count(slot_index, 0) + func _use_action_slot(slot_index: int): - match slot_index: - 0: # Autoattack manuell starten - if target != null and global_cooldown <= 0: - start_autoattack() - perform_autoattack() - 1: # Heavy Strike - use_heavy_strike() - _: # Slots 2-8: Consumables - var item = action_bar_items[slot_index] - if item is Consumable: - if use_consumable(item): - # Item aus Inventar entfernen wenn Stack leer - if item.stack_size <= 0: - inventory.remove_item(item) - _update_action_bar_stacks() + var entry = action_bar_items[slot_index] + if entry is String: + # Skill ausführen + match entry: + "autoattack": + if target != null and global_cooldown <= 0: + start_autoattack() + perform_autoattack() + "heavy_strike": + use_heavy_strike() + elif entry is Consumable: + if use_consumable(entry): + if entry.stack_size <= 0: + inventory.remove_item(entry) + _update_action_bar_stacks() # Schaden am Spieler abziehen und HP-Leiste aktualisieren func take_damage(amount): @@ -539,9 +609,10 @@ func _physics_process(delta): if potion_cooldown > 0: potion_cooldown -= delta - # HUD Cooldowns aktualisieren - hud.set_slot_cooldown(0, global_cooldown) # Slot 1: GCD (Autoattack) - hud.set_slot_cooldown(1, heavy_strike_cooldown) # Slot 2: Heavy Strike CD + # HUD Cooldowns aktualisieren - generisch pro Slot + for i in range(9): + var cd = _get_slot_cooldown(i) + hud.set_slot_cooldown(i, cd) # Schwerkraft if not is_on_floor(): @@ -559,36 +630,12 @@ func _physics_process(delta): if Input.is_action_just_pressed("ui_right_mouse"): _try_select_target(true) - # Aktionsleiste 1-9 - if Input.is_action_just_pressed("action_1"): - hud.set_active_slot(0) - if target != null and global_cooldown <= 0: - start_autoattack() - perform_autoattack() - if Input.is_action_just_pressed("action_2"): - hud.set_active_slot(1) - use_heavy_strike() - if Input.is_action_just_pressed("action_3"): - hud.set_active_slot(2) - _use_action_slot(2) - if Input.is_action_just_pressed("action_4"): - hud.set_active_slot(3) - _use_action_slot(3) - if Input.is_action_just_pressed("action_5"): - hud.set_active_slot(4) - _use_action_slot(4) - if Input.is_action_just_pressed("action_6"): - hud.set_active_slot(5) - _use_action_slot(5) - if Input.is_action_just_pressed("action_7"): - hud.set_active_slot(6) - _use_action_slot(6) - if Input.is_action_just_pressed("action_8"): - hud.set_active_slot(7) - _use_action_slot(7) - if Input.is_action_just_pressed("action_9"): - hud.set_active_slot(8) - _use_action_slot(8) + # Aktionsleiste 1-9 — alle generisch über _use_action_slot + for i in range(9): + var action_name = "action_" + str(i + 1) + if Input.is_action_just_pressed(action_name): + hud.set_active_slot(i) + _use_action_slot(i) # TEST: T drücken = 10 Schaden if Input.is_action_just_pressed("test_damage"): @@ -603,6 +650,10 @@ func _physics_process(delta): if Input.is_action_just_pressed("toggle_inventory"): inventory_panel.toggle() + # P drücken = Fähigkeiten-Panel öffnen/schließen + if Input.is_action_just_pressed("toggle_skills"): + skill_panel.toggle() + # Eingabe var input_dir = Vector2.ZERO if Input.is_action_pressed("move_forward"): diff --git a/player.tscn b/player.tscn index df39355..fd9da51 100644 --- a/player.tscn +++ b/player.tscn @@ -7,6 +7,7 @@ [ext_resource type="PackedScene" uid="uid://character_panel" path="res://character_panel.tscn" id="5_char_panel"] [ext_resource type="PackedScene" uid="uid://inventory_panel" path="res://inventory_panel.tscn" id="6_inv_panel"] [ext_resource type="PackedScene" uid="uid://loot_window" path="res://loot_window.tscn" id="7_loot_win"] +[ext_resource type="PackedScene" uid="uid://skill_panel" path="res://skill_panel.tscn" id="8_skill_panel"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4flbx"] radius = 0.6 @@ -36,3 +37,5 @@ transform = Transform3D(2, 0, 0, 0, 1.8126155, 0.84523654, 0, -0.84523654, 1.812 [node name="InventoryPanel" parent="." instance=ExtResource("6_inv_panel")] [node name="LootWindow" parent="." instance=ExtResource("7_loot_win")] + +[node name="SkillPanel" parent="." instance=ExtResource("8_skill_panel")] diff --git a/project.godot b/project.godot index 586ae3e..07b335d 100644 --- a/project.godot +++ b/project.godot @@ -107,6 +107,11 @@ toggle_inventory={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":73,"key_label":0,"unicode":105,"location":0,"echo":false,"script":null) ] } +toggle_skills={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":80,"key_label":0,"unicode":112,"location":0,"echo":false,"script":null) +] +} [physics] diff --git a/skill_panel.gd b/skill_panel.gd new file mode 100644 index 0000000..6112763 --- /dev/null +++ b/skill_panel.gd @@ -0,0 +1,120 @@ +# SkillPanel.gd +# Zeigt alle verfügbaren Fähigkeiten an, von hier aus auf Aktionsleiste ziehen +extends CanvasLayer + +var panel_visible = false +var player = null + +# Drag State +var dragging = false +var drag_skill_id: String = "" +var drag_icon: TextureRect = null + +@onready var panel = $Panel +@onready var skill_list = $Panel/VBoxContainer/ScrollContainer/SkillList + +func _ready(): + panel.visible = false + +func setup(p): + player = p + +func toggle(): + panel_visible = !panel_visible + panel.visible = panel_visible + if panel_visible: + _refresh_skills() + +func _refresh_skills(): + if player == null: + return + + for child in skill_list.get_children(): + child.queue_free() + + for skill in player.available_skills: + var hbox = HBoxContainer.new() + hbox.add_theme_constant_override("separation", 8) + + # Icon + var icon_rect = TextureRect.new() + var tex = load(skill["icon"]) + if tex: + icon_rect.texture = tex + icon_rect.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL + icon_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + icon_rect.custom_minimum_size = Vector2(36, 36) + icon_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE + hbox.add_child(icon_rect) + + # Name + Beschreibung + var label = Label.new() + label.text = skill["name"] + label.add_theme_font_size_override("font_size", 14) + label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + label.mouse_filter = Control.MOUSE_FILTER_IGNORE + hbox.add_child(label) + + # Tooltip + hbox.tooltip_text = skill["name"] + "\n" + skill["description"] + hbox.custom_minimum_size = Vector2(0, 40) + hbox.mouse_filter = Control.MOUSE_FILTER_STOP + + # Drag starten bei Linksklick + var skill_id = skill["id"] + var skill_icon_path = skill["icon"] + hbox.gui_input.connect(_on_skill_input.bind(skill_id, skill_icon_path)) + + skill_list.add_child(hbox) + +func _on_skill_input(event: InputEvent, skill_id: String, icon_path: String): + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed: + _start_drag(skill_id, icon_path) + +func _start_drag(skill_id: String, icon_path: String): + dragging = true + drag_skill_id = skill_id + var tex = load(icon_path) + drag_icon = TextureRect.new() + 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 + 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) + 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) + if player and player.hud: + player.hud.update_drag_hover(get_viewport().get_mouse_position()) + +func _input(event): + if not dragging: + return + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and not event.pressed: + _end_drag() + +func _end_drag(): + if not dragging: + return + if player and player.hud: + var slot_index = player.hud.get_slot_at_position(get_viewport().get_mouse_position()) + if slot_index >= 0 and slot_index <= 8 and drag_skill_id != "": + player.assign_skill_to_action_bar(slot_index, drag_skill_id) + player.hud.set_drag_active(false) + if drag_icon: + var drag_layer = drag_icon.get_parent() + drag_layer.queue_free() + drag_icon = null + dragging = false + drag_skill_id = "" diff --git a/skill_panel.tscn b/skill_panel.tscn new file mode 100644 index 0000000..bfe3d5a --- /dev/null +++ b/skill_panel.tscn @@ -0,0 +1,48 @@ +[gd_scene load_steps=2 format=3 uid="uid://skill_panel"] + +[ext_resource type="Script" path="res://skill_panel.gd" id="1"] + +[node name="SkillPanel" type="CanvasLayer"] +script = ExtResource("1") + +[node name="Panel" type="Panel" parent="."] +anchors_preset = 0 +offset_left = 250 +offset_top = 100 +offset_right = 530 +offset_bottom = 400 +custom_minimum_size = Vector2(280, 300) + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 10 +offset_top = 10 +offset_right = -10 +offset_bottom = -10 + +[node name="TitleLabel" type="Label" parent="Panel/VBoxContainer"] +layout_mode = 2 +text = "Fähigkeiten" +horizontal_alignment = 1 +theme_override_font_sizes/font_size = 18 + +[node name="HSeparator" type="HSeparator" parent="Panel/VBoxContainer"] +layout_mode = 2 + +[node name="HintLabel" type="Label" parent="Panel/VBoxContainer"] +layout_mode = 2 +text = "Linksklick + Ziehen auf Aktionsleiste" +horizontal_alignment = 1 +theme_override_font_sizes/font_size = 10 +modulate = Color(0.7, 0.7, 0.7, 1) + +[node name="ScrollContainer" type="ScrollContainer" parent="Panel/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="SkillList" type="VBoxContainer" parent="Panel/VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3