Drag & Drop Aktionsleiste und Fähigkeiten-Panel
- Alle Aktionsleisten-Slots frei belegbar (Skills + Consumables) - Drag & Drop: Items/Skills zwischen Slots verschieben oder rausziehen - Gelber Highlight-Rand beim Hover über Slots während Drag - Drag-Icon auf eigener CanvasLayer (Layer 200) für korrektes Z-Order - Fähigkeiten-Panel (P-Taste): Listet alle Skills, per Drag auf Leiste ziehen - Skills als Strings in action_bar_items gespeichert (generisches System) - Cooldown-Anzeige generisch für alle Slot-Typen - Bugfix: theme_override_constants und Tooltip-Typen in LootWindow Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e682ed65e4
commit
d029a37e7f
9 changed files with 466 additions and 77 deletions
|
|
@ -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
|
||||
|
|
|
|||
110
hud.gd
110
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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
# 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
|
||||
# Kein freier Slot - auf Slot 2 legen
|
||||
player.assign_to_action_bar(2, item)
|
||||
print(item.item_name + " auf Slot 3 gelegt (überschrieben)")
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
151
player.gd
151
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,21 +269,79 @@ 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
|
||||
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()
|
||||
1: # Heavy Strike
|
||||
"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)
|
||||
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
|
||||
|
|
@ -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"):
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
120
skill_panel.gd
Normal file
120
skill_panel.gd
Normal file
|
|
@ -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 = ""
|
||||
48
skill_panel.tscn
Normal file
48
skill_panel.tscn
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Reference in a new issue