Consumable-System, Klassen-Ressourcen, Hauptmenü und Item-Icons

- 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>
This commit is contained in:
Andre 2026-03-15 20:45:57 +01:00
parent 0d371c9f68
commit e682ed65e4
44 changed files with 1431 additions and 187 deletions

View file

@ -25,7 +25,7 @@ Gegner bekämpfen und ihre Charaktere mit verschiedenen Klassen und Ausrüstunge
| Rechtsklick auf Gegner | Ziel markieren + Autoattack starten | | Rechtsklick auf Gegner | Ziel markieren + Autoattack starten |
| 1 | Autoattack manuell starten | | 1 | Autoattack manuell starten |
| 2 | Heavy Strike (starke Attacke) | | 2 | Heavy Strike (starke Attacke) |
| 3 9 | Aktionsleiste Slots (noch frei) | | 3 9 | Aktionsleiste Slots (Consumables/Items) |
| C | Charakter-Panel (Stats + Equipment) | | C | Charakter-Panel (Stats + Equipment) |
| I | Inventar öffnen/schließen | | I | Inventar öffnen/schließen |
| Leertaste | Springen | | Leertaste | Springen |
@ -36,7 +36,7 @@ Gegner bekämpfen und ihre Charaktere mit verschiedenen Klassen und Ausrüstunge
## Szenen-Struktur ## Szenen-Struktur
### world.tscn ### world.tscn
Hauptszene der Spielwelt. Zeigt bei Start das Klassenauswahl-Menü. Hauptszene der Spielwelt. Zeigt bei Start das Hauptmenü (Einstellungen), dann Klassenauswahl.
``` ```
World (Node3D) World (Node3D)
├── Player (player.tscn) ├── Player (player.tscn)
@ -90,21 +90,44 @@ Resource-Klasse die eine spielbare Klasse definiert.
| unarmed_min_damage | int | Unbewaffneter Mindestschaden | | unarmed_min_damage | int | Unbewaffneter Mindestschaden |
| unarmed_max_damage | int | Unbewaffneter Maximalschaden | | unarmed_max_damage | int | Unbewaffneter Maximalschaden |
| unarmed_attack_speed | float | Unbewaffnete Angriffsgeschwindigkeit | | unarmed_attack_speed | float | Unbewaffnete Angriffsgeschwindigkeit |
| resource_type | ResourceType | NONE, MANA, RAGE, ENERGY |
| base_resource | int | Basis-Ressource auf Level 1 |
**Konstanten:** **Konstanten:**
- `HP_PER_STAMINA = 10` — HP pro Stamina-Punkt - `HP_PER_STAMINA = 10` — HP pro Stamina-Punkt
- `MANA_PER_INT = 5` — Mana pro Intelligenz-Punkt
- `DAMAGE_PER_MAIN_STAT = 0.5` — Zusatzschaden pro Main-Stat-Punkt - `DAMAGE_PER_MAIN_STAT = 0.5` — Zusatzschaden pro Main-Stat-Punkt
### Verfügbare Klassen (classes/) ### Verfügbare Klassen (classes/)
| Klasse | Main-Stat | STR | AGI | INT | STA | Unbewaffnet | | Klasse | Main-Stat | Ressource | STR | AGI | INT | STA | Unbewaffnet |
|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|
| Krieger | Stärke | 15 | 8 | 5 | 12 | 2-4 Schaden, 1.8s | | Krieger | Stärke | Keine | 15 | 8 | 5 | 12 | 2-4 Schaden, 1.8s |
| Schurke | Beweglichkeit | 8 | 15 | 7 | 10 | 1-3 Schaden, 1.5s | | Schurke | Beweglichkeit | Energie (100) | 8 | 15 | 7 | 10 | 1-3 Schaden, 1.5s |
| Magier | Intelligenz | 5 | 8 | 15 | 8 | 1-2 Schaden, 2.0s | | Magier | Intelligenz | Mana (100 + INT*5) | 5 | 8 | 15 | 8 | 1-2 Schaden, 2.0s |
### Hauptmenü (main_menu.gd)
Wird als erstes angezeigt mit Optionen:
- **Spielen** — Öffnet Klassenauswahl
- **Einstellungen** — Auflösung (720p-4K), Fenstermodus (Fenster/Randlos/Vollbild), VSync, Anti-Aliasing (Aus/2x/4x/8x)
- **Beenden** — Spiel schließen
### Klassenauswahl (class_selection_menu.gd) ### Klassenauswahl (class_selection_menu.gd)
Wird bei Spielstart angezeigt. Spieler wählt eine Klasse, danach wird Startausrüstung angelegt. Wird nach dem Hauptmenü angezeigt. Spieler wählt eine Klasse, danach wird Startausrüstung angelegt.
---
## Klassen-Ressourcen-System
Jede Klasse kann eine eigene Ressource haben:
| Ressource | Farbe | Skalierung | Klasse |
|---|---|---|---|
| Keine | - | - | Krieger |
| Mana | Blau | base_resource + INT * 5 | Magier |
| Energie | Gelb | Fix (base_resource) | Schurke |
| Wut | Rot | Fix (base_resource) | (geplant) |
Die Ressourcen-Leiste wird nur angezeigt wenn die Klasse eine Ressource hat.
--- ---
@ -112,7 +135,7 @@ Wird bei Spielstart angezeigt. Spieler wählt eine Klasse, danach wird Startausr
- **XP-Kurve:** Level N benötigt `100 * N` XP (Level 2: 100, Level 3: 200, ...) - **XP-Kurve:** Level N benötigt `100 * N` XP (Level 2: 100, Level 3: 200, ...)
- **Stats pro Level:** Basierend auf Klassen-Zuwachsraten - **Stats pro Level:** Basierend auf Klassen-Zuwachsraten
- **HP bei Level-Up:** Werden vollständig aufgefüllt - **HP bei Level-Up:** Werden vollständig aufgefüllt (HP + Klassen-Ressource)
- **Character Panel:** Aktualisiert sich automatisch bei Level-Up - **Character Panel:** Aktualisiert sich automatisch bei Level-Up
--- ---
@ -155,12 +178,41 @@ Resource-Klasse für alle Ausrüstungsgegenstände.
### Character Panel (character_panel.gd, C-Taste) ### Character Panel (character_panel.gd, C-Taste)
Zeigt zwei Spalten: Zeigt zwei Spalten:
- **Stats-Spalte:** Stärke, Beweglichkeit, Intelligenz, Ausdauer, Rüstung, HP - **Stats-Spalte:** Stärke, Beweglichkeit, Intelligenz, Ausdauer, Rüstung, HP
- **Equipment-Spalte:** Alle 7 Slots mit ausgerüsteten Items, Waffenschaden + DPS - **Equipment-Spalte:** Icon-Grid mit Tooltips bei Hover, Waffenschaden + DPS
Unbewaffnet: Zeigt klassenabhängige Schadenswerte. Unbewaffnet: Zeigt klassenabhängige Schadenswerte.
--- ---
## Consumable-System
### Consumable (consumable.gd)
Resource-Klasse für verbrauchbare Items (Tränke, Essen, etc.).
| Property | Typ | Beschreibung |
|---|---|---|
| item_name | String | Name des Tranks |
| effect_type | EffectType | HEAL_HP, RESTORE_MANA, HEAL_AND_MANA |
| amount | int | Heilungsmenge |
| cooldown | float | Cooldown nach Benutzung |
| icon | Texture2D | Icon für UI |
| stack_size | int | Aktuelle Anzahl im Stack |
| max_stack | int | Maximale Stack-Größe (20) |
### Vorhandene Tränke (consumables/)
- **small_hp_potion.tres** — Kleiner Heiltrank (+50 HP, 40% Drop-Chance)
- **small_mana_potion.tres** — Kleiner Manatrank (+40 Mana, 25% Drop-Chance)
### Benutzung
- **Rechtsklick im Inventar:** Trank direkt benutzen
- **Shift+Linksklick im Inventar:** Trank auf nächsten freien Aktionsleisten-Slot legen
- **Taste 3-9:** Trank über Aktionsleiste benutzen
- HP-Tränke: Nur wenn HP nicht voll, stellt HP wieder her
- Mana-Tränke: Nur wenn Mana nicht voll, stellt Ressource wieder her
- **Stacking:** Gleichnamige Tränke werden automatisch gestackt (max 20)
---
## Inventar-System ## Inventar-System
### Inventory (inventory.gd) ### Inventory (inventory.gd)
@ -204,11 +256,11 @@ Ein einzelner Drop-Eintrag.
| Property | Typ | Beschreibung | | Property | Typ | Beschreibung |
|---|---|---| |---|---|---|
| item | Equipment | Das droppbare Item | | item | Resource | Das droppbare Item (Equipment oder Consumable) |
| drop_chance | float | Wahrscheinlichkeit (0.0 - 1.0) | | drop_chance | float | Wahrscheinlichkeit (0.0 - 1.0) |
### Vorhandene LootTables (loot_tables/) ### Vorhandene LootTables (loot_tables/)
- **goblin_loot.tres** — 2-8 Gold, Eisenschwert (15%), Lederrüstung (10%), Eisenhelm (10%) - **goblin_loot.tres** — 2-8 Gold, Eisenschwert (15%), Lederrüstung (10%), Eisenhelm (10%), Heiltrank (40%), Manatrank (25%)
- **skeleton_loot.tres** — 5-15 Gold, Stahlschwert (10%), Holzschild (12%), Eisenhelm (15%) - **skeleton_loot.tres** — 5-15 Gold, Stahlschwert (10%), Holzschild (12%), Eisenhelm (15%)
### LootWindow (loot_window.gd) ### LootWindow (loot_window.gd)
@ -298,15 +350,17 @@ Level-basiert mit automatischer Skalierung:
| Element | Beschreibung | | Element | Beschreibung |
|---|---| |---|---|
| HealthBar | Rote HP-Leiste mit Text "aktuell / max" | | HealthBar | Rote HP-Leiste mit Text "aktuell / max" |
| ResourceBar | Ressourcen-Leiste (Blau=Mana, Gelb=Energie, Rot=Wut), nur sichtbar wenn Klasse eine Ressource hat |
| LevelLabel | "Lv X" Anzeige | | LevelLabel | "Lv X" Anzeige |
| XPBar | Blaue XP-Leiste | | XPBar | Blaue XP-Leiste |
| GoldLabel | Gold-Anzeige in Goldfarbe | | GoldLabel | Gold-Anzeige in Goldfarbe |
| ActionBar | 9 Slots mit Icons, Cooldowns, Klick-Support | | ActionBar | 9 Slots mit Icons, Cooldowns, Klick-Support, Stack-Anzeige für Consumables |
--- ---
## Geplante Features ## Geplante Features
- [ ] Mana-System für Magier - [ ] Wut-Ressource für Krieger
- [ ] Ressourcen-System für Gegner (nicht alle haben Mana)
- [ ] Spell-System (Feuerbälle etc.) - [ ] Spell-System (Feuerbälle etc.)
- [ ] Schadenstypen (Physical, Fire, Ice, Lightning, Poison) - [ ] Schadenstypen (Physical, Fire, Ice, Lightning, Poison)
- [ ] Mehrere Gegnertypen - [ ] Mehrere Gegnertypen
@ -322,9 +376,12 @@ Level-basiert mit automatischer Skalierung:
DungeonCrawler/ DungeonCrawler/
├── assets/ # 3D-Modelle (Kenney GLB) ├── assets/ # 3D-Modelle (Kenney GLB)
├── classes/ # Klassen-Definitionen (.tres) ├── classes/ # Klassen-Definitionen (.tres)
│ ├── warrior.tres │ ├── warrior.tres # Krieger (Ressource: NONE)
│ ├── rogue.tres │ ├── rogue.tres # Schurke (Ressource: ENERGY, 100)
│ └── mage.tres │ └── mage.tres # Magier (Ressource: MANA, 100)
├── consumables/ # Verbrauchbare Items (.tres)
│ ├── small_hp_potion.tres
│ └── small_mana_potion.tres
├── equipment/ # Equipment-Items (.tres) ├── equipment/ # Equipment-Items (.tres)
│ ├── iron_sword.tres │ ├── iron_sword.tres
│ ├── steel_sword.tres │ ├── steel_sword.tres
@ -334,19 +391,27 @@ DungeonCrawler/
├── loot_tables/ # Loot-Tabellen (.tres) ├── loot_tables/ # Loot-Tabellen (.tres)
│ ├── goblin_loot.tres │ ├── goblin_loot.tres
│ └── skeleton_loot.tres │ └── skeleton_loot.tres
├── icons/ # Skill-Icons (SVG) ├── icons/ # Icons (SVG)
│ ├── autoattack_icon.svg │ ├── autoattack_icon.svg
│ └── heavy_strike_icon.svg │ ├── heavy_strike_icon.svg
│ ├── iron_sword_icon.svg
│ ├── steel_sword_icon.svg
│ ├── leather_chest_icon.svg
│ ├── iron_helm_icon.svg
│ ├── wooden_shield_icon.svg
│ ├── hp_potion_icon.svg
│ └── mana_potion_icon.svg
├── camera_pivot.gd # Kamera-Script ├── camera_pivot.gd # Kamera-Script
├── character_class.gd # CharacterClass Resource ├── character_class.gd # CharacterClass Resource (inkl. ResourceType)
├── character_panel.gd # Charakter-Panel Script ├── character_panel.gd # Charakter-Panel Script (Icon-Grid)
├── character_panel.tscn # Charakter-Panel Scene ├── character_panel.tscn # Charakter-Panel Scene
├── class_selection_menu.gd # Klassenauswahl Script ├── class_selection_menu.gd # Klassenauswahl Script
├── class_selection_menu.tscn # Klassenauswahl Scene ├── class_selection_menu.tscn # Klassenauswahl Scene
├── consumable.gd # Consumable Resource
├── enemy.gd # Gegner-Script ├── enemy.gd # Gegner-Script
├── enemy.tscn # Gegner-Scene ├── enemy.tscn # Gegner-Scene
├── equipment.gd # Equipment Resource ├── equipment.gd # Equipment Resource
├── hud.gd # HUD-Script ├── hud.gd # HUD-Script (inkl. ResourceBar)
├── hud.tscn # HUD-Scene ├── hud.tscn # HUD-Scene
├── inventory.gd # Inventar Resource ├── inventory.gd # Inventar Resource
├── inventory_panel.gd # Inventar-Panel Script ├── inventory_panel.gd # Inventar-Panel Script
@ -355,7 +420,9 @@ DungeonCrawler/
├── loot_table.gd # LootTable Resource ├── loot_table.gd # LootTable Resource
├── loot_window.gd # Loot-Fenster Script ├── loot_window.gd # Loot-Fenster Script
├── loot_window.tscn # Loot-Fenster Scene ├── loot_window.tscn # Loot-Fenster Scene
├── player.gd # Spieler-Script ├── main_menu.gd # Hauptmenü Script (Einstellungen)
├── main_menu.tscn # Hauptmenü Scene
├── player.gd # Spieler-Script (inkl. Ressourcen, Aktionsleiste)
├── player.tscn # Spieler-Scene ├── player.tscn # Spieler-Scene
├── world.gd # Welt-Script ├── world.gd # Welt-Script
├── world.tscn # Hauptszene ├── world.tscn # Hauptszene

View file

@ -4,9 +4,12 @@ extends Resource
class_name CharacterClass class_name CharacterClass
enum MainStat { STRENGTH, AGILITY, INTELLIGENCE } enum MainStat { STRENGTH, AGILITY, INTELLIGENCE }
enum ResourceType { NONE, MANA, RAGE, ENERGY }
@export var class_name_de: String = "Krieger" @export var class_name_de: String = "Krieger"
@export var main_stat: MainStat = MainStat.STRENGTH @export var main_stat: MainStat = MainStat.STRENGTH
@export var resource_type: ResourceType = ResourceType.NONE
@export var base_resource: int = 0 # Basis-Ressource auf Level 1 (0 = keine)
# Grund-Stats auf Level 1 # Grund-Stats auf Level 1
@export var base_strength: int = 10 @export var base_strength: int = 10
@ -27,6 +30,8 @@ enum MainStat { STRENGTH, AGILITY, INTELLIGENCE }
# HP pro Stamina-Punkt # HP pro Stamina-Punkt
const HP_PER_STAMINA = 10 const HP_PER_STAMINA = 10
# Mana pro Intelligenz-Punkt
const MANA_PER_INT = 5
# Schaden-Skalierung mit Main-Stat # Schaden-Skalierung mit Main-Stat
const DAMAGE_PER_MAIN_STAT = 0.5 const DAMAGE_PER_MAIN_STAT = 0.5

View file

@ -1,13 +1,14 @@
# CharacterPanel.gd # CharacterPanel.gd
# Zeigt Charakterinfos: Klasse, Level, Stats und Equipment # Zeigt Charakterinfos: Klasse, Level, Stats und Equipment-Icons mit Tooltips
extends CanvasLayer extends CanvasLayer
var panel_visible = false var panel_visible = false
const EQUIP_SLOT_SIZE = 48
@onready var panel = $Panel @onready var panel = $Panel
@onready var class_label = $Panel/HBoxContainer/StatsColumn/ClassLabel @onready var class_label = $Panel/HBoxContainer/StatsColumn/ClassLabel
@onready var level_label = $Panel/HBoxContainer/StatsColumn/LevelLabel @onready var level_label = $Panel/HBoxContainer/StatsColumn/LevelLabel
@onready var stats_container = $Panel/HBoxContainer/StatsColumn/StatsContainer
@onready var str_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/StrLabel @onready var str_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/StrLabel
@onready var agi_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/AgiLabel @onready var agi_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/AgiLabel
@onready var int_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/IntLabel @onready var int_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/IntLabel
@ -16,15 +17,18 @@ var panel_visible = false
@onready var hp_label = $Panel/HBoxContainer/StatsColumn/HPLabel @onready var hp_label = $Panel/HBoxContainer/StatsColumn/HPLabel
@onready var damage_label = $Panel/HBoxContainer/EquipmentColumn/DamageLabel @onready var damage_label = $Panel/HBoxContainer/EquipmentColumn/DamageLabel
@onready var dps_label = $Panel/HBoxContainer/EquipmentColumn/DPSLabel @onready var dps_label = $Panel/HBoxContainer/EquipmentColumn/DPSLabel
@onready var equipment_grid = $Panel/HBoxContainer/EquipmentColumn/EquipmentGrid
# Equipment Slots # Slot-Reihenfolge und deutsche Namen
@onready var head_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/HeadSlot const SLOT_ORDER = [
@onready var chest_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/ChestSlot Equipment.Slot.HEAD,
@onready var hands_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/HandsSlot Equipment.Slot.CHEST,
@onready var legs_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/LegsSlot Equipment.Slot.HANDS,
@onready var feet_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/FeetSlot Equipment.Slot.LEGS,
@onready var weapon_slot = $Panel/HBoxContainer/EquipmentColumn/WeaponSlot Equipment.Slot.FEET,
@onready var offhand_slot = $Panel/HBoxContainer/EquipmentColumn/OffhandSlot Equipment.Slot.WEAPON,
Equipment.Slot.OFFHAND,
]
func _ready(): func _ready():
panel.visible = false panel.visible = false
@ -57,12 +61,11 @@ func update_stats(player):
hp_label.text = "HP: " + str(player.current_hp) + " / " + str(player.max_hp) hp_label.text = "HP: " + str(player.current_hp) + " / " + str(player.max_hp)
# Waffen-Stats (jetzt in Equipment-Spalte) # Waffen-Stats
var weapon = player.get_equipped_weapon() var weapon = player.get_equipped_weapon()
if weapon: if weapon:
damage_label.text = "Schaden: " + str(weapon.min_damage) + "-" + str(weapon.max_damage) + " (%.1fs)" % weapon.attack_speed damage_label.text = "Schaden: " + str(weapon.min_damage) + "-" + str(weapon.max_damage) + " (%.1fs)" % weapon.attack_speed
else: else:
# Unbewaffnet: klassenabhängiger Schaden
if player.character_class: if player.character_class:
var min_dmg = player.character_class.unarmed_min_damage var min_dmg = player.character_class.unarmed_min_damage
var max_dmg = player.character_class.unarmed_max_damage var max_dmg = player.character_class.unarmed_max_damage
@ -86,19 +89,115 @@ func update_stats(player):
CharacterClass.MainStat.INTELLIGENCE: CharacterClass.MainStat.INTELLIGENCE:
int_label.modulate = Color(1, 0.8, 0.2) int_label.modulate = Color(1, 0.8, 0.2)
# Equipment aktualisieren # Equipment-Grid mit Icons aktualisieren
_update_equipment_slot(head_slot, "Kopf", player.equipment[Equipment.Slot.HEAD]) _rebuild_equipment_grid(player)
_update_equipment_slot(chest_slot, "Brust", player.equipment[Equipment.Slot.CHEST])
_update_equipment_slot(hands_slot, "Hände", player.equipment[Equipment.Slot.HANDS])
_update_equipment_slot(legs_slot, "Beine", player.equipment[Equipment.Slot.LEGS])
_update_equipment_slot(feet_slot, "Füße", player.equipment[Equipment.Slot.FEET])
_update_equipment_slot(weapon_slot, "Waffe", player.equipment[Equipment.Slot.WEAPON])
_update_equipment_slot(offhand_slot, "Nebenhand", player.equipment[Equipment.Slot.OFFHAND])
func _update_equipment_slot(label: Label, slot_name: String, item: Equipment): func _rebuild_equipment_grid(player):
if item == null: # Alte Slots entfernen
label.text = slot_name + ": -" for child in equipment_grid.get_children():
label.modulate = Color(0.6, 0.6, 0.6) child.queue_free()
# Slots als Icon-Panels erstellen
for slot_type in SLOT_ORDER:
var item = player.equipment[slot_type]
var slot_panel = _create_equip_slot(slot_type, item)
equipment_grid.add_child(slot_panel)
func _create_equip_slot(slot_type: Equipment.Slot, item: Equipment) -> Panel:
var slot = Panel.new()
slot.custom_minimum_size = Vector2(EQUIP_SLOT_SIZE, EQUIP_SLOT_SIZE)
# Hintergrund
var style = StyleBoxFlat.new()
style.bg_color = Color(0.12, 0.12, 0.12)
style.border_color = Color(0.3, 0.3, 0.3)
style.set_border_width_all(1)
style.set_corner_radius_all(4)
slot.add_theme_stylebox_override("panel", style)
if item != null:
# Rahmen in Seltenheitsfarbe
style.border_color = Equipment.get_rarity_color(item.rarity)
style.set_border_width_all(2)
if item.icon:
# Icon anzeigen
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(EQUIP_SLOT_SIZE - 6, EQUIP_SLOT_SIZE - 6)
icon.position = Vector2(3, 3)
slot.add_child(icon)
else:
# Fallback: Kurzname
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)
label.modulate = Equipment.get_rarity_color(item.rarity)
label.anchors_preset = Control.PRESET_FULL_RECT
slot.add_child(label)
# Tooltip mit Item-Eigenschaften
slot.tooltip_text = _get_item_tooltip(item)
else: else:
label.text = slot_name + ": " + item.item_name # Leerer Slot: Slot-Name als Tooltip
label.modulate = Equipment.get_rarity_color(item.rarity) slot.tooltip_text = Equipment.get_slot_name(slot_type) + ": Leer"
# Slot-Kürzel anzeigen
var label = Label.new()
label.text = _get_slot_short(slot_type)
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
label.add_theme_font_size_override("font_size", 9)
label.modulate = Color(0.4, 0.4, 0.4)
label.anchors_preset = Control.PRESET_FULL_RECT
slot.add_child(label)
return slot
func _get_slot_short(slot_type: Equipment.Slot) -> String:
match slot_type:
Equipment.Slot.HEAD: return "Kopf"
Equipment.Slot.CHEST: return "Brust"
Equipment.Slot.HANDS: return "Hand"
Equipment.Slot.LEGS: return "Bein"
Equipment.Slot.FEET: return "Fuß"
Equipment.Slot.WEAPON: return "Waffe"
Equipment.Slot.OFFHAND: return "Neben"
return "?"
func _get_item_tooltip(item: Equipment) -> String:
var tooltip = item.item_name + "\n"
tooltip += Equipment.get_slot_name(item.slot) + "\n"
# Seltenheit
match item.rarity:
Equipment.Rarity.COMMON: tooltip += "Gewöhnlich\n"
Equipment.Rarity.UNCOMMON: tooltip += "Ungewöhnlich\n"
Equipment.Rarity.RARE: tooltip += "Selten\n"
Equipment.Rarity.EPIC: tooltip += "Episch\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"
tooltip += "Reichweite: " + str(item.weapon_range) + "\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"
return tooltip

View file

@ -104,46 +104,15 @@ horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"] [node name="HSeparator" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2 layout_mode = 2
[node name="EquipmentContainer" type="VBoxContainer" parent="Panel/HBoxContainer/EquipmentColumn"] [node name="EquipmentGrid" type="GridContainer" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2 layout_mode = 2
theme_override_constants/separation = 4 theme_override_constants/h_separation = 6
theme_override_constants/v_separation = 6
[node name="HeadSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"] columns = 4
layout_mode = 2
text = "Kopf: -"
[node name="ChestSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"]
layout_mode = 2
text = "Brust: -"
[node name="HandsSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"]
layout_mode = 2
text = "Hände: -"
[node name="LegsSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"]
layout_mode = 2
text = "Beine: -"
[node name="FeetSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"]
layout_mode = 2
text = "Füße: -"
[node name="HSeparator2" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"] [node name="HSeparator2" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2 layout_mode = 2
[node name="WeaponSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Waffe: -"
[node name="OffhandSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Nebenhand: -"
[node name="HSeparator3" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2
[node name="DamageLabel" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"] [node name="DamageLabel" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2 layout_mode = 2
theme_override_font_sizes/font_size = 14 theme_override_font_sizes/font_size = 14

View file

@ -1,11 +1,13 @@
[gd_resource type="Resource" script_class="CharacterClass" format=3] [gd_resource type="Resource" script_class="CharacterClass" format=3 uid="uid://24g7k0k7vbr6"]
[ext_resource type="Script" path="res://character_class.gd" id="1"] [ext_resource type="Script" uid="uid://ci45xxb5vn857" path="res://character_class.gd" id="1"]
[resource] [resource]
script = ExtResource("1") script = ExtResource("1")
class_name_de = "Magier" class_name_de = "Magier"
main_stat = 2 main_stat = 2
resource_type = 1
base_resource = 100
base_strength = 5 base_strength = 5
base_agility = 8 base_agility = 8
base_intelligence = 15 base_intelligence = 15
@ -14,6 +16,3 @@ strength_per_level = 1.0
agility_per_level = 1.5 agility_per_level = 1.5
intelligence_per_level = 3.0 intelligence_per_level = 3.0
stamina_per_level = 1.5 stamina_per_level = 1.5
unarmed_min_damage = 1
unarmed_max_damage = 2
unarmed_attack_speed = 2.0

View file

@ -1,19 +1,18 @@
[gd_resource type="Resource" script_class="CharacterClass" format=3] [gd_resource type="Resource" script_class="CharacterClass" format=3 uid="uid://cdcx81iw6hxic"]
[ext_resource type="Script" path="res://character_class.gd" id="1"] [ext_resource type="Script" uid="uid://ci45xxb5vn857" path="res://character_class.gd" id="1"]
[resource] [resource]
script = ExtResource("1") script = ExtResource("1")
class_name_de = "Schurke" class_name_de = "Schurke"
main_stat = 1 main_stat = 1
resource_type = 3
base_resource = 100
base_strength = 8 base_strength = 8
base_agility = 15 base_agility = 15
base_intelligence = 7 base_intelligence = 7
base_stamina = 10
strength_per_level = 1.5 strength_per_level = 1.5
agility_per_level = 3.0 agility_per_level = 3.0
intelligence_per_level = 1.5 intelligence_per_level = 1.5
stamina_per_level = 2.0
unarmed_min_damage = 1
unarmed_max_damage = 3 unarmed_max_damage = 3
unarmed_attack_speed = 1.5 unarmed_attack_speed = 1.5

View file

@ -1,11 +1,9 @@
[gd_resource type="Resource" script_class="CharacterClass" format=3] [gd_resource type="Resource" script_class="CharacterClass" format=3 uid="uid://bebfnwygqy1gu"]
[ext_resource type="Script" path="res://character_class.gd" id="1"] [ext_resource type="Script" uid="uid://ci45xxb5vn857" path="res://character_class.gd" id="1"]
[resource] [resource]
script = ExtResource("1") script = ExtResource("1")
class_name_de = "Krieger"
main_stat = 0
base_strength = 15 base_strength = 15
base_agility = 8 base_agility = 8
base_intelligence = 5 base_intelligence = 5

55
consumable.gd Normal file
View file

@ -0,0 +1,55 @@
# Consumable.gd
# Resource für verbrauchbare Items (Tränke, Essen, etc.)
extends Resource
class_name Consumable
enum EffectType {
HEAL_HP, # HP heilen
RESTORE_MANA, # Mana wiederherstellen
HEAL_AND_MANA, # Beides
}
@export var item_name: String = "Trank"
@export var effect_type: EffectType = EffectType.HEAL_HP
@export var amount: int = 50 # Wie viel geheilt/wiederhergestellt wird
@export var cooldown: float = 1.0 # Cooldown nach Benutzung
@export var icon: Texture2D
@export var stack_size: int = 1 # Aktuelle Anzahl im Stack
@export var max_stack: int = 20 # Maximale Stack-Größe
# Effekt-Beschreibung für Tooltip
func get_effect_text() -> String:
match effect_type:
EffectType.HEAL_HP:
return "Stellt " + str(amount) + " HP wieder her"
EffectType.RESTORE_MANA:
return "Stellt " + str(amount) + " Mana wieder her"
EffectType.HEAL_AND_MANA:
return "Stellt " + str(amount) + " HP und Mana wieder her"
return ""
# Effekt auf Spieler anwenden, gibt true zurück wenn verbraucht
func use(player) -> bool:
match effect_type:
EffectType.HEAL_HP:
if player.current_hp >= player.max_hp:
print("HP bereits voll!")
return false
player.heal(amount)
print("+" + str(amount) + " HP")
EffectType.RESTORE_MANA:
if player.current_mana >= player.max_mana:
print("Mana bereits voll!")
return false
player.restore_mana(amount)
print("+" + str(amount) + " Mana")
EffectType.HEAL_AND_MANA:
if player.current_hp >= player.max_hp and player.current_mana >= player.max_mana:
print("HP und Mana bereits voll!")
return false
player.heal(amount)
player.restore_mana(amount)
print("+" + str(amount) + " HP und Mana")
stack_size -= 1
return true

1
consumable.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://b25cvd8swhsg3

View file

@ -0,0 +1,14 @@
[gd_resource type="Resource" script_class="Consumable" load_steps=3 format=3]
[ext_resource type="Script" path="res://consumable.gd" id="1"]
[ext_resource type="Texture2D" path="res://icons/hp_potion_icon.svg" id="2_icon"]
[resource]
script = ExtResource("1")
item_name = "Kleiner Heiltrank"
effect_type = 0
amount = 50
cooldown = 1.0
icon = ExtResource("2_icon")
stack_size = 5
max_stack = 20

View file

@ -0,0 +1,14 @@
[gd_resource type="Resource" script_class="Consumable" load_steps=3 format=3]
[ext_resource type="Script" path="res://consumable.gd" id="1"]
[ext_resource type="Texture2D" path="res://icons/mana_potion_icon.svg" id="2_icon"]
[resource]
script = ExtResource("1")
item_name = "Kleiner Manatrank"
effect_type = 1
amount = 40
cooldown = 1.0
icon = ExtResource("2_icon")
stack_size = 5
max_stack = 20

View file

@ -1,18 +1,13 @@
[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] [gd_resource type="Resource" script_class="Equipment" format=3 uid="uid://d54jjc24pj3t"]
[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] [ext_resource type="Script" uid="uid://re0xiie1udfq" path="res://equipment.gd" id="1_equipment"]
[ext_resource type="Texture2D" uid="uid://c8x8d055wjcpu" path="res://icons/iron_helm_icon.svg" id="2_icon"]
[resource] [resource]
script = ExtResource("1_equipment") script = ExtResource("1_equipment")
item_name = "Eisenhelm" item_name = "Eisenhelm"
slot = 0 slot = 0
rarity = 0
armor = 5 armor = 5
strength = 1 strength = 1
agility = 0
intelligence = 0
stamina = 1 stamina = 1
min_damage = 0 icon = ExtResource("2_icon")
max_damage = 0
attack_speed = 1.5
weapon_range = 3.0

View file

@ -1,18 +1,12 @@
[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] [gd_resource type="Resource" script_class="Equipment" format=3 uid="uid://vprfv2phlcbc"]
[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] [ext_resource type="Script" uid="uid://re0xiie1udfq" path="res://equipment.gd" id="1_equipment"]
[ext_resource type="Texture2D" uid="uid://bi7sh66txlj1c" path="res://icons/iron_sword_icon.svg" id="2_icon"]
[resource] [resource]
script = ExtResource("1_equipment") script = ExtResource("1_equipment")
item_name = "Eisenschwert" item_name = "Eisenschwert"
slot = 5
rarity = 0
armor = 0
strength = 2 strength = 2
agility = 0
intelligence = 0
stamina = 0
min_damage = 3 min_damage = 3
max_damage = 6 max_damage = 6
attack_speed = 1.5 icon = ExtResource("2_icon")
weapon_range = 3.0

View file

@ -1,18 +1,13 @@
[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] [gd_resource type="Resource" script_class="Equipment" format=3 uid="uid://cnijhxsjmepss"]
[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] [ext_resource type="Script" uid="uid://re0xiie1udfq" path="res://equipment.gd" id="1_equipment"]
[ext_resource type="Texture2D" uid="uid://c6kmscwfv0ssa" path="res://icons/leather_chest_icon.svg" id="2_icon"]
[resource] [resource]
script = ExtResource("1_equipment") script = ExtResource("1_equipment")
item_name = "Lederrüstung" item_name = "Lederrüstung"
slot = 1 slot = 1
rarity = 0
armor = 8 armor = 8
strength = 0
agility = 1 agility = 1
intelligence = 0
stamina = 2 stamina = 2
min_damage = 0 icon = ExtResource("2_icon")
max_damage = 0
attack_speed = 1.5
weapon_range = 3.0

View file

@ -1,6 +1,7 @@
[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] [gd_resource type="Resource" script_class="Equipment" load_steps=3 format=3]
[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] [ext_resource type="Script" path="res://equipment.gd" id="1_equipment"]
[ext_resource type="Texture2D" path="res://icons/steel_sword_icon.svg" id="2_icon"]
[resource] [resource]
script = ExtResource("1_equipment") script = ExtResource("1_equipment")
@ -16,3 +17,4 @@ min_damage = 5
max_damage = 9 max_damage = 9
attack_speed = 1.4 attack_speed = 1.4
weapon_range = 3.0 weapon_range = 3.0
icon = ExtResource("2_icon")

View file

@ -1,6 +1,7 @@
[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] [gd_resource type="Resource" script_class="Equipment" load_steps=3 format=3]
[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] [ext_resource type="Script" path="res://equipment.gd" id="1_equipment"]
[ext_resource type="Texture2D" path="res://icons/wooden_shield_icon.svg" id="2_icon"]
[resource] [resource]
script = ExtResource("1_equipment") script = ExtResource("1_equipment")
@ -16,3 +17,4 @@ min_damage = 0
max_damage = 0 max_damage = 0
attack_speed = 1.5 attack_speed = 1.5
weapon_range = 3.0 weapon_range = 3.0
icon = ExtResource("2_icon")

89
hud.gd
View file

@ -27,6 +27,11 @@ var active_slot = 0
var slot_icons = [] # TextureRect nodes für Icons var slot_icons = [] # TextureRect nodes für Icons
var slot_cooldown_overlays = [] # ColorRect für Cooldown-Anzeige var slot_cooldown_overlays = [] # ColorRect für Cooldown-Anzeige
var slot_cooldown_labels = [] # Label für Cooldown-Text 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(): func _ready():
_create_level_ui() _create_level_ui()
@ -66,6 +71,19 @@ func _ready():
action_slots[i].add_child(cooldown_label) action_slots[i].add_child(cooldown_label)
slot_cooldown_labels.append(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 # Button für Klicks erstellen
var button = Button.new() var button = Button.new()
button.name = "SlotButton" button.name = "SlotButton"
@ -131,10 +149,36 @@ func _create_level_ui():
control.add_child(xp_bar) 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
gold_label = Label.new() gold_label = Label.new()
gold_label.name = "GoldLabel" gold_label.name = "GoldLabel"
gold_label.position = Vector2(20, 78) gold_label.position = Vector2(20, 98)
gold_label.add_theme_font_size_override("font_size", 14) 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.add_theme_color_override("font_color", Color(1, 0.85, 0, 1))
gold_label.text = "0 Gold" gold_label.text = "0 Gold"
@ -145,6 +189,49 @@ func update_gold(amount: int):
if gold_label: if gold_label:
gold_label.text = str(amount) + " Gold" 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 # HP-Leiste und Text aktualisieren
func update_health(current_hp, max_hp): func update_health(current_hp, max_hp):
health_bar.max_value = max_hp health_bar.max_value = max_hp

13
icons/hp_potion_icon.svg Normal file
View file

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Flasche -->
<path d="M24 20 L24 12 L40 12 L40 20 L46 32 L46 50 Q46 56 40 56 L24 56 Q18 56 18 50 L18 32 Z" fill="#CC2222" stroke="#8B1515" stroke-width="2"/>
<!-- Flaschenhals -->
<rect x="26" y="8" width="12" height="6" fill="#A08060" stroke="#806040" stroke-width="1" rx="1"/>
<!-- Korken -->
<rect x="27" y="5" width="10" height="5" fill="#B09070" rx="2"/>
<!-- Highlight -->
<path d="M22 34 Q22 28 26 24 L26 34 Z" fill="rgba(255,255,255,0.3)"/>
<!-- Kreuz -->
<rect x="29" y="32" width="6" height="18" fill="white" rx="1"/>
<rect x="23" y="38" width="18" height="6" fill="white" rx="1"/>
</svg>

After

Width:  |  Height:  |  Size: 686 B

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cih3xmw6q88um"
path="res://.godot/imported/hp_potion_icon.svg-fba9724ef0f842c8b57011d6e4f03fb3.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/hp_potion_icon.svg"
dest_files=["res://.godot/imported/hp_potion_icon.svg-fba9724ef0f842c8b57011d6e4f03fb3.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

13
icons/iron_helm_icon.svg Normal file
View file

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Helm Kuppel -->
<path d="M16 38 Q16 12 32 10 Q48 12 48 38 Z" fill="#909090" stroke="#707070" stroke-width="2"/>
<!-- Helmrand -->
<path d="M14 38 L50 38" stroke="#606060" stroke-width="4" stroke-linecap="round"/>
<!-- Nasenschutz -->
<rect x="30" y="28" width="4" height="14" fill="#808080" rx="1"/>
<!-- Nieten -->
<circle cx="20" cy="38" r="2" fill="#505050"/>
<circle cx="44" cy="38" r="2" fill="#505050"/>
<!-- Highlight -->
<path d="M24 16 Q30 14 32 18" fill="none" stroke="#B0B0B0" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 604 B

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c8x8d055wjcpu"
path="res://.godot/imported/iron_helm_icon.svg-f08644c5491a1e9fd669ad08b500cdc1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/iron_helm_icon.svg"
dest_files=["res://.godot/imported/iron_helm_icon.svg-f08644c5491a1e9fd669ad08b500cdc1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

11
icons/iron_sword_icon.svg Normal file
View file

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Klinge -->
<line x1="32" y1="8" x2="32" y2="42" stroke="#A0A0A0" stroke-width="6" stroke-linecap="round"/>
<line x1="32" y1="8" x2="32" y2="42" stroke="#C0C0C0" stroke-width="3" stroke-linecap="round"/>
<!-- Parierstange -->
<line x1="22" y1="42" x2="42" y2="42" stroke="#8B7355" stroke-width="5" stroke-linecap="round"/>
<!-- Griff -->
<line x1="32" y1="42" x2="32" y2="54" stroke="#6B4226" stroke-width="5" stroke-linecap="round"/>
<!-- Knauf -->
<circle cx="32" cy="56" r="3" fill="#A0A0A0"/>
</svg>

After

Width:  |  Height:  |  Size: 587 B

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bi7sh66txlj1c"
path="res://.godot/imported/iron_sword_icon.svg-7666556b18881bfee488b89a46108232.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/iron_sword_icon.svg"
dest_files=["res://.godot/imported/iron_sword_icon.svg-7666556b18881bfee488b89a46108232.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Rumpf -->
<path d="M20 18 L44 18 L46 50 L18 50 Z" fill="#8B6914" stroke="#6B4E0A" stroke-width="2"/>
<!-- Schultern -->
<path d="M14 18 L20 18 L20 28 L14 24 Z" fill="#7A5C10" stroke="#6B4E0A" stroke-width="1.5"/>
<path d="M44 18 L50 18 L50 24 L44 28 Z" fill="#7A5C10" stroke="#6B4E0A" stroke-width="1.5"/>
<!-- Kragen -->
<path d="M24 18 L32 14 L40 18" fill="none" stroke="#6B4E0A" stroke-width="2"/>
<!-- Nähte -->
<line x1="32" y1="18" x2="32" y2="50" stroke="#6B4E0A" stroke-width="1" stroke-dasharray="3,3"/>
<!-- Gürtel -->
<rect x="18" y="38" width="28" height="4" fill="#5A3E08" rx="1"/>
<rect x="30" y="37" width="4" height="6" fill="#C0A030" rx="1"/>
</svg>

After

Width:  |  Height:  |  Size: 759 B

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c6kmscwfv0ssa"
path="res://.godot/imported/leather_chest_icon.svg-c139cc7810b1521bcabf2843303318b5.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/leather_chest_icon.svg"
dest_files=["res://.godot/imported/leather_chest_icon.svg-c139cc7810b1521bcabf2843303318b5.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Flasche -->
<path d="M24 20 L24 12 L40 12 L40 20 L46 32 L46 50 Q46 56 40 56 L24 56 Q18 56 18 50 L18 32 Z" fill="#2244CC" stroke="#152288" stroke-width="2"/>
<!-- Flaschenhals -->
<rect x="26" y="8" width="12" height="6" fill="#A08060" stroke="#806040" stroke-width="1" rx="1"/>
<!-- Korken -->
<rect x="27" y="5" width="10" height="5" fill="#B09070" rx="2"/>
<!-- Highlight -->
<path d="M22 34 Q22 28 26 24 L26 34 Z" fill="rgba(255,255,255,0.3)"/>
<!-- Stern -->
<polygon points="32,30 34,38 42,38 36,43 38,51 32,46 26,51 28,43 22,38 30,38" fill="white" opacity="0.9"/>
</svg>

After

Width:  |  Height:  |  Size: 663 B

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dc6ai8ec7g20"
path="res://.godot/imported/mana_potion_icon.svg-a748c0946003b46a082e0546c17e9f84.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/mana_potion_icon.svg"
dest_files=["res://.godot/imported/mana_potion_icon.svg-a748c0946003b46a082e0546c17e9f84.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Klinge -->
<line x1="32" y1="6" x2="32" y2="40" stroke="#7090B0" stroke-width="7" stroke-linecap="round"/>
<line x1="32" y1="6" x2="32" y2="40" stroke="#A0C0E0" stroke-width="3" stroke-linecap="round"/>
<!-- Parierstange -->
<line x1="20" y1="40" x2="44" y2="40" stroke="#4060A0" stroke-width="5" stroke-linecap="round"/>
<!-- Griff -->
<line x1="32" y1="40" x2="32" y2="53" stroke="#3B2516" stroke-width="5" stroke-linecap="round"/>
<!-- Knauf -->
<circle cx="32" cy="56" r="4" fill="#4060A0"/>
</svg>

After

Width:  |  Height:  |  Size: 587 B

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bsj8lgen1giiw"
path="res://.godot/imported/steel_sword_icon.svg-e1e6ede998445ecfd316dac81b83a7c5.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/steel_sword_icon.svg"
dest_files=["res://.godot/imported/steel_sword_icon.svg-e1e6ede998445ecfd316dac81b83a7c5.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Schild Form -->
<path d="M16 14 L48 14 L48 36 L32 56 L16 36 Z" fill="#8B6914" stroke="#5A3E08" stroke-width="2.5"/>
<!-- Holzmaserung -->
<line x1="26" y1="14" x2="26" y2="44" stroke="#7A5C10" stroke-width="1"/>
<line x1="38" y1="14" x2="38" y2="44" stroke="#7A5C10" stroke-width="1"/>
<!-- Metallbeschlag Mitte -->
<circle cx="32" cy="30" r="6" fill="none" stroke="#A0A0A0" stroke-width="2.5"/>
<circle cx="32" cy="30" r="2" fill="#A0A0A0"/>
<!-- Metallrand oben -->
<line x1="16" y1="14" x2="48" y2="14" stroke="#808080" stroke-width="3"/>
<!-- Nieten -->
<circle cx="20" cy="18" r="1.5" fill="#707070"/>
<circle cx="44" cy="18" r="1.5" fill="#707070"/>
<circle cx="20" cy="34" r="1.5" fill="#707070"/>
<circle cx="44" cy="34" r="1.5" fill="#707070"/>
</svg>

After

Width:  |  Height:  |  Size: 857 B

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dovodjtj2gb2e"
path="res://.godot/imported/wooden_shield_icon.svg-9243b141ac1930d785b6e64e9bdd401e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/wooden_shield_icon.svg"
dest_files=["res://.godot/imported/wooden_shield_icon.svg-9243b141ac1930d785b6e64e9bdd401e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -1,5 +1,5 @@
# Inventory.gd # Inventory.gd
# Verwaltet das Spieler-Inventar mit Items und Gold # Verwaltet das Spieler-Inventar mit Items (Equipment + Consumables) und Gold
extends Resource extends Resource
class_name Inventory class_name Inventory
@ -8,7 +8,7 @@ signal gold_changed(new_amount: int)
const MAX_SLOTS = 20 # Maximale Inventarplätze const MAX_SLOTS = 20 # Maximale Inventarplätze
var items: Array[Equipment] = [] var items: Array = [] # Equipment oder Consumable
var gold: int = 0 var gold: int = 0
func _init(): func _init():
@ -16,17 +16,35 @@ func _init():
gold = 0 gold = 0
# Item hinzufügen - gibt true zurück wenn erfolgreich # Item hinzufügen - gibt true zurück wenn erfolgreich
func add_item(item: Equipment) -> bool: # Consumables werden gestackt wenn möglich
func add_item(item) -> bool:
# Consumable stacking
if item is Consumable:
for existing in items:
if existing is Consumable and existing.item_name == item.item_name:
var space = existing.max_stack - existing.stack_size
if space > 0:
var transfer = mini(item.stack_size, space)
existing.stack_size += transfer
item.stack_size -= transfer
if item.stack_size <= 0:
inventory_changed.emit()
print("Item gestackt: ", existing.item_name, " x", existing.stack_size)
return true
if items.size() >= MAX_SLOTS: if items.size() >= MAX_SLOTS:
print("Inventar voll!") print("Inventar voll!")
return false return false
items.append(item) items.append(item)
inventory_changed.emit() inventory_changed.emit()
print("Item erhalten: ", item.item_name) if item is Consumable:
print("Item erhalten: ", item.item_name, " x", item.stack_size)
else:
print("Item erhalten: ", item.item_name)
return true return true
# Item an Index entfernen # Item an Index entfernen
func remove_item_at(index: int) -> Equipment: func remove_item_at(index: int):
if index < 0 or index >= items.size(): if index < 0 or index >= items.size():
return null return null
var item = items[index] var item = items[index]
@ -35,7 +53,7 @@ func remove_item_at(index: int) -> Equipment:
return item return item
# Item direkt entfernen # Item direkt entfernen
func remove_item(item: Equipment) -> bool: func remove_item(item) -> bool:
var index = items.find(item) var index = items.find(item)
if index == -1: if index == -1:
return false return false
@ -67,7 +85,7 @@ func free_slots() -> int:
return MAX_SLOTS - items.size() return MAX_SLOTS - items.size()
# Item an Index holen # Item an Index holen
func get_item(index: int) -> Equipment: func get_item(index: int):
if index < 0 or index >= items.size(): if index < 0 or index >= items.size():
return null return null
return items[index] return items[index]

View file

@ -1,8 +1,8 @@
# InventoryPanel.gd # InventoryPanel.gd
# UI für das Spieler-Inventar # UI für das Spieler-Inventar (Equipment + Consumables)
extends CanvasLayer extends CanvasLayer
signal item_selected(item: Equipment, index: int) signal item_selected(item, index: int)
var panel_visible = false var panel_visible = false
var player = null var player = null
@ -30,7 +30,8 @@ func toggle():
_refresh_inventory() _refresh_inventory()
func _on_inventory_changed(): func _on_inventory_changed():
_refresh_inventory() if panel_visible:
_refresh_inventory()
func _on_gold_changed(new_amount: int): func _on_gold_changed(new_amount: int):
gold_label.text = str(new_amount) + " Gold" gold_label.text = str(new_amount) + " Gold"
@ -66,18 +67,48 @@ func _create_slot(index: int) -> Panel:
if player.inventory and index < player.inventory.item_count(): if player.inventory and index < player.inventory.item_count():
var item = player.inventory.get_item(index) var item = player.inventory.get_item(index)
if item: if item:
# Item-Name Label var item_icon = null
var label = Label.new() if item is Consumable:
label.text = item.item_name.substr(0, 3) # Erste 3 Buchstaben item_icon = item.icon
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER elif item is Equipment:
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER item_icon = item.icon
label.add_theme_font_size_override("font_size", 10)
label.modulate = Equipment.get_rarity_color(item.rarity)
label.anchors_preset = Control.PRESET_FULL_RECT
slot.add_child(label)
# Rahmen in Seltenheitsfarbe if item_icon:
style.border_color = Equipment.get_rarity_color(item.rarity) 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)
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
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)
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) # Grüner Rand für Consumables
style.set_border_width_all(2) style.set_border_width_all(2)
# Klick-Handler # Klick-Handler
@ -88,21 +119,60 @@ func _create_slot(index: int) -> Panel:
return slot return slot
func _on_slot_clicked(event: InputEvent, index: int, item: Equipment): func _on_slot_clicked(event: InputEvent, index: int, item):
if event is InputEventMouseButton and event.pressed: if event is InputEventMouseButton and event.pressed:
if event.button_index == MOUSE_BUTTON_LEFT: if item is Equipment:
# Linksklick: Item auswählen/anlegen if event.button_index == MOUSE_BUTTON_RIGHT:
item_selected.emit(item, index) # Rechtsklick auf Equipment: Anlegen
elif event.button_index == MOUSE_BUTTON_RIGHT: if player:
# Rechtsklick: Item direkt anlegen var old_item = player.equip_item(item)
if player: player.inventory.remove_item(item)
var old_item = player.equip_item(item) if old_item:
player.inventory.remove_item(item) player.inventory.add_item(old_item)
if old_item: _refresh_inventory()
player.inventory.add_item(old_item) elif item is Consumable:
_refresh_inventory() 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 and event.shift_pressed:
# Shift+Linksklick: Auf nächsten freien Aktionsleisten-Slot legen
if player:
_assign_consumable_to_bar(item)
func _get_item_tooltip(item: Equipment) -> String: 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)")
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[Shift+Linksklick: Auf Leiste legen]"
return tooltip
func _get_equipment_tooltip(item: Equipment) -> String:
var tooltip = item.item_name + "\n" var tooltip = item.item_name + "\n"
tooltip += Equipment.get_slot_name(item.slot) + "\n" tooltip += Equipment.get_slot_name(item.slot) + "\n"
tooltip += "---\n" tooltip += "---\n"

View file

@ -3,5 +3,5 @@
extends Resource extends Resource
class_name LootEntry class_name LootEntry
@export var item: Equipment @export var item: Resource # Equipment oder Consumable
@export_range(0.0, 1.0) var drop_chance: float = 0.1 # 10% Standard @export_range(0.0, 1.0) var drop_chance: float = 0.1 # 10% Standard

View file

@ -19,6 +19,12 @@ func generate_loot() -> Dictionary:
for entry in possible_drops: for entry in possible_drops:
if randf() <= entry.drop_chance: if randf() <= entry.drop_chance:
result["items"].append(entry.item) # Consumables kopieren damit Stacks unabhängig bleiben
if entry.item is Consumable:
var copy = entry.item.duplicate()
copy.stack_size = 1
result["items"].append(copy)
else:
result["items"].append(entry.item)
return result return result

View file

@ -1,10 +1,12 @@
[gd_resource type="Resource" script_class="LootTable" format=3] [gd_resource type="Resource" script_class="LootTable" format=3 uid="uid://83hg0vwdovn8"]
[ext_resource type="Script" path="res://loot_table.gd" id="1"] [ext_resource type="Script" uid="uid://dej1tpamsi71r" path="res://loot_table.gd" id="1"]
[ext_resource type="Script" path="res://loot_entry.gd" id="2"] [ext_resource type="Script" uid="uid://cx2w8nkuoylv5" path="res://loot_entry.gd" id="2"]
[ext_resource type="Resource" path="res://equipment/iron_sword.tres" id="3"] [ext_resource type="Resource" uid="uid://vprfv2phlcbc" path="res://equipment/iron_sword.tres" id="3"]
[ext_resource type="Resource" path="res://equipment/leather_chest.tres" id="4"] [ext_resource type="Resource" uid="uid://cnijhxsjmepss" path="res://equipment/leather_chest.tres" id="4"]
[ext_resource type="Resource" path="res://equipment/iron_helm.tres" id="5"] [ext_resource type="Resource" uid="uid://d54jjc24pj3t" path="res://equipment/iron_helm.tres" id="5"]
[ext_resource type="Resource" path="res://consumables/small_hp_potion.tres" id="6"]
[ext_resource type="Resource" path="res://consumables/small_mana_potion.tres" id="7"]
[sub_resource type="Resource" id="entry_1"] [sub_resource type="Resource" id="entry_1"]
script = ExtResource("2") script = ExtResource("2")
@ -14,15 +16,23 @@ drop_chance = 0.15
[sub_resource type="Resource" id="entry_2"] [sub_resource type="Resource" id="entry_2"]
script = ExtResource("2") script = ExtResource("2")
item = ExtResource("4") item = ExtResource("4")
drop_chance = 0.1
[sub_resource type="Resource" id="entry_3"] [sub_resource type="Resource" id="entry_3"]
script = ExtResource("2") script = ExtResource("2")
item = ExtResource("5") item = ExtResource("5")
drop_chance = 0.1
[sub_resource type="Resource" id="entry_4"]
script = ExtResource("2")
item = ExtResource("6")
drop_chance = 0.4
[sub_resource type="Resource" id="entry_5"]
script = ExtResource("2")
item = ExtResource("7")
drop_chance = 0.25
[resource] [resource]
script = ExtResource("1") script = ExtResource("1")
min_gold = 2 min_gold = 2
max_gold = 8 max_gold = 8
possible_drops = [SubResource("entry_1"), SubResource("entry_2"), SubResource("entry_3")] possible_drops = Array[ExtResource("2")]([SubResource("entry_1"), SubResource("entry_2"), SubResource("entry_3"), SubResource("entry_4"), SubResource("entry_5")])

View file

@ -49,12 +49,32 @@ func _refresh_display():
var items = current_loot.get("items", []) var items = current_loot.get("items", [])
for i in range(items.size()): for i in range(items.size()):
var item = items[i] var item = items[i]
var hbox = HBoxContainer.new()
hbox.theme_override_constants = {}
# Icon wenn vorhanden
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(24, 24)
hbox.add_child(icon)
var button = Button.new() var button = Button.new()
button.text = item.item_name + " (" + Equipment.get_slot_name(item.slot) + ")" if item is Equipment:
button.modulate = Equipment.get_rarity_color(item.rarity) button.text = item.item_name + " (" + Equipment.get_slot_name(item.slot) + ")"
button.modulate = Equipment.get_rarity_color(item.rarity)
elif item is Consumable:
var count_text = " x" + str(item.stack_size) if item.stack_size > 1 else ""
button.text = item.item_name + count_text
button.modulate = Color(0.3, 0.9, 0.3)
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
button.pressed.connect(_on_loot_item.bind(i)) button.pressed.connect(_on_loot_item.bind(i))
button.tooltip_text = _get_item_tooltip(item) button.tooltip_text = _get_item_tooltip(item)
item_list.add_child(button) hbox.add_child(button)
item_list.add_child(hbox)
# Wenn kein Loot mehr da, Fenster schließen # Wenn kein Loot mehr da, Fenster schließen
if current_loot.get("gold", 0) <= 0 and items.size() == 0: if current_loot.get("gold", 0) <= 0 and items.size() == 0:

1
loot_window.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://y6v242l8n1ti

155
main_menu.gd Normal file
View file

@ -0,0 +1,155 @@
# MainMenu.gd
# Hauptmenü mit Spielstart und Einstellungen
extends CanvasLayer
signal start_game
@onready var main_panel = $MainPanel
@onready var settings_panel = $SettingsPanel
@onready var play_btn = $MainPanel/VBoxContainer/PlayBtn
@onready var settings_btn = $MainPanel/VBoxContainer/SettingsBtn
@onready var quit_btn = $MainPanel/VBoxContainer/QuitBtn
# Settings Controls
@onready var resolution_option = $SettingsPanel/VBoxContainer/ResolutionRow/ResolutionOption
@onready var window_mode_option = $SettingsPanel/VBoxContainer/WindowModeRow/WindowModeOption
@onready var vsync_check = $SettingsPanel/VBoxContainer/VsyncRow/VsyncCheck
@onready var msaa_option = $SettingsPanel/VBoxContainer/MSAARow/MSAAOption
@onready var back_btn = $SettingsPanel/VBoxContainer/BackBtn
# Auflösungen
const RESOLUTIONS = [
Vector2i(1280, 720),
Vector2i(1366, 768),
Vector2i(1600, 900),
Vector2i(1920, 1080),
Vector2i(2560, 1440),
Vector2i(3840, 2160),
]
func _ready():
get_tree().paused = true
process_mode = Node.PROCESS_MODE_ALWAYS
main_panel.visible = true
settings_panel.visible = false
play_btn.pressed.connect(_on_play)
settings_btn.pressed.connect(_on_settings)
quit_btn.pressed.connect(_on_quit)
back_btn.pressed.connect(_on_back)
_setup_resolution_options()
_setup_window_mode_options()
_setup_vsync()
_setup_msaa()
func _setup_resolution_options():
resolution_option.clear()
var current_size = DisplayServer.window_get_size()
var selected = 0
for i in range(RESOLUTIONS.size()):
var res = RESOLUTIONS[i]
resolution_option.add_item(str(res.x) + " x " + str(res.y), i)
if res == current_size:
selected = i
resolution_option.selected = selected
resolution_option.item_selected.connect(_on_resolution_changed)
func _setup_window_mode_options():
window_mode_option.clear()
window_mode_option.add_item("Fenster", 0)
window_mode_option.add_item("Randloses Fenster", 1)
window_mode_option.add_item("Vollbild", 2)
var current_mode = DisplayServer.window_get_mode()
match current_mode:
DisplayServer.WINDOW_MODE_WINDOWED:
window_mode_option.selected = 0
DisplayServer.WINDOW_MODE_FULLSCREEN:
window_mode_option.selected = 2
_:
window_mode_option.selected = 0
window_mode_option.item_selected.connect(_on_window_mode_changed)
func _setup_vsync():
var vsync_mode = DisplayServer.window_get_vsync_mode()
vsync_check.button_pressed = (vsync_mode != DisplayServer.VSYNC_DISABLED)
vsync_check.toggled.connect(_on_vsync_toggled)
func _setup_msaa():
msaa_option.clear()
msaa_option.add_item("Aus", 0)
msaa_option.add_item("2x", 1)
msaa_option.add_item("4x", 2)
msaa_option.add_item("8x", 3)
var current_msaa = get_viewport().msaa_3d
match current_msaa:
Viewport.MSAA_DISABLED:
msaa_option.selected = 0
Viewport.MSAA_2X:
msaa_option.selected = 1
Viewport.MSAA_4X:
msaa_option.selected = 2
Viewport.MSAA_8X:
msaa_option.selected = 3
msaa_option.item_selected.connect(_on_msaa_changed)
func _on_resolution_changed(index: int):
var res = RESOLUTIONS[index]
DisplayServer.window_set_size(res)
# Fenster zentrieren
var screen_size = DisplayServer.screen_get_size()
var window_pos = (screen_size - res) / 2
DisplayServer.window_set_position(window_pos)
func _on_window_mode_changed(index: int):
match index:
0: # Fenster
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
1: # Randloses Fenster
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, true)
var screen_size = DisplayServer.screen_get_size()
DisplayServer.window_set_size(screen_size)
DisplayServer.window_set_position(Vector2i.ZERO)
2: # Vollbild
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
func _on_vsync_toggled(enabled: bool):
if enabled:
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
else:
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
func _on_msaa_changed(index: int):
match index:
0:
get_viewport().msaa_3d = Viewport.MSAA_DISABLED
1:
get_viewport().msaa_3d = Viewport.MSAA_2X
2:
get_viewport().msaa_3d = Viewport.MSAA_4X
3:
get_viewport().msaa_3d = Viewport.MSAA_8X
func _on_play():
start_game.emit()
get_tree().paused = false
queue_free()
func _on_settings():
main_panel.visible = false
settings_panel.visible = true
func _on_back():
settings_panel.visible = false
main_panel.visible = true
func _on_quit():
get_tree().quit()

1
main_menu.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://bw65mud8idif0

161
main_menu.tscn Normal file
View file

@ -0,0 +1,161 @@
[gd_scene format=3 uid="uid://main_menu"]
[ext_resource type="Script" path="res://main_menu.gd" id="1_menu"]
[node name="MainMenu" type="CanvasLayer"]
script = ExtResource("1_menu")
[node name="MainPanel" type="Panel" parent="."]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -200.0
offset_top = -180.0
offset_right = 200.0
offset_bottom = 180.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="MainPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 30.0
offset_top = 30.0
offset_right = -30.0
offset_bottom = -30.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 20
[node name="Title" type="Label" parent="MainPanel/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "DungeonCrawler"
horizontal_alignment = 1
[node name="Spacer" type="Control" parent="MainPanel/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="PlayBtn" type="Button" parent="MainPanel/VBoxContainer"]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Spielen"
[node name="SettingsBtn" type="Button" parent="MainPanel/VBoxContainer"]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Einstellungen"
[node name="QuitBtn" type="Button" parent="MainPanel/VBoxContainer"]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Beenden"
[node name="SettingsPanel" type="Panel" parent="."]
visible = false
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -250.0
offset_top = -220.0
offset_right = 250.0
offset_bottom = 220.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="SettingsPanel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 20.0
offset_top = 20.0
offset_right = -20.0
offset_bottom = -20.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 15
[node name="Title" type="Label" parent="SettingsPanel/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "Einstellungen"
horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="SettingsPanel/VBoxContainer"]
layout_mode = 2
[node name="ResolutionRow" type="HBoxContainer" parent="SettingsPanel/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="SettingsPanel/VBoxContainer/ResolutionRow"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 16
text = "Auflösung"
[node name="ResolutionOption" type="OptionButton" parent="SettingsPanel/VBoxContainer/ResolutionRow"]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
theme_override_font_sizes/font_size = 14
[node name="WindowModeRow" type="HBoxContainer" parent="SettingsPanel/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="SettingsPanel/VBoxContainer/WindowModeRow"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 16
text = "Fenstermodus"
[node name="WindowModeOption" type="OptionButton" parent="SettingsPanel/VBoxContainer/WindowModeRow"]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
theme_override_font_sizes/font_size = 14
[node name="VsyncRow" type="HBoxContainer" parent="SettingsPanel/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="SettingsPanel/VBoxContainer/VsyncRow"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 16
text = "VSync"
[node name="VsyncCheck" type="CheckBox" parent="SettingsPanel/VBoxContainer/VsyncRow"]
layout_mode = 2
text = "Aktiviert"
[node name="MSAARow" type="HBoxContainer" parent="SettingsPanel/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="SettingsPanel/VBoxContainer/MSAARow"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 16
text = "Anti-Aliasing"
[node name="MSAAOption" type="OptionButton" parent="SettingsPanel/VBoxContainer/MSAARow"]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
theme_override_font_sizes/font_size = 14
[node name="Spacer" type="Control" parent="SettingsPanel/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="BackBtn" type="Button" parent="SettingsPanel/VBoxContainer"]
custom_minimum_size = Vector2(0, 45)
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "Zurück"

116
player.gd
View file

@ -25,8 +25,16 @@ const MAX_LEVEL_DIFF_MOD = 0.5 # Maximal 50% Modifikation
var max_hp = 100 var max_hp = 100
var current_hp = 100 var current_hp = 100
var max_resource = 0 # Klassen-Ressource (Mana/Energie/Wut), 0 = keine
var current_resource = 0
var target = null # Aktuell markierter Gegner 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]
var potion_cooldown: float = 0.0
const POTION_COOLDOWN_TIME = 1.0
# Equipment System # Equipment System
var equipment: Dictionary = { var equipment: Dictionary = {
Equipment.Slot.HEAD: null, Equipment.Slot.HEAD: null,
@ -67,8 +75,10 @@ func _ready():
# Stats aus Klasse berechnen # Stats aus Klasse berechnen
_calculate_stats() _calculate_stats()
current_hp = max_hp current_hp = max_hp
current_resource = max_resource
hud.update_health(current_hp, max_hp) hud.update_health(current_hp, max_hp)
hud.update_resource(current_resource, max_resource, get_resource_name())
hud.update_level(level, current_xp, xp_to_next_level) hud.update_level(level, current_xp, xp_to_next_level)
hud.set_active_slot(0) hud.set_active_slot(0)
# Icons für Skills setzen # Icons für Skills setzen
@ -96,6 +106,7 @@ func _calculate_stats():
intelligence = 10 intelligence = 10
stamina = 10 stamina = 10
max_hp = 100 max_hp = 100
max_resource = 0
return return
# Stats = Basis + (Level-1) * Zuwachs pro Level # Stats = Basis + (Level-1) * Zuwachs pro Level
@ -107,11 +118,36 @@ func _calculate_stats():
# HP aus Stamina berechnen # HP aus Stamina berechnen
max_hp = stamina * CharacterClass.HP_PER_STAMINA max_hp = stamina * CharacterClass.HP_PER_STAMINA
# Klassen-Ressource berechnen
_calculate_resource()
# Equipment-Boni hinzufügen # Equipment-Boni hinzufügen
_apply_equipment_stats() _apply_equipment_stats()
print("Stats berechnet - STR: ", strength, " AGI: ", agility, " INT: ", intelligence, " STA: ", stamina, " ARM: ", armor, " HP: ", max_hp) print("Stats berechnet - STR: ", strength, " AGI: ", agility, " INT: ", intelligence, " STA: ", stamina, " ARM: ", armor, " HP: ", max_hp, " RES: ", max_resource)
# Klassen-Ressource berechnen (Mana aus INT, Energie fix, Wut fix)
func _calculate_resource():
if character_class == null or character_class.resource_type == CharacterClass.ResourceType.NONE:
max_resource = 0
return
match character_class.resource_type:
CharacterClass.ResourceType.MANA:
max_resource = character_class.base_resource + intelligence * CharacterClass.MANA_PER_INT
CharacterClass.ResourceType.ENERGY:
max_resource = character_class.base_resource # Fix, skaliert nicht
CharacterClass.ResourceType.RAGE:
max_resource = character_class.base_resource # Fix, skaliert nicht
# Name der Klassen-Ressource
func get_resource_name() -> String:
if character_class == null:
return ""
match character_class.resource_type:
CharacterClass.ResourceType.MANA: return "Mana"
CharacterClass.ResourceType.ENERGY: return "Energie"
CharacterClass.ResourceType.RAGE: return "Wut"
return ""
# Equipment-Stats auf Charakter anwenden # Equipment-Stats auf Charakter anwenden
func _apply_equipment_stats(): func _apply_equipment_stats():
@ -137,8 +173,9 @@ func _apply_equipment_stats():
intelligence += bonus_int intelligence += bonus_int
stamina += bonus_sta stamina += bonus_sta
# HP neu berechnen mit Equipment-Stamina # HP und Ressource neu berechnen mit Equipment-Boni
max_hp = stamina * CharacterClass.HP_PER_STAMINA max_hp = stamina * CharacterClass.HP_PER_STAMINA
_calculate_resource()
# Equipment anlegen # Equipment anlegen
func equip_item(item: Equipment) -> Equipment: func equip_item(item: Equipment) -> Equipment:
@ -202,13 +239,15 @@ func _level_up():
# Stats neu berechnen # Stats neu berechnen
_calculate_stats() _calculate_stats()
# HP vollständig auffüllen bei Level-Up # HP und Ressource vollständig auffüllen bei Level-Up
current_hp = max_hp current_hp = max_hp
current_resource = max_resource
hud.update_health(current_hp, max_hp) hud.update_health(current_hp, max_hp)
hud.update_resource(current_resource, max_resource, get_resource_name())
# Character Panel aktualisieren falls offen # Character Panel aktualisieren falls offen
character_panel.update_stats(self) character_panel.update_stats(self)
print("LEVEL UP! Jetzt Level ", level, " - HP voll aufgefüllt!") print("LEVEL UP! Jetzt Level ", level, " - HP und Ressource voll aufgefüllt!")
# XP-Kurve: Jedes Level braucht mehr XP # XP-Kurve: Jedes Level braucht mehr XP
func _calculate_xp_for_level(target_level: int) -> int: func _calculate_xp_for_level(target_level: int) -> int:
@ -216,6 +255,9 @@ func _calculate_xp_for_level(target_level: int) -> int:
# Handler für HUD-Slot-Klicks # Handler für HUD-Slot-Klicks
func _on_slot_clicked(slot_index: int): func _on_slot_clicked(slot_index: int):
_use_action_slot(slot_index)
func _use_action_slot(slot_index: int):
match slot_index: match slot_index:
0: # Autoattack manuell starten 0: # Autoattack manuell starten
if target != null and global_cooldown <= 0: if target != null and global_cooldown <= 0:
@ -223,6 +265,14 @@ func _on_slot_clicked(slot_index: int):
perform_autoattack() perform_autoattack()
1: # Heavy Strike 1: # Heavy Strike
use_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()
# Schaden am Spieler abziehen und HP-Leiste aktualisieren # Schaden am Spieler abziehen und HP-Leiste aktualisieren
func take_damage(amount): func take_damage(amount):
@ -258,6 +308,55 @@ func heal(amount):
current_hp = clamp(current_hp + amount, 0, max_hp) current_hp = clamp(current_hp + amount, 0, max_hp)
hud.update_health(current_hp, max_hp) hud.update_health(current_hp, max_hp)
# Ressource wiederherstellen (Mana/Energie/Wut)
func restore_mana(amount):
current_resource = clamp(current_resource + amount, 0, max_resource)
hud.update_resource(current_resource, max_resource, get_resource_name())
# Ressource verbrauchen
func spend_resource(amount) -> bool:
if current_resource < amount:
print("Nicht genug " + get_resource_name() + "!")
return false
current_resource = clamp(current_resource - amount, 0, max_resource)
hud.update_resource(current_resource, max_resource, get_resource_name())
return true
# Consumable benutzen (Trank etc.)
func use_consumable(consumable: Consumable) -> bool:
if potion_cooldown > 0:
print("Trank noch im Cooldown!")
return false
if consumable.use(self):
potion_cooldown = consumable.cooldown
if consumable.stack_size <= 0:
return true # Verbraucht
return false
# Consumable auf Aktionsleiste legen
func assign_to_action_bar(slot_index: int, consumable: Consumable):
if slot_index < 2 or slot_index > 8:
return # Slot 0+1 sind reserviert für Skills
action_bar_items[slot_index] = consumable
if consumable and consumable.icon:
hud.set_slot_icon_texture(slot_index, consumable.icon)
hud.set_slot_stack_count(slot_index, consumable.stack_size)
else:
hud.clear_slot_icon(slot_index)
hud.set_slot_stack_count(slot_index, 0)
# Aktionsleiste Stack-Counts aktualisieren
func _update_action_bar_stacks():
for i in range(2, 9):
var item = action_bar_items[i]
if item is Consumable:
if item.stack_size <= 0:
action_bar_items[i] = null
hud.clear_slot_icon(i)
hud.set_slot_stack_count(i, 0)
else:
hud.set_slot_stack_count(i, item.stack_size)
# Loot empfangen und Fenster anzeigen # Loot empfangen und Fenster anzeigen
func receive_loot(loot: Dictionary, world_pos: Vector3): func receive_loot(loot: Dictionary, world_pos: Vector3):
loot_window.show_loot(loot, world_pos) loot_window.show_loot(loot, world_pos)
@ -437,6 +536,8 @@ func _physics_process(delta):
# Skill-Cooldowns herunterzählen # Skill-Cooldowns herunterzählen
if heavy_strike_cooldown > 0: if heavy_strike_cooldown > 0:
heavy_strike_cooldown -= delta heavy_strike_cooldown -= delta
if potion_cooldown > 0:
potion_cooldown -= delta
# HUD Cooldowns aktualisieren # HUD Cooldowns aktualisieren
hud.set_slot_cooldown(0, global_cooldown) # Slot 1: GCD (Autoattack) hud.set_slot_cooldown(0, global_cooldown) # Slot 1: GCD (Autoattack)
@ -469,18 +570,25 @@ func _physics_process(delta):
use_heavy_strike() use_heavy_strike()
if Input.is_action_just_pressed("action_3"): if Input.is_action_just_pressed("action_3"):
hud.set_active_slot(2) hud.set_active_slot(2)
_use_action_slot(2)
if Input.is_action_just_pressed("action_4"): if Input.is_action_just_pressed("action_4"):
hud.set_active_slot(3) hud.set_active_slot(3)
_use_action_slot(3)
if Input.is_action_just_pressed("action_5"): if Input.is_action_just_pressed("action_5"):
hud.set_active_slot(4) hud.set_active_slot(4)
_use_action_slot(4)
if Input.is_action_just_pressed("action_6"): if Input.is_action_just_pressed("action_6"):
hud.set_active_slot(5) hud.set_active_slot(5)
_use_action_slot(5)
if Input.is_action_just_pressed("action_7"): if Input.is_action_just_pressed("action_7"):
hud.set_active_slot(6) hud.set_active_slot(6)
_use_action_slot(6)
if Input.is_action_just_pressed("action_8"): if Input.is_action_just_pressed("action_8"):
hud.set_active_slot(7) hud.set_active_slot(7)
_use_action_slot(7)
if Input.is_action_just_pressed("action_9"): if Input.is_action_just_pressed("action_9"):
hud.set_active_slot(8) hud.set_active_slot(8)
_use_action_slot(8)
# TEST: T drücken = 10 Schaden # TEST: T drücken = 10 Schaden
if Input.is_action_just_pressed("test_damage"): if Input.is_action_just_pressed("test_damage"):

View file

@ -3,6 +3,7 @@
extends Node3D extends Node3D
const ENEMY_SCENE = preload("res://enemy.tscn") const ENEMY_SCENE = preload("res://enemy.tscn")
const MAIN_MENU = preload("res://main_menu.tscn")
const CLASS_SELECTION_MENU = preload("res://class_selection_menu.tscn") const CLASS_SELECTION_MENU = preload("res://class_selection_menu.tscn")
const RESPAWN_TIME = 5.0 const RESPAWN_TIME = 5.0
@ -16,10 +17,18 @@ const GOBLIN_LOOT = preload("res://loot_tables/goblin_loot.tres")
@onready var player = $Player @onready var player = $Player
func _ready(): func _ready():
# Klassenauswahl-Menü anzeigen # Hauptmenü anzeigen
var main_menu = MAIN_MENU.instantiate()
add_child(main_menu)
main_menu.start_game.connect(_on_start_game)
# Nach Hauptmenü: Klassenauswahl anzeigen
func _on_start_game():
var menu = CLASS_SELECTION_MENU.instantiate() var menu = CLASS_SELECTION_MENU.instantiate()
add_child(menu) add_child(menu)
menu.class_selected.connect(_on_class_selected) menu.class_selected.connect(_on_class_selected)
# Spiel wieder pausieren für Klassenauswahl
get_tree().paused = true
# Klasse ausgewählt: Spieler initialisieren # Klasse ausgewählt: Spieler initialisieren
func _on_class_selected(character_class: CharacterClass): func _on_class_selected(character_class: CharacterClass):

View file

@ -3,7 +3,6 @@
[ext_resource type="PackedScene" uid="uid://dniyuebl8yhtv" path="res://player.tscn" id="1_f3sb7"] [ext_resource type="PackedScene" uid="uid://dniyuebl8yhtv" path="res://player.tscn" id="1_f3sb7"]
[ext_resource type="Script" uid="uid://cx56h588mfsk0" path="res://world.gd" id="1_tlwt5"] [ext_resource type="Script" uid="uid://cx56h588mfsk0" path="res://world.gd" id="1_tlwt5"]
[ext_resource type="PackedScene" uid="uid://cvojaeanxugfj" path="res://enemy.tscn" id="2_fj7yv"] [ext_resource type="PackedScene" uid="uid://cvojaeanxugfj" path="res://enemy.tscn" id="2_fj7yv"]
[ext_resource type="Script" uid="uid://bg5qs3pcfp7p7" path="res://enemy.gd" id="4_aqk2v"]
[sub_resource type="BoxShape3D" id="BoxShape3D_fj7yv"] [sub_resource type="BoxShape3D" id="BoxShape3D_fj7yv"]
size = Vector3(200, 0.5, 200) size = Vector3(200, 0.5, 200)
@ -16,12 +15,12 @@ size = Vector3(200, 0.5, 200)
[node name="World" type="Node3D" unique_id=2007838514] [node name="World" type="Node3D" unique_id=2007838514]
script = ExtResource("1_tlwt5") script = ExtResource("1_tlwt5")
[node name="StaticBody3D" type="StaticBody3D" parent="." unique_id=2101916269] [node name="Boden" type="StaticBody3D" parent="." unique_id=2101916269]
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" unique_id=1873339390] [node name="CollisionShape3D" type="CollisionShape3D" parent="Boden" unique_id=1873339390]
shape = SubResource("BoxShape3D_fj7yv") shape = SubResource("BoxShape3D_fj7yv")
[node name="MeshInstance3D" type="MeshInstance3D" parent="StaticBody3D" unique_id=1214783061] [node name="MeshInstance3D" type="MeshInstance3D" parent="Boden" unique_id=1214783061]
mesh = SubResource("BoxMesh_tlwt5") mesh = SubResource("BoxMesh_tlwt5")
[node name="Player" parent="." unique_id=937297102 instance=ExtResource("1_f3sb7")] [node name="Player" parent="." unique_id=937297102 instance=ExtResource("1_f3sb7")]
@ -32,7 +31,6 @@ transform = Transform3D(-45, 0, 0, 0, -45, 0, 0, 0, -45, 0, 0, 0)
[node name="Enemy" parent="." unique_id=332011146 instance=ExtResource("2_fj7yv")] [node name="Enemy" parent="." unique_id=332011146 instance=ExtResource("2_fj7yv")]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0.3, -7.038) transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0.3, -7.038)
script = ExtResource("4_aqk2v")
[node name="NavigationRegion3D" type="NavigationRegion3D" parent="." unique_id=827244005] [node name="NavigationRegion3D" type="NavigationRegion3D" parent="." unique_id=827244005]
navigation_mesh = SubResource("NavigationMesh_fj7yv") navigation_mesh = SubResource("NavigationMesh_fj7yv")