This commit is contained in:
Andre 2026-03-16 18:06:12 +01:00
commit 716816e1e5
128 changed files with 6009 additions and 244 deletions

View file

@ -23,7 +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 9 | Aktionsleiste Slot auswählen |
| 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 |
@ -32,7 +35,7 @@ Gegner bekämpfen und ihre Charaktere mit verschiedenen Klassen und Ausrüstunge
## Szenen-Struktur
### world.tscn
Hauptszene der Spielwelt.
Hauptszene der Spielwelt. Zeigt bei Start das Hauptmenü (Einstellungen), dann Klassenauswahl.
```
World (Node3D)
├── Player (player.tscn)
@ -45,21 +48,25 @@ World (Node3D)
```
### player.tscn
Der Spielercharakter.
Der Spielercharakter mit allen UI-Panels.
```
Player (CharacterBody3D)
├── [Kenney GLB Modell]
├── PlayerModel (warrior.fbx — Mixamo Charakter mit Skeleton + AnimationPlayer)
├── CollisionShape3D
├── CameraPivot (Node3D)
│ └── Camera3D
└── HUD (hud.tscn)
├── HUD (hud.tscn)
├── CharacterPanel (character_panel.tscn)
├── InventoryPanel (inventory_panel.tscn)
├── LootWindow (loot_window.tscn)
└── SkillPanel (skill_panel.tscn)
```
### enemy.tscn
Ein Gegner.
Ein Gegner mit Level-basiertem Stats-System.
```
Enemy (CharacterBody3D)
├── [Kenney GLB Modell]
├── EnemyModel (warrior.fbx — Mixamo Charakter mit Skeleton + AnimationPlayer)
├── CollisionShape3D
├── Area3D
│ └── CollisionShape3D
@ -67,188 +74,414 @@ Enemy (CharacterBody3D)
└── HealthLabel (Label3D)
```
### hud.tscn
Die Spieler-UI.
```
HUD (CanvasLayer)
└── Control
├── HealthBar (ProgressBar)
│ └── HealthLabel (Label)
└── ActionBar (HBoxContainer)
└── A1 A9 (Panel)
└── Label (19)
```
---
## Scripts
## Klassen-System
### player.gd
Steuert den Spielercharakter.
**Variablen:**
| Variable | Typ | Beschreibung |
|---|---|---|
| SPEED | const float | Bewegungsgeschwindigkeit (5.0) |
| JUMP_VELOCITY | const float | Sprungkraft (4.5) |
| GRAVITY | const float | Schwerkraft (9.8) |
| max_hp | int | Maximale HP (100) |
| current_hp | int | Aktuelle HP |
| can_attack | bool | Angriff möglich (Cooldown-Flag) |
| target | Node | Aktuell markierter Gegner |
| equipped_weapon | Weapon | Ausgerüstete Waffe (null = unbewaffnet) |
**Funktionen:**
| Funktion | Beschreibung |
|---|---|
| take_damage(amount) | Schaden abziehen, HP-Leiste aktualisieren |
| heal(amount) | HP heilen |
| die() | Spieler gestorben |
| get_attack_damage() | Schaden berechnen (Waffe oder 1) |
| get_attack_range() | Reichweite (Waffe oder 1.5) |
| get_attack_cooldown() | Cooldown (Waffe oder 1.5s) |
| set_target(target, start_attack) | Ziel markieren, optional Autoattack starten |
| autoattack() | Wiederholt angreifen solange Ziel gültig |
| _try_select_target(start_attack) | Raycast von Kamera auf Mausposition |
---
### enemy.gd
Steuert den Gegner.
**Variablen:**
| Variable | Typ | Beschreibung |
|---|---|---|
| SPEED | const float | Bewegungsgeschwindigkeit (3.0) |
| ATTACK_DAMAGE | const int | Schaden pro Angriff (5) |
| ATTACK_RANGE | const float | Angriffsreichweite (1.5) |
| ATTACK_COOLDOWN | const float | Angriffspause (2.0s) |
| max_hp | int | Maximale HP (50) |
| target | Node | Ziel des Gegners (Spieler) |
**Funktionen:**
| Funktion | Beschreibung |
|---|---|
| take_damage(amount) | Schaden nehmen, bei 0 HP sterben |
| die() | Gegner aus Szene entfernen |
| show_health() | HP-Label einblenden |
| hide_health() | HP-Label ausblenden |
| _attack() | Angriff mit Cooldown |
---
### camera_pivot.gd
Steuert die Third-Person-Kamera.
**Einstellbare Parameter (Export):**
| Parameter | Standard | Beschreibung |
|---|---|---|
| sensitivity | 0.3 | Mausempfindlichkeit |
| min_pitch | -40° | Maximale Neigung nach unten |
| max_pitch | 20° | Maximale Neigung nach oben |
| min_zoom | 5.0 | Minimale Kameraentfernung |
| max_zoom | 20.0 | Maximale Kameraentfernung |
| zoom_speed | 1.0 | Zoom pro Mausrad-Schritt |
---
### hud.gd
Verwaltet die Spieler-UI.
**Funktionen:**
| Funktion | Beschreibung |
|---|---|
| update_health(current, max) | HP-Leiste und Text aktualisieren |
| set_active_slot(index) | Aktions-Slot 0.1s golden hervorheben |
---
## Ressourcen-Klassen
### Weapon (resources/weapon.gd)
Definiert eine Waffe als Godot-Ressource (.tres).
### CharacterClass (character_class.gd)
Resource-Klasse die eine spielbare Klasse definiert.
| Property | Typ | Beschreibung |
|---|---|---|
| name | String | Waffenname |
| weapon_type | WeaponType | UNARMED, SWORD, AXE, MACE, DAGGER, STAFF, BOW |
| min_damage | int | Minimaler Schaden |
| max_damage | int | Maximaler Schaden |
| attack_speed | float | Angriffsgeschwindigkeit (Cooldown) |
| range | float | Angriffsreichweite |
| icon | Texture2D | Icon für die Aktionsleiste |
| class_name_de | String | Deutscher Klassenname |
| main_stat | MainStat | STRENGTH, AGILITY oder INTELLIGENCE |
| base_strength/agility/intelligence/stamina | int | Basis-Stats auf Level 1 |
| strength/agility/intelligence/stamina_per_level | float | Stat-Zuwachs pro Level |
| unarmed_min_damage | int | Unbewaffneter Mindestschaden |
| unarmed_max_damage | int | Unbewaffneter Maximalschaden |
| unarmed_attack_speed | float | Unbewaffnete Angriffsgeschwindigkeit |
| resource_type | ResourceType | NONE, MANA, RAGE, ENERGY |
| base_resource | int | Basis-Ressource auf Level 1 |
### Attack (resources/attack.gd)
Definiert einen Angriff — Schaden kommt von der ausgerüsteten Waffe.
**Konstanten:**
- `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
### Verfügbare Klassen (classes/)
| Klasse | Main-Stat | Ressource | STR | AGI | INT | STA | Unbewaffnet |
|---|---|---|---|---|---|---|---|
| Krieger | Stärke | Keine | 15 | 8 | 5 | 12 | 2-4 Schaden, 1.8s |
| Schurke | Beweglichkeit | Energie (100) | 8 | 15 | 7 | 10 | 1-3 Schaden, 1.5s |
| 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)
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.
---
## Level-System
- **XP-Kurve:** Level N benötigt `100 * N` XP (Level 2: 100, Level 3: 200, ...)
- **Stats pro Level:** Basierend auf Klassen-Zuwachsraten
- **HP bei Level-Up:** Werden vollständig aufgefüllt (HP + Klassen-Ressource)
- **Character Panel:** Aktualisiert sich automatisch bei Level-Up
---
## Equipment-System
### Equipment (equipment.gd)
Resource-Klasse für alle Ausrüstungsgegenstände.
**Slots:** HEAD, CHEST, HANDS, LEGS, FEET, WEAPON, OFFHAND
**Seltenheiten:**
| Seltenheit | Farbe |
|---|---|
| COMMON | Weiß |
| UNCOMMON | Grün |
| RARE | Blau |
| EPIC | Lila |
**Stats auf Equipment:**
| Property | Typ | Beschreibung |
|---|---|---|
| item_name | String | Name des Items |
| slot | Slot | Equipment-Slot |
| rarity | Rarity | Seltenheitsstufe |
| armor | int | Rüstungswert |
| strength/agility/intelligence/stamina | int | Stat-Boni |
| haste | float | Angriffsgeschwindigkeits-Bonus (0.1 = 10%) |
| min_damage / max_damage | int | Waffenschaden (nur Waffen) |
| attack_speed | float | Angriffsgeschwindigkeit (nur Waffen) |
| weapon_range | float | Reichweite (nur Waffen) |
### Vorhandene Equipment-Items (equipment/)
- **iron_sword.tres** — Eisenschwert (Waffe)
- **steel_sword.tres** — Stahlschwert (Waffe, besser)
- **leather_chest.tres** — Lederrüstung (Brust)
- **iron_helm.tres** — Eisenhelm (Kopf)
- **wooden_shield.tres** — Holzschild (Nebenhand)
### Character Panel (character_panel.gd, C-Taste)
Zeigt zwei Spalten:
- **Stats-Spalte:** Stärke, Beweglichkeit, Intelligenz, Ausdauer, Rüstung, HP
- **Equipment-Spalte:** Icon-Grid mit Tooltips bei Hover, Waffenschaden + DPS
Unbewaffnet: Zeigt klassenabhängige Schadenswerte.
---
## Consumable-System
### Consumable (consumable.gd)
Resource-Klasse für verbrauchbare Items (Tränke, Essen, etc.).
| Property | Typ | Beschreibung |
|---|---|---|
| name | String | Angriffsname |
| damage_type | DamageType | PHYSICAL, FIRE, ICE, LIGHTNING, POISON |
| icon | Texture2D | Icon für die Aktionsleiste |
| 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
### Inventory (inventory.gd)
Resource-Klasse für das Spieler-Inventar.
- **MAX_SLOTS:** 20 Inventarplätze
- **Gold:** Währung, wird im HUD angezeigt
- **Signals:** `inventory_changed`, `gold_changed`
**Funktionen:**
| Funktion | Beschreibung |
|---|---|
| add_item(item) | Item hinzufügen (false wenn voll) |
| remove_item(item) | Item entfernen |
| add_gold(amount) | Gold hinzufügen |
| spend_gold(amount) | Gold ausgeben (false wenn nicht genug) |
| is_full() | Prüfen ob Inventar voll |
### Inventory Panel (inventory_panel.gd, I-Taste)
- 5x4 Grid mit Item-Slots
- Gold-Anzeige im Header
- Items in Seltenheitsfarbe
- Tooltips mit vollständigen Item-Stats
- **Rechtsklick** auf Item: Direkt anlegen (tauscht mit aktuellem Equipment)
---
## Loot-System
### LootTable (loot_table.gd)
Resource die mögliche Drops eines Gegners definiert.
| Property | Typ | Beschreibung |
|---|---|---|
| min_gold | int | Minimaler Gold-Drop |
| max_gold | int | Maximaler Gold-Drop |
| possible_drops | Array[LootEntry] | Mögliche Item-Drops |
### LootEntry (loot_entry.gd)
Ein einzelner Drop-Eintrag.
| Property | Typ | Beschreibung |
|---|---|---|
| item | Resource | Das droppbare Item (Equipment oder Consumable) |
| drop_chance | float | Wahrscheinlichkeit (0.0 - 1.0) |
### Vorhandene LootTables (loot_tables/)
- **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%)
### LootWindow (loot_window.gd)
- Erscheint automatisch wenn ein Gegner stirbt
- Zeigt Gold und gedropte Items
- Einzelne Items per Klick aufheben
- "Alles aufheben" Button
- Gold skaliert mit Gegner-Level
---
## Kampfsystem
### Autoattack
- Rechtsklick auf Gegner → Ziel markieren + Autoattack startet
- Linksklick auf Gegner → nur Ziel markieren
- Autoattack läuft automatisch im Takt der Waffen-Angriffsgeschwindigkeit
- Ohne Waffe: 1 Schaden, 1.5s Cooldown, 1.5 Reichweite
- Mit Waffe: Schaden = zufällig zwischen min/max, Speed und Range von der Waffe
### Global Cooldown (GCD)
Alle Aktionen (Autoattack + Skills) teilen sich einen GCD.
### Schadenstypen (geplant)
- **PHYSICAL** normaler Schaden
- **FIRE** Feuerschaden (z.B. Magier)
- **ICE** Eisschaden
- **LIGHTNING** Blitzschaden
- **POISON** Giftschaden (Schaden über Zeit geplant)
**Formel:** `GCD = Waffen-Attackspeed / (1 + Haste)`
Beispiel: Waffe mit 1.5s + 50% Haste → `1.5 / 1.5 = 1.0s`
### Schadensberechnung
1. **Basisschaden:** Zufällig zwischen Waffen min/max (oder unbewaffnet klassenabhängig)
2. **Stat-Bonus:** `Main-Stat * 0.5`
3. **Rüstungsreduktion (Nahkampf):** `Reduktion = Rüstung / (Rüstung + 50)`
4. **Level-Differenz:** `±10% pro Level, max ±50%`
5. **Mindestschaden:** Immer mindestens 1
### DPS-Berechnung
`DPS = (Durchschnittsschaden + Stat-Bonus) / GCD`
### Skills
#### Autoattack (Taste 1, Krieger/Schurke)
- **Schaden:** Waffenschaden oder klassenabhängig unbewaffnet + Main-Stat Bonus
- **Cooldown:** GCD (Waffen-Attackspeed / Haste)
- **Reichweite:** Waffen-Reichweite oder 3.0 (unbewaffnet)
- Automatisch per Rechtsklick oder manuell per Taste 1
- **Animation:** autoattack
#### Zauberstab (Taste 1, Magier)
- **Schaden:** Waffenschaden + INT (magisch, ignoriert Rüstung)
- **Cooldown:** GCD
- **Reichweite:** 20.0
- Deaktiviert Autoattack, exklusiver Fernkampf-Modus
- **Animation:** autoattack
#### Frostblitz (Taste 2, Magier)
- **Schaden:** 12-20 + INT (magisch)
- **Castzeit:** 1.5 Sekunden (unterbrechbar durch Bewegung/Schaden/Springen)
- **Manakosten:** 20
- **Cooldown:** 2.5 Sekunden
- **Reichweite:** 20.0
- Castbar wird über der Aktionsleiste angezeigt
#### Heavy Strike (Taste 2, Krieger/Schurke)
- **Schaden:** 10-15 + Main-Stat Bonus
- **Cooldown:** 3 Sekunden (eigener Cooldown, löst auch GCD aus)
- **Reichweite:** 4.0
- Aktiviert automatisch Autoattack danach
- **Animation:** heavy_strike
### UI & Icons
- 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
- Gelber Highlight-Rand beim Drag über Slots
---
## Gegner-System (enemy.gd)
### Stats
Level-basiert mit automatischer Skalierung:
| Stat | Formel |
|---|---|
| Stärke | base_strength + (level-1) * 2 |
| Ausdauer | base_stamina + (level-1) * 3 |
| Rüstung | base_armor + (level-1) * 2 |
| HP | Ausdauer * 10 |
| Schaden | Stärke * 0.5 + 2 |
| XP-Belohnung | 25 * Level |
### KI-Verhalten (State Machine)
| State | Beschreibung |
|---|---|
| PATROL | Zufällig im Radius um Spawn-Position herumlaufen |
| CHASE | Spieler verfolgen (Aggro-Range: 8.0) |
| ATTACK | Angreifen wenn in Reichweite (1.5) |
### Respawn
- Gegner spawnen nach 5 Sekunden am Ursprungsort neu
- Verwaltet durch world.gd
### Loot-Drops
- Jeder Gegner hat eine optionale `loot_table` (LootTable Resource)
- Gold skaliert mit Gegner-Level
- Ohne LootTable: Standard-Gold-Drop (1-3 * Level)
---
## HUD (hud.gd)
| Element | Beschreibung |
|---|---|
| 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 |
| XPBar | Blaue XP-Leiste |
| GoldLabel | Gold-Anzeige in Goldfarbe |
| ActionBar | 9 Slots mit Icons, Cooldowns, Klick-Support, Stack-Anzeige für Consumables |
---
## Animations-System
### Mixamo-Integration
Charaktermodelle stammen von Mixamo (warrior.fbx) und werden mit separaten Animations-FBX-Dateien kombiniert.
**Lade-Prozess (player.gd / enemy.gd):**
1. `_setup_animations()` findet den AnimationPlayer im Modell
2. Jede Animations-FBX wird als PackedScene geladen
3. Die Animation wird extrahiert und der AnimationLibrary hinzugefügt
4. `_update_animation()` wählt basierend auf Bewegungszustand die passende Animation
### Verfügbare Animationen (assets/animations/)
| Animation | Datei | Verwendung |
|---|---|---|
| walk | Walking.fbx | Vorwärts laufen |
| walk_back | Walking Backwards.fbx | Rückwärts laufen |
| strafe_left | Left Strafe Walking.fbx | Links seitwärts |
| strafe_right | Right Strafe Walking.fbx | Rechts seitwärts |
| jump | Jumping.fbx | Springen |
| autoattack | Autoattack.fbx | Autoattack / Zauberstab |
| heavy_strike | Heavy Strike.fbx | Heavy Strike Skill |
| die | Dying Backwards.fbx | Tod (Spieler + Gegner) |
### Offene Punkte
- [ ] Enemy Walk-Animation: Track-Pfade matchen noch nicht korrekt (Debug in Arbeit)
- [ ] Idle-Animation fehlt (kein FBX vorhanden)
- [ ] Cast-Animation fehlt (für Frostblitz)
---
## Geplante Features
### Klassen-System
Jede Klasse hat unterschiedliche Basis-Stats und verfügbare Skills:
| Klasse | Stärken | Waffen |
|---|---|---|
| Krieger | Hohe HP, hoher physischer Schaden | Schwert, Axt, Streitkolben |
| Magier | Hoher Magieschaden, niedrige HP | Stab |
| Bogenschütze | Hohe Reichweite, schnelle Angriffe | Bogen, Dolch |
### Weitere geplante Features
- [ ] Klassen-System mit unterschiedlichen Stats
- [ ] Mana-System für Magier
- [ ] Wut-Ressource für Krieger
- [ ] Ressourcen-System für Gegner (nicht alle haben Mana)
- [ ] Spell-System (Feuerbälle etc.)
- [ ] Inventar und Ausrüstungssystem
- [ ] Waffen aufheben und ausrüsten
- [ ] Erfahrungspunkte und Level-Up
- [ ] Schadenstypen (Physical, Fire, Ice, Lightning, Poison)
- [ ] Mehrere Gegnertypen
- [ ] Dungeon-Level mit Wänden und Räumen
- [ ] Multiplayer (bis zu 6 Spieler)
- [ ] Gegner-Respawn
- [ ] Boss-Gegner
- [ ] Item-Shop / Händler
---
## Projektstruktur
```
DungeonCrawler/
├── assets/ # 3D-Modelle (Kenney GLB)
├── resources/
│ ├── attack.gd # Attack-Ressourcen-Klasse
│ └── weapon.gd # Weapon-Ressourcen-Klasse
├── basic_attack.tres # Autoattack-Ressource
├── camera_pivot.gd # Kamera-Script
├── enemy.gd # Gegner-Script
├── enemy.tscn # Gegner-Scene
├── hud.gd # HUD-Script
├── hud.tscn # HUD-Scene
├── player.gd # Spieler-Script
├── player.tscn # Spieler-Scene
├── world.gd # Welt-Script
├── world.tscn # Hauptszene
└── PROJEKTDOKU.md # Diese Dokumentation
├── assets/ # 3D-Modelle und Animationen
│ ├── models/ # Mixamo Charakter-Modelle (warrior.fbx + Texturen)
│ ├── animations/ # Mixamo Animationen (Walking, Attack, etc.)
│ └── kenney_blocky-characters_20/ # Kenney Block-Chars (nicht mehr aktiv)
├── classes/ # Klassen-Definitionen (.tres)
│ ├── warrior.tres # Krieger (Ressource: NONE)
│ ├── rogue.tres # Schurke (Ressource: ENERGY, 100)
│ └── mage.tres # Magier (Ressource: MANA, 100)
├── consumables/ # Verbrauchbare Items (.tres)
│ ├── small_hp_potion.tres
│ └── small_mana_potion.tres
├── equipment/ # Equipment-Items (.tres)
│ ├── iron_sword.tres
│ ├── steel_sword.tres
│ ├── leather_chest.tres
│ ├── iron_helm.tres
│ └── wooden_shield.tres
├── loot_tables/ # Loot-Tabellen (.tres)
│ ├── goblin_loot.tres
│ └── skeleton_loot.tres
├── icons/ # Icons (SVG)
│ ├── autoattack_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
│ ├── frostbolt_icon.svg
│ ├── wand_icon.svg
│ └── wooden_staff_icon.svg
├── camera_pivot.gd # Kamera-Script
├── character_class.gd # CharacterClass Resource (inkl. ResourceType)
├── character_panel.gd # Charakter-Panel Script (Icon-Grid)
├── character_panel.tscn # Charakter-Panel Scene
├── class_selection_menu.gd # Klassenauswahl Script
├── class_selection_menu.tscn # Klassenauswahl Scene
├── consumable.gd # Consumable Resource
├── enemy.gd # Gegner-Script
├── enemy.tscn # Gegner-Scene
├── equipment.gd # Equipment Resource
├── hud.gd # HUD-Script (inkl. ResourceBar)
├── hud.tscn # HUD-Scene
├── inventory.gd # Inventar Resource
├── inventory_panel.gd # Inventar-Panel Script
├── inventory_panel.tscn # Inventar-Panel Scene
├── loot_entry.gd # LootEntry Resource
├── loot_table.gd # LootTable Resource
├── loot_window.gd # Loot-Fenster Script
├── loot_window.tscn # Loot-Fenster Scene
├── main_menu.gd # Hauptmenü Script (Einstellungen)
├── 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
```

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cfmx5li2h2gsb"
path="res://.godot/imported/Autoattack.fbx-3670ec5144a5943b8d9ee95f7186efd3.scn"
[deps]
source_file="res://assets/animations/Autoattack.fbx"
dest_files=["res://.godot/imported/Autoattack.fbx-3670ec5144a5943b8d9ee95f7186efd3.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dpm8k57akvg3k"
path="res://.godot/imported/Dying Backwards.fbx-77c7ef392c2c96235d9f4f9f7c331de8.scn"
[deps]
source_file="res://assets/animations/Dying Backwards.fbx"
dest_files=["res://.godot/imported/Dying Backwards.fbx-77c7ef392c2c96235d9f4f9f7c331de8.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cmpv0gty3rcm6"
path="res://.godot/imported/Heavy Strike.fbx-5d5103d3203a6a38edd177bdd35ccb16.scn"
[deps]
source_file="res://assets/animations/Heavy Strike.fbx"
dest_files=["res://.godot/imported/Heavy Strike.fbx-5d5103d3203a6a38edd177bdd35ccb16.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

BIN
assets/animations/Idle.fbx Normal file

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cxr7xlsq3vvhi"
path="res://.godot/imported/Idle.fbx-ecf8c7ec9efbd10accf3a620e1992827.scn"
[deps]
source_file="res://assets/animations/Idle.fbx"
dest_files=["res://.godot/imported/Idle.fbx-ecf8c7ec9efbd10accf3a620e1992827.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://kaq2rgjrxnm4"
path="res://.godot/imported/Jumping.fbx-785df5cde6fd6a2d61091c404e44b339.scn"
[deps]
source_file="res://assets/animations/Jumping.fbx"
dest_files=["res://.godot/imported/Jumping.fbx-785df5cde6fd6a2d61091c404e44b339.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://wurcf4qd6sn1"
path="res://.godot/imported/Left Strafe Walking.fbx-918d1a60e568dd43e53b31199b3f57d3.scn"
[deps]
source_file="res://assets/animations/Left Strafe Walking.fbx"
dest_files=["res://.godot/imported/Left Strafe Walking.fbx-918d1a60e568dd43e53b31199b3f57d3.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bhv5ig8xnw4v0"
path="res://.godot/imported/Right Strafe Walking.fbx-f1318b812bb12a00a11ec8112c1f6855.scn"
[deps]
source_file="res://assets/animations/Right Strafe Walking.fbx"
dest_files=["res://.godot/imported/Right Strafe Walking.fbx-f1318b812bb12a00a11ec8112c1f6855.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bjwb78njpjo2d"
path="res://.godot/imported/Start Walking.fbx-c54b281554787bf73bcae8159e84a930.scn"
[deps]
source_file="res://assets/animations/Start Walking.fbx"
dest_files=["res://.godot/imported/Start Walking.fbx-c54b281554787bf73bcae8159e84a930.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dusjhgyamhmgt"
path="res://.godot/imported/Stop Walking.fbx-3d24a0466a0c2e2a6357ddcab01c8e7a.scn"
[deps]
source_file="res://assets/animations/Stop Walking.fbx"
dest_files=["res://.godot/imported/Stop Walking.fbx-3d24a0466a0c2e2a6357ddcab01c8e7a.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bck2nreqc2gb3"
path="res://.godot/imported/Walking Backwards.fbx-485a35ec4e48207190c0e10717297ab7.scn"
[deps]
source_file="res://assets/animations/Walking Backwards.fbx"
dest_files=["res://.godot/imported/Walking Backwards.fbx-485a35ec4e48207190c0e10717297ab7.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bv1wgnjxtwty1"
path="res://.godot/imported/Walking.fbx-a17a5e9807a8458d066589326a10c768.scn"
[deps]
source_file="res://assets/animations/Walking.fbx"
dest_files=["res://.godot/imported/Walking.fbx-a17a5e9807a8458d066589326a10c768.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View file

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://clc13cbh72eov"
path.s3tc="res://.godot/imported/Walking_0.png-04bee5fb7233febb0f01a436248762b5.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
generator_parameters={
"md5": "a1dc5c32f83d0182b6af19d596014cce"
}
[deps]
source_file="res://assets/animations/Walking_0.png"
dest_files=["res://.godot/imported/Walking_0.png-04bee5fb7233febb0f01a436248762b5.s3tc.ctex"]
[params]
compress/mode=2
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=true
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=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dphpf7qa3hkvh"
path="res://.godot/imported/Walking_1.png-ba1e8e03f6c24586962fc1b62dccdfe9.ctex"
metadata={
"vram_texture": false
}
generator_parameters={
"md5": "4201468097ebb1a15961ee25c2d64310"
}
[deps]
source_file="res://assets/animations/Walking_1.png"
dest_files=["res://.godot/imported/Walking_1.png-ba1e8e03f6c24586962fc1b62dccdfe9.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=true
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View file

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dm2h4hkvqyoph"
path.s3tc="res://.godot/imported/Walking_2.png-ce8a6c3320aac98a8634c6e5cf2b9141.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
generator_parameters={
"md5": "16d2ed20714fd916290f48d68a233060"
}
[deps]
source_file="res://assets/animations/Walking_2.png"
dest_files=["res://.godot/imported/Walking_2.png-ce8a6c3320aac98a8634c6e5cf2b9141.s3tc.ctex"]
[params]
compress/mode=2
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=1
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=1
roughness/src_normal="res://assets/animations/Walking_2.png"
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=0

BIN
assets/models/warrior.fbx Normal file

Binary file not shown.

View file

@ -0,0 +1,44 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://da1w523lg7i2b"
path="res://.godot/imported/warrior.fbx-b4acdad06881ff9e3d6bb32ec03761c9.scn"
[deps]
source_file="res://assets/models/warrior.fbx"
dest_files=["res://.godot/imported/warrior.fbx-b4acdad06881ff9e3d6bb32ec03761c9.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2

BIN
assets/models/warrior_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View file

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://kb2tcclrpsgp"
path.s3tc="res://.godot/imported/warrior_0.png-922555626e5523f977897d33a0262592.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
generator_parameters={
"md5": "a1dc5c32f83d0182b6af19d596014cce"
}
[deps]
source_file="res://assets/models/warrior_0.png"
dest_files=["res://.godot/imported/warrior_0.png-922555626e5523f977897d33a0262592.s3tc.ctex"]
[params]
compress/mode=2
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=true
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=0

BIN
assets/models/warrior_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dnlkuf4pjgbht"
path="res://.godot/imported/warrior_1.png-aafdc73fc8d6c19365ff4d442a16edcf.ctex"
metadata={
"vram_texture": false
}
generator_parameters={
"md5": "4201468097ebb1a15961ee25c2d64310"
}
[deps]
source_file="res://assets/models/warrior_1.png"
dest_files=["res://.godot/imported/warrior_1.png-aafdc73fc8d6c19365ff4d442a16edcf.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=true
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

BIN
assets/models/warrior_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View file

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://coqdhbh5uwkjf"
path.s3tc="res://.godot/imported/warrior_2.png-0164e54eb7a56547f2b489b55a55ca25.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
generator_parameters={
"md5": "16d2ed20714fd916290f48d68a233060"
}
[deps]
source_file="res://assets/models/warrior_2.png"
dest_files=["res://.godot/imported/warrior_2.png-0164e54eb7a56547f2b489b55a55ca25.s3tc.ctex"]
[params]
compress/mode=2
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=1
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=1
roughness/src_normal="res://assets/models/warrior_2.png"
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=0

37
character_class.gd Normal file
View file

@ -0,0 +1,37 @@
# CharacterClass.gd
# Definiert Charakterklassen mit Grundstats und Main-Stat
extends Resource
class_name CharacterClass
enum MainStat { STRENGTH, AGILITY, INTELLIGENCE }
enum ResourceType { NONE, MANA, RAGE, ENERGY }
@export var class_name_de: String = "Krieger"
@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
@export var base_strength: int = 10
@export var base_agility: int = 10
@export var base_intelligence: int = 10
@export var base_stamina: int = 10 # Beeinflusst HP
# Stat-Zuwachs pro Level
@export var strength_per_level: float = 2.0
@export var agility_per_level: float = 2.0
@export var intelligence_per_level: float = 2.0
@export var stamina_per_level: float = 2.0
# Unbewaffneter Schaden (klassenabhängig)
@export var unarmed_min_damage: int = 1
@export var unarmed_max_damage: int = 2
@export var unarmed_attack_speed: float = 2.0 # Langsamer als mit Waffe
# HP pro Stamina-Punkt
const HP_PER_STAMINA = 10
# Mana pro Intelligenz-Punkt
const MANA_PER_INT = 5
# Schaden-Skalierung mit Main-Stat
const DAMAGE_PER_MAIN_STAT = 0.5

1
character_class.gd.uid Normal file
View file

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

203
character_panel.gd Normal file
View file

@ -0,0 +1,203 @@
# CharacterPanel.gd
# Zeigt Charakterinfos: Klasse, Level, Stats und Equipment-Icons mit Tooltips
extends CanvasLayer
var panel_visible = false
const EQUIP_SLOT_SIZE = 48
@onready var panel = $Panel
@onready var class_label = $Panel/HBoxContainer/StatsColumn/ClassLabel
@onready var level_label = $Panel/HBoxContainer/StatsColumn/LevelLabel
@onready var str_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/StrLabel
@onready var agi_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/AgiLabel
@onready var int_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/IntLabel
@onready var sta_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/StaLabel
@onready var armor_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/ArmorLabel
@onready var hp_label = $Panel/HBoxContainer/StatsColumn/HPLabel
@onready var damage_label = $Panel/HBoxContainer/EquipmentColumn/DamageLabel
@onready var dps_label = $Panel/HBoxContainer/EquipmentColumn/DPSLabel
@onready var equipment_grid = $Panel/HBoxContainer/EquipmentColumn/EquipmentGrid
# Slot-Reihenfolge und deutsche Namen
const SLOT_ORDER = [
Equipment.Slot.HEAD,
Equipment.Slot.CHEST,
Equipment.Slot.HANDS,
Equipment.Slot.LEGS,
Equipment.Slot.FEET,
Equipment.Slot.WEAPON,
Equipment.Slot.OFFHAND,
]
func _ready():
panel.visible = false
func toggle():
panel_visible = !panel_visible
panel.visible = panel_visible
func update_stats(player):
if player.character_class:
var main_stat_name = ""
match player.character_class.main_stat:
CharacterClass.MainStat.STRENGTH:
main_stat_name = "STR"
CharacterClass.MainStat.AGILITY:
main_stat_name = "AGI"
CharacterClass.MainStat.INTELLIGENCE:
main_stat_name = "INT"
class_label.text = player.character_class.class_name_de + " (Haupt: " + main_stat_name + ")"
else:
class_label.text = "Keine Klasse"
level_label.text = "Level " + str(player.level) + " (" + str(player.current_xp) + "/" + str(player.xp_to_next_level) + " XP)"
str_label.text = "Stärke: " + str(player.strength)
agi_label.text = "Beweglichkeit: " + str(player.agility)
int_label.text = "Intelligenz: " + str(player.intelligence)
sta_label.text = "Ausdauer: " + str(player.stamina)
armor_label.text = "Rüstung: " + str(player.armor)
hp_label.text = "HP: " + str(player.current_hp) + " / " + str(player.max_hp)
# Waffen-Stats
var weapon = player.get_equipped_weapon()
if weapon:
damage_label.text = "Schaden: " + str(weapon.min_damage) + "-" + str(weapon.max_damage) + " (%.1fs)" % weapon.attack_speed
else:
if player.character_class:
var min_dmg = player.character_class.unarmed_min_damage
var max_dmg = player.character_class.unarmed_max_damage
var atk_spd = player.character_class.unarmed_attack_speed
damage_label.text = "Unbewaffnet: " + str(min_dmg) + "-" + str(max_dmg) + " (%.1fs)" % atk_spd
else:
damage_label.text = "Unbewaffnet: 1-2 (2.0s)"
dps_label.text = "DPS: %.1f" % player.get_dps()
# Main-Stat hervorheben
str_label.modulate = Color(1, 1, 1)
agi_label.modulate = Color(1, 1, 1)
int_label.modulate = Color(1, 1, 1)
if player.character_class:
match player.character_class.main_stat:
CharacterClass.MainStat.STRENGTH:
str_label.modulate = Color(1, 0.8, 0.2)
CharacterClass.MainStat.AGILITY:
agi_label.modulate = Color(1, 0.8, 0.2)
CharacterClass.MainStat.INTELLIGENCE:
int_label.modulate = Color(1, 0.8, 0.2)
# Equipment-Grid mit Icons aktualisieren
_rebuild_equipment_grid(player)
func _rebuild_equipment_grid(player):
# Alte Slots entfernen
for child in equipment_grid.get_children():
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:
# Leerer Slot: Slot-Name als Tooltip
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

1
character_panel.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://7pdlor66gi51

124
character_panel.tscn Normal file
View file

@ -0,0 +1,124 @@
[gd_scene format=3 uid="uid://character_panel"]
[ext_resource type="Script" path="res://character_panel.gd" id="1_panel"]
[node name="CharacterPanel" type="CanvasLayer"]
script = ExtResource("1_panel")
[node name="Panel" type="Panel" parent="."]
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_left = 20.0
offset_top = -200.0
offset_right = 520.0
offset_bottom = 200.0
grow_vertical = 2
[node name="HBoxContainer" type="HBoxContainer" parent="Panel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 15.0
offset_top = 15.0
offset_right = -15.0
offset_bottom = -15.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 20
[node name="StatsColumn" type="VBoxContainer" parent="Panel/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="Title" type="Label" parent="Panel/HBoxContainer/StatsColumn"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Charakter"
horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="Panel/HBoxContainer/StatsColumn"]
layout_mode = 2
[node name="ClassLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn"]
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Krieger (Haupt: STR)"
[node name="LevelLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Level 1 (0/100 XP)"
[node name="HSeparator2" type="HSeparator" parent="Panel/HBoxContainer/StatsColumn"]
layout_mode = 2
[node name="StatsContainer" type="VBoxContainer" parent="Panel/HBoxContainer/StatsColumn"]
layout_mode = 2
theme_override_constants/separation = 4
[node name="StrLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"]
layout_mode = 2
text = "Stärke: 15"
[node name="AgiLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"]
layout_mode = 2
text = "Beweglichkeit: 8"
[node name="IntLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"]
layout_mode = 2
text = "Intelligenz: 5"
[node name="StaLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"]
layout_mode = 2
text = "Ausdauer: 12"
[node name="ArmorLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"]
layout_mode = 2
text = "Rüstung: 0"
[node name="HSeparator3" type="HSeparator" parent="Panel/HBoxContainer/StatsColumn"]
layout_mode = 2
[node name="HPLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "HP: 120 / 120"
[node name="VSeparator" type="VSeparator" parent="Panel/HBoxContainer"]
layout_mode = 2
[node name="EquipmentColumn" type="VBoxContainer" parent="Panel/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="EquipTitle" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "Ausrüstung"
horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2
[node name="EquipmentGrid" type="GridContainer" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2
theme_override_constants/h_separation = 6
theme_override_constants/v_separation = 6
columns = 4
[node name="HSeparator2" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2
[node name="DamageLabel" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Schaden: 3-6 (1.5s)"
[node name="DPSLabel" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "DPS: 5.0"

81
class_selection_menu.gd Normal file
View file

@ -0,0 +1,81 @@
# ClassSelectionMenu.gd
# Menü zur Auswahl der Charakterklasse beim Spielstart
extends CanvasLayer
signal class_selected(character_class: CharacterClass)
const WARRIOR_CLASS = preload("res://classes/warrior.tres")
const ROGUE_CLASS = preload("res://classes/rogue.tres")
const MAGE_CLASS = preload("res://classes/mage.tres")
var selected_class: CharacterClass = null
@onready var panel = $Panel
@onready var warrior_btn = $Panel/VBoxContainer/ClassButtons/WarriorBtn
@onready var rogue_btn = $Panel/VBoxContainer/ClassButtons/RogueBtn
@onready var mage_btn = $Panel/VBoxContainer/ClassButtons/MageBtn
@onready var start_btn = $Panel/VBoxContainer/StartBtn
@onready var class_info = $Panel/VBoxContainer/ClassInfo
@onready var stats_label = $Panel/VBoxContainer/StatsLabel
func _ready():
# Spiel pausieren während Menü offen
get_tree().paused = true
process_mode = Node.PROCESS_MODE_ALWAYS
# Buttons verbinden
warrior_btn.pressed.connect(_on_warrior_selected)
rogue_btn.pressed.connect(_on_rogue_selected)
mage_btn.pressed.connect(_on_mage_selected)
start_btn.pressed.connect(_on_start_pressed)
# Start-Button deaktiviert bis Klasse gewählt
start_btn.disabled = true
class_info.text = "Wähle eine Klasse!"
stats_label.text = ""
func _on_warrior_selected():
selected_class = WARRIOR_CLASS
_update_selection("Krieger", WARRIOR_CLASS)
_highlight_button(warrior_btn)
func _on_rogue_selected():
selected_class = ROGUE_CLASS
_update_selection("Schurke", ROGUE_CLASS)
_highlight_button(rogue_btn)
func _on_mage_selected():
selected_class = MAGE_CLASS
_update_selection("Magier", MAGE_CLASS)
_highlight_button(mage_btn)
func _update_selection(cls_name: String, char_class: CharacterClass):
var main_stat_name = ""
match char_class.main_stat:
CharacterClass.MainStat.STRENGTH:
main_stat_name = "Stärke"
CharacterClass.MainStat.AGILITY:
main_stat_name = "Beweglichkeit"
CharacterClass.MainStat.INTELLIGENCE:
main_stat_name = "Intelligenz"
class_info.text = cls_name + " - Haupt-Stat: " + main_stat_name
stats_label.text = "STR: " + str(char_class.base_strength) + " AGI: " + str(char_class.base_agility) + " INT: " + str(char_class.base_intelligence) + " STA: " + str(char_class.base_stamina)
start_btn.disabled = false
func _highlight_button(active_btn: Button):
# Alle Buttons zurücksetzen
warrior_btn.modulate = Color(1, 1, 1)
rogue_btn.modulate = Color(1, 1, 1)
mage_btn.modulate = Color(1, 1, 1)
# Aktiven Button hervorheben
active_btn.modulate = Color(1, 0.8, 0.2)
func _on_start_pressed():
if selected_class == null:
return
# Signal senden und Menü schließen
class_selected.emit(selected_class)
get_tree().paused = false
queue_free()

View file

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

80
class_selection_menu.tscn Normal file
View file

@ -0,0 +1,80 @@
[gd_scene format=3 uid="uid://class_selection_menu"]
[ext_resource type="Script" path="res://class_selection_menu.gd" id="1_menu"]
[node name="ClassSelectionMenu" type="CanvasLayer"]
script = ExtResource("1_menu")
[node name="Panel" 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="Panel"]
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="Panel/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 28
text = "Wähle deine Klasse"
horizontal_alignment = 1
[node name="ClassButtons" type="HBoxContainer" parent="Panel/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 20
alignment = 1
[node name="WarriorBtn" type="Button" parent="Panel/VBoxContainer/ClassButtons"]
custom_minimum_size = Vector2(100, 80)
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Krieger"
[node name="RogueBtn" type="Button" parent="Panel/VBoxContainer/ClassButtons"]
custom_minimum_size = Vector2(100, 80)
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Schurke"
[node name="MageBtn" type="Button" parent="Panel/VBoxContainer/ClassButtons"]
custom_minimum_size = Vector2(100, 80)
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Magier"
[node name="ClassInfo" type="Label" parent="Panel/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "Wähle eine Klasse!"
horizontal_alignment = 1
[node name="StatsLabel" type="Label" parent="Panel/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = ""
horizontal_alignment = 1
[node name="StartBtn" type="Button" parent="Panel/VBoxContainer"]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
theme_override_font_sizes/font_size = 20
disabled = true
text = "Spiel starten"

21
classes/mage.tres Normal file
View file

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

18
classes/rogue.tres Normal file
View file

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

17
classes/warrior.tres Normal file
View file

@ -0,0 +1,17 @@
[gd_resource type="Resource" script_class="CharacterClass" format=3 uid="uid://bebfnwygqy1gu"]
[ext_resource type="Script" uid="uid://ci45xxb5vn857" path="res://character_class.gd" id="1"]
[resource]
script = ExtResource("1")
base_strength = 15
base_agility = 8
base_intelligence = 5
base_stamina = 12
strength_per_level = 3.0
agility_per_level = 1.5
intelligence_per_level = 1.0
stamina_per_level = 2.5
unarmed_min_damage = 2
unarmed_max_damage = 4
unarmed_attack_speed = 1.8

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

286
enemy.gd
View file

@ -2,26 +2,166 @@
# Steuert den Gegner: KI-Bewegung zum Spieler, Angriff, HP, Zielanzeige
extends CharacterBody3D
signal enemy_died(spawn_position: Vector3, xp_reward: int)
signal enemy_dropped_loot(loot: Dictionary, world_position: Vector3)
const SPEED = 3.0
const PATROL_SPEED = 1.5
const GRAVITY = 9.8
const ATTACK_DAMAGE = 5
const ATTACK_RANGE = 1.5
const ATTACK_COOLDOWN = 2.0
const AGGRO_RANGE = 8.0 # Entfernung ab der der Gegner angreift
const PATROL_RADIUS = 5.0 # Radius um Spawn-Position für Patrol
const PATROL_WAIT_TIME = 2.0 # Wartezeit am Patrol-Punkt
var max_hp = 50
var current_hp = 50
var target = null # Ziel des Gegners (normalerweise der Spieler)
# Level-Differenz Konstanten
const LEVEL_DIFF_DAMAGE_MOD = 0.1 # 10% mehr/weniger Schaden pro Level-Differenz
const MAX_LEVEL_DIFF_MOD = 0.5 # Maximal 50% Modifikation
enum State { PATROL, CHASE, ATTACK }
# Stats-System
@export var level: int = 1
@export var base_strength: int = 8
@export var base_stamina: int = 10
@export var base_armor: int = 5 # Rüstung reduziert Nahkampfschaden
# Berechnete Stats
var strength: int = 8
var stamina: int = 10
var armor: int = 5
var max_hp: int = 100
var current_hp: int = 100
var attack_damage: int = 5
# XP-Belohnung (skaliert mit Level)
var xp_reward: int = 25
# Loot-System
@export var loot_table: LootTable
var target = null # Spieler-Referenz (wird von World gesetzt)
var can_attack = true
var spawn_position: Vector3 # Ursprüngliche Spawn-Position
var current_state = State.PATROL
var patrol_target: Vector3 # Aktuelles Patrol-Ziel
var is_waiting = false # Ob Gegner am Patrol-Punkt wartet
# Animation System
const ANIMATION_FILES = {
"walk": "res://assets/animations/Walking.fbx",
"autoattack": "res://assets/animations/Autoattack.fbx",
"die": "res://assets/animations/Dying Backwards.fbx",
"idle": "res://assets/animations/Idle.fbx",
}
const LOOP_ANIMATIONS = ["walk", "idle"]
var anim_player: AnimationPlayer = null
var current_anim: String = ""
@onready var health_label = $HealthLabel
func _ready():
_calculate_stats()
current_hp = max_hp
health_label.visible = false
_update_label()
spawn_position = global_position
_pick_new_patrol_target()
_setup_animations()
# Animationen laden
func _setup_animations():
var model = get_node_or_null("EnemyModel")
if model == null:
return
anim_player = _find_node_by_class(model, "AnimationPlayer")
if anim_player == null:
anim_player = AnimationPlayer.new()
anim_player.name = "AnimationPlayer"
model.add_child(anim_player)
var lib: AnimationLibrary
if anim_player.has_animation_library(""):
lib = anim_player.get_animation_library("")
else:
lib = AnimationLibrary.new()
anim_player.add_animation_library("", lib)
for anim_id in ANIMATION_FILES:
var scene = load(ANIMATION_FILES[anim_id]) as PackedScene
if scene == null:
continue
var instance = scene.instantiate()
var source_ap = _find_node_by_class(instance, "AnimationPlayer")
if source_ap:
var names = source_ap.get_animation_list()
if names.size() > 0:
var anim = source_ap.get_animation(names[0])
if anim_id in ["walk", "idle"]:
anim.loop_mode = Animation.LOOP_LINEAR
if lib.has_animation(anim_id):
lib.remove_animation(anim_id)
lib.add_animation(anim_id, anim)
instance.queue_free()
func _find_node_by_class(node: Node, class_name_str: String) -> Node:
for child in node.get_children():
if child.get_class() == class_name_str:
return child
var result = _find_node_by_class(child, class_name_str)
if result:
return result
return null
func _play_anim(anim_name: String):
if anim_player == null:
return
# Wenn Animation ausgelaufen ist, zurücksetzen
if not anim_player.is_playing():
current_anim = ""
if anim_name != current_anim:
current_anim = anim_name
if anim_player.has_animation(anim_name):
anim_player.play(anim_name)
else:
anim_player.stop()
# Stats basierend auf Level berechnen
func _calculate_stats():
var levels_gained = level - 1
strength = base_strength + levels_gained * 2
stamina = base_stamina + levels_gained * 3
armor = base_armor + levels_gained * 2
# HP = Stamina * 10
max_hp = stamina * 10
# Schaden = Stärke / 2
attack_damage = int(strength * 0.5) + 2
# XP = 25 * Level
xp_reward = 25 * level
print("Enemy Stats (Lv", level, ") - STR:", strength, " STA:", stamina, " ARM:", armor, " HP:", max_hp, " DMG:", attack_damage)
# Schaden mit Rüstung und Level-Differenz berechnen
func calculate_incoming_damage(raw_damage: int, attacker_level: int, is_melee: bool) -> int:
var damage = float(raw_damage)
# Rüstung reduziert nur Nahkampfschaden
if is_melee:
# Rüstungsreduktion: armor / (armor + 50) = Prozent Reduktion
# Bei 5 Rüstung: 5/55 = ~9% Reduktion
# Bei 20 Rüstung: 20/70 = ~29% Reduktion
var armor_reduction = float(armor) / (float(armor) + 50.0)
damage = damage * (1.0 - armor_reduction)
# Level-Differenz Modifikator
var level_diff = attacker_level - level
var level_mod = clamp(level_diff * LEVEL_DIFF_DAMAGE_MOD, -MAX_LEVEL_DIFF_MOD, MAX_LEVEL_DIFF_MOD)
damage = damage * (1.0 + level_mod)
return maxi(1, int(damage)) # Mindestens 1 Schaden
# HP-Label Text aktualisieren
func _update_label():
health_label.text = str(current_hp) + " / " + str(max_hp)
health_label.text = "Lv" + str(level) + " " + str(current_hp) + "/" + str(max_hp)
# HP-Label anzeigen (wenn Gegner markiert wird)
func show_health():
@ -35,45 +175,145 @@ func hide_health():
func take_damage(amount):
current_hp -= amount
_update_label()
# Aggro bei Schaden — sofort angreifen
if current_state == State.PATROL:
current_state = State.CHASE
is_waiting = false
print("Gegner wurde angegriffen und verfolgt den Spieler!")
if current_hp <= 0:
die()
# Schaden mit vollem Schadenssystem (Rüstung, Level-Differenz)
func take_damage_from(raw_damage: int, attacker_level: int, is_melee: bool = true):
var final_damage = calculate_incoming_damage(raw_damage, attacker_level, is_melee)
print("Eingehender Schaden: ", raw_damage, " -> ", final_damage, " (nach Rüstung/Level)")
take_damage(final_damage)
# Gegner aus der Szene entfernen
func die():
print("Gegner besiegt!")
print("Gegner besiegt! +", xp_reward, " XP")
# XP an Spieler geben
if target and target.has_method("gain_xp"):
target.gain_xp(xp_reward)
# Loot generieren und droppen
_drop_loot()
enemy_died.emit(spawn_position, xp_reward)
# Death-Animation abspielen, dann entfernen
if anim_player and anim_player.has_animation("die"):
_play_anim("die")
# Kollision deaktivieren damit der Gegner nicht mehr im Weg ist
set_physics_process(false)
$CollisionShape3D.set_deferred("disabled", true)
await anim_player.animation_finished
queue_free()
# Loot generieren basierend auf LootTable
func _drop_loot():
if loot_table == null:
# Standard-Gold-Drop wenn keine LootTable zugewiesen
var gold = randi_range(1, 3) * level
var loot = {"gold": gold, "items": []}
enemy_dropped_loot.emit(loot, global_position)
return
var loot = loot_table.generate_loot()
# Gold mit Level skalieren
loot["gold"] = loot["gold"] * level
enemy_dropped_loot.emit(loot, global_position)
func _physics_process(delta):
if not is_on_floor():
velocity.y -= GRAVITY * delta
if target == null:
# Ohne Spieler-Referenz nur patrouillieren
_do_patrol()
move_and_slide()
return
var distance = global_position.distance_to(target.global_position)
# Prüfe Distanz zum Spieler für Aggro
var distance_to_player = global_position.distance_to(target.global_position)
if distance <= ATTACK_RANGE:
# In Reichweite: angreifen
velocity.x = 0
velocity.z = 0
if can_attack:
_attack()
else:
# Direkt auf Ziel zubewegen
var direction = (target.global_position - global_position)
direction.y = 0
direction = direction.normalized()
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
look_at(Vector3(target.global_position.x, global_position.y, target.global_position.z))
# State-Wechsel basierend auf Distanz
match current_state:
State.PATROL:
if distance_to_player <= AGGRO_RANGE:
current_state = State.CHASE
print("Gegner hat Spieler entdeckt!")
else:
_do_patrol()
State.CHASE:
if distance_to_player <= ATTACK_RANGE:
current_state = State.ATTACK
else:
_chase_player()
State.ATTACK:
if distance_to_player > ATTACK_RANGE:
current_state = State.CHASE
else:
velocity.x = 0
velocity.z = 0
_play_anim("idle") # Idle beim Angriff
if can_attack:
_attack()
move_and_slide()
# Neues Patrol-Ziel in der Nähe der Spawn-Position wählen
func _pick_new_patrol_target():
var angle = randf() * TAU # Zufälliger Winkel
var distance = randf_range(2.0, PATROL_RADIUS)
patrol_target = spawn_position + Vector3(cos(angle) * distance, 0, sin(angle) * distance)
# Patrol-Verhalten: Zufällig herumlaufen
func _do_patrol():
if is_waiting:
return
var distance_to_patrol = global_position.distance_to(patrol_target)
if distance_to_patrol <= 0.5:
# Am Ziel angekommen, warten und neues Ziel wählen
velocity.x = 0
velocity.z = 0
_play_anim("idle")
_wait_at_patrol_point()
else:
# Zum Patrol-Ziel laufen
var direction = (patrol_target - global_position)
direction.y = 0
direction = direction.normalized()
velocity.x = direction.x * PATROL_SPEED
velocity.z = direction.z * PATROL_SPEED
_play_anim("walk")
look_at(Vector3(patrol_target.x, global_position.y, patrol_target.z))
# Am Patrol-Punkt warten
func _wait_at_patrol_point():
is_waiting = true
await get_tree().create_timer(PATROL_WAIT_TIME).timeout
is_waiting = false
_pick_new_patrol_target()
# Spieler verfolgen
func _chase_player():
_play_anim("walk")
var direction = (target.global_position - global_position)
direction.y = 0
direction = direction.normalized()
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
look_at(Vector3(target.global_position.x, global_position.y, target.global_position.z))
# Angriff mit Cooldown
func _attack():
can_attack = false
target.take_damage(ATTACK_DAMAGE)
print("Gegner greift an!")
_play_anim("autoattack")
# Gegner verwendet auch das Schadenssystem mit Level-Differenz
if target.has_method("take_damage_from"):
target.take_damage_from(attack_damage, level, true)
else:
target.take_damage(attack_damage)
print("Gegner (Lv", level, ") greift an: ", attack_damage, " Schaden")
await get_tree().create_timer(ATTACK_COOLDOWN).timeout
can_attack = true

View file

@ -1,6 +1,7 @@
[gd_scene format=3 uid="uid://cvojaeanxugfj"]
[ext_resource type="PackedScene" uid="uid://tosl2au4emxt" path="res://assets/kenney_blocky-characters_20/Models/GLB format/character-d.glb" id="1_7k104"]
[ext_resource type="PackedScene" uid="uid://da1w523lg7i2b" path="res://assets/models/warrior.fbx" id="1_7k104"]
[ext_resource type="Script" uid="uid://bg5qs3pcfp7p7" path="res://enemy.gd" id="2_enemy"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4gyqm"]
radius = 0.6
@ -11,8 +12,9 @@ radius = 0.6
height = 3.0
[node name="Enemy" type="CharacterBody3D" unique_id=332011146]
script = ExtResource("2_enemy")
[node name="character-d2" parent="." unique_id=846574684 instance=ExtResource("1_7k104")]
[node name="EnemyModel" parent="." unique_id=846574684 instance=ExtResource("1_7k104")]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0, 0)
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1323028920]

63
equipment.gd Normal file
View file

@ -0,0 +1,63 @@
# Equipment.gd
# Resource für Ausrüstungsgegenstände
extends Resource
class_name Equipment
enum Slot {
HEAD, # Helm
CHEST, # Brustpanzer
HANDS, # Handschuhe
LEGS, # Beinschienen
FEET, # Stiefel
WEAPON, # Waffe
OFFHAND # Nebenhand (Schild, etc.)
}
enum Rarity {
COMMON, # Weiß
UNCOMMON, # Grün
RARE, # Blau
EPIC # Lila
}
@export var item_name: String = "Unbekannt"
@export var slot: Slot = Slot.WEAPON
@export var rarity: Rarity = Rarity.COMMON
# Stats die das Item gibt
@export var armor: int = 0
@export var strength: int = 0
@export var agility: int = 0
@export var intelligence: int = 0
@export var stamina: int = 0
@export var haste: float = 0.0 # Angriffsgeschwindigkeit (0.1 = 10% schneller)
# Nur für Waffen
@export var min_damage: int = 0
@export var max_damage: int = 0
@export var attack_speed: float = 1.5 # Sekunden zwischen Angriffen
@export var weapon_range: float = 3.0
# Icon für UI
@export var icon: Texture2D
# Slot-Namen für Anzeige
static func get_slot_name(s: Slot) -> String:
match s:
Slot.HEAD: return "Kopf"
Slot.CHEST: return "Brust"
Slot.HANDS: return "Hände"
Slot.LEGS: return "Beine"
Slot.FEET: return "Füße"
Slot.WEAPON: return "Waffe"
Slot.OFFHAND: return "Nebenhand"
return "Unbekannt"
# Seltenheitsfarbe
static func get_rarity_color(r: Rarity) -> Color:
match r:
Rarity.COMMON: return Color(1, 1, 1) # Weiß
Rarity.UNCOMMON: return Color(0.2, 0.8, 0.2) # Grün
Rarity.RARE: return Color(0.3, 0.5, 1.0) # Blau
Rarity.EPIC: return Color(0.7, 0.3, 0.9) # Lila
return Color(1, 1, 1)

1
equipment.gd.uid Normal file
View file

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

13
equipment/iron_helm.tres Normal file
View file

@ -0,0 +1,13 @@
[gd_resource type="Resource" script_class="Equipment" format=3 uid="uid://d54jjc24pj3t"]
[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]
script = ExtResource("1_equipment")
item_name = "Eisenhelm"
slot = 0
armor = 5
strength = 1
stamina = 1
icon = ExtResource("2_icon")

12
equipment/iron_sword.tres Normal file
View file

@ -0,0 +1,12 @@
[gd_resource type="Resource" script_class="Equipment" format=3 uid="uid://vprfv2phlcbc"]
[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]
script = ExtResource("1_equipment")
item_name = "Eisenschwert"
strength = 2
min_damage = 3
max_damage = 6
icon = ExtResource("2_icon")

View file

@ -0,0 +1,13 @@
[gd_resource type="Resource" script_class="Equipment" format=3 uid="uid://cnijhxsjmepss"]
[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]
script = ExtResource("1_equipment")
item_name = "Lederrüstung"
slot = 1
armor = 8
agility = 1
stamina = 2
icon = ExtResource("2_icon")

View file

@ -0,0 +1,20 @@
[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="Texture2D" path="res://icons/steel_sword_icon.svg" id="2_icon"]
[resource]
script = ExtResource("1_equipment")
item_name = "Stahlschwert"
slot = 5
rarity = 1
armor = 0
strength = 4
agility = 0
intelligence = 0
stamina = 0
min_damage = 5
max_damage = 9
attack_speed = 1.4
weapon_range = 3.0
icon = ExtResource("2_icon")

View file

@ -0,0 +1,20 @@
[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="Texture2D" path="res://icons/wooden_shield_icon.svg" id="2_icon"]
[resource]
script = ExtResource("1_equipment")
item_name = "Holzschild"
slot = 6
rarity = 0
armor = 6
strength = 0
agility = 0
intelligence = 0
stamina = 2
min_damage = 0
max_damage = 0
attack_speed = 1.5
weapon_range = 3.0
icon = ExtResource("2_icon")

View file

@ -0,0 +1,14 @@
[gd_resource type="Resource" script_class="Equipment" format=3 uid="uid://wooden_staff_01"]
[ext_resource type="Script" uid="uid://re0xiie1udfq" path="res://equipment.gd" id="1_equipment"]
[ext_resource type="Texture2D" path="res://icons/wooden_staff_icon.svg" id="2_icon"]
[resource]
script = ExtResource("1_equipment")
item_name = "Holzstab"
intelligence = 3
min_damage = 2
max_damage = 5
attack_speed = 2.0
weapon_range = 3.0
icon = ExtResource("2_icon")

9
heavy_strike.tres Normal file
View file

@ -0,0 +1,9 @@
[gd_resource type="Resource" script_class="Attack" load_steps=2 format=3]
[ext_resource type="Script" path="res://resources/attack.gd" id="1"]
[resource]
script = ExtResource("1")
name = "Heavy Strike"
damage_type = 0
icon = null

418
hud.gd
View file

@ -1,9 +1,25 @@
# HUD.gd
# Verwaltet die Spieler-UI: HP-Leiste, Aktionsleiste (Slots 1-9)
# Verwaltet die Spieler-UI: HP-Leiste, XP-Leiste, Aktionsleiste (Slots 1-9)
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
# Level/XP UI (wird dynamisch erstellt)
var level_label: Label
var xp_bar: ProgressBar
var gold_label: Label
@onready var action_slots = [
$Control/ActionBar/A1,
$Control/ActionBar/A2,
@ -17,6 +33,281 @@ extends CanvasLayer
]
var active_slot = 0
var slot_icons = [] # TextureRect nodes für Icons
var slot_cooldown_overlays = [] # ColorRect für Cooldown-Anzeige
var slot_cooldown_labels = [] # Label für Cooldown-Text
var slot_stack_labels = [] # Label für Stack-Anzahl
# Ressourcen-Bar (Mana/Energie/Wut)
var resource_bar: ProgressBar
var resource_label: Label
# Castbar
var castbar: ProgressBar
var castbar_label: Label
var castbar_container: PanelContainer
func _ready():
_create_level_ui()
for i in range(9):
# Icon erstellen
var icon = TextureRect.new()
icon.name = "Icon"
icon.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.custom_minimum_size = Vector2(40, 40)
icon.position = Vector2(5, 5)
action_slots[i].add_child(icon)
slot_icons.append(icon)
# Cooldown-Overlay erstellen (dunkle Überlagerung)
var cooldown_overlay = ColorRect.new()
cooldown_overlay.name = "CooldownOverlay"
cooldown_overlay.color = Color(0, 0, 0, 0.7)
cooldown_overlay.size = Vector2(50, 50)
cooldown_overlay.position = Vector2(0, 0)
cooldown_overlay.visible = false
cooldown_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
action_slots[i].add_child(cooldown_overlay)
slot_cooldown_overlays.append(cooldown_overlay)
# Cooldown-Text erstellen
var cooldown_label = Label.new()
cooldown_label.name = "CooldownLabel"
cooldown_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
cooldown_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
cooldown_label.size = Vector2(50, 50)
cooldown_label.position = Vector2(0, 0)
cooldown_label.add_theme_font_size_override("font_size", 16)
cooldown_label.visible = false
cooldown_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
action_slots[i].add_child(cooldown_label)
slot_cooldown_labels.append(cooldown_label)
# Stack-Count Label (unten rechts)
var stack_label = Label.new()
stack_label.name = "StackLabel"
stack_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
stack_label.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
stack_label.size = Vector2(50, 50)
stack_label.position = Vector2(-4, -2)
stack_label.add_theme_font_size_override("font_size", 11)
stack_label.visible = false
stack_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
action_slots[i].add_child(stack_label)
slot_stack_labels.append(stack_label)
# Button für Klicks und Drag erstellen
var button = Button.new()
button.name = "SlotButton"
button.flat = true
button.size = Vector2(50, 50)
button.position = Vector2(0, 0)
button.modulate = Color(1, 1, 1, 0) # Unsichtbar
var slot_index = i
button.pressed.connect(func(): _on_slot_clicked(slot_index))
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 >= 0 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 > 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)
# Icon für einen Slot setzen
func set_slot_icon(slot_index: int, icon_path: String):
if slot_index >= 0 and slot_index < 9:
var texture = load(icon_path)
if texture:
slot_icons[slot_index].texture = texture
else:
print("Icon nicht gefunden: ", icon_path)
# Cooldown für einen Slot anzeigen (remaining_time in Sekunden)
func set_slot_cooldown(slot_index: int, remaining_time: float):
if slot_index < 0 or slot_index >= 9:
return
if remaining_time > 0:
slot_cooldown_overlays[slot_index].visible = true
slot_cooldown_labels[slot_index].visible = true
slot_cooldown_labels[slot_index].text = "%.1f" % remaining_time
else:
slot_cooldown_overlays[slot_index].visible = false
slot_cooldown_labels[slot_index].visible = false
# Level/XP UI erstellen
func _create_level_ui():
var control = $Control
# Level Label
level_label = Label.new()
level_label.name = "LevelLabel"
level_label.position = Vector2(20, 55)
level_label.add_theme_font_size_override("font_size", 14)
level_label.text = "Level 1"
control.add_child(level_label)
# XP Bar
xp_bar = ProgressBar.new()
xp_bar.name = "XPBar"
xp_bar.position = Vector2(80, 55)
xp_bar.size = Vector2(140, 18)
xp_bar.show_percentage = false
xp_bar.value = 0
# XP Bar Farbe (blau)
var xp_style = StyleBoxFlat.new()
xp_style.bg_color = Color(0.2, 0.4, 0.9, 1.0)
xp_bar.add_theme_stylebox_override("fill", xp_style)
control.add_child(xp_bar)
# Ressourcen-Bar (Mana/Energie/Wut) - unter HP-Bar
resource_bar = ProgressBar.new()
resource_bar.name = "ResourceBar"
resource_bar.position = Vector2(20, 50)
resource_bar.size = Vector2(200, 20)
resource_bar.show_percentage = false
resource_bar.value = 0
resource_bar.visible = false # Nur sichtbar wenn Klasse Ressource hat
var resource_style = StyleBoxFlat.new()
resource_style.bg_color = Color(0.2, 0.3, 0.9, 1.0) # Blau für Mana (Standard)
resource_bar.add_theme_stylebox_override("fill", resource_style)
resource_label = Label.new()
resource_label.name = "ResourceLabel"
resource_label.size = Vector2(200, 20)
resource_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
resource_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
resource_bar.add_child(resource_label)
control.add_child(resource_bar)
# Level/XP etwas runter verschieben wegen Ressourcen-Bar
level_label.position = Vector2(20, 75)
xp_bar.position = Vector2(80, 75)
# Gold Label
gold_label = Label.new()
gold_label.name = "GoldLabel"
gold_label.position = Vector2(20, 98)
gold_label.add_theme_font_size_override("font_size", 14)
gold_label.add_theme_color_override("font_color", Color(1, 0.85, 0, 1))
gold_label.text = "0 Gold"
control.add_child(gold_label)
# Castbar — direkt über der Aktionsleiste positioniert
_create_castbar()
# Gold aktualisieren
func update_gold(amount: int):
if gold_label:
gold_label.text = str(amount) + " Gold"
# Ressourcen-Leiste aktualisieren (Mana/Energie/Wut)
func update_resource(current: int, maximum: int, resource_name: String):
if resource_bar == null:
return
if maximum <= 0:
resource_bar.visible = false
return
resource_bar.visible = true
resource_bar.max_value = maximum
resource_bar.value = current
resource_label.text = str(current) + " / " + str(maximum)
# Farbe je nach Ressourcen-Typ
var style = resource_bar.get_theme_stylebox("fill") as StyleBoxFlat
if style:
match resource_name:
"Mana":
style.bg_color = Color(0.2, 0.3, 0.9, 1.0) # Blau
"Energie":
style.bg_color = Color(0.9, 0.8, 0.1, 1.0) # Gelb
"Wut":
style.bg_color = Color(0.8, 0.15, 0.1, 1.0) # Rot
# Icon-Textur direkt setzen (für Consumables)
func set_slot_icon_texture(slot_index: int, texture: Texture2D):
if slot_index >= 0 and slot_index < 9:
slot_icons[slot_index].texture = texture
# Slot-Icon entfernen
func clear_slot_icon(slot_index: int):
if slot_index >= 0 and slot_index < 9:
slot_icons[slot_index].texture = null
# Stack-Anzahl auf Slot anzeigen
func set_slot_stack_count(slot_index: int, count: int):
if slot_index < 0 or slot_index >= 9:
return
if count > 1:
slot_stack_labels[slot_index].text = str(count)
slot_stack_labels[slot_index].visible = true
else:
slot_stack_labels[slot_index].visible = false
# HP-Leiste und Text aktualisieren
func update_health(current_hp, max_hp):
@ -24,6 +315,14 @@ func update_health(current_hp, max_hp):
health_bar.value = current_hp
health_label.text = str(current_hp) + " / " + str(max_hp)
# Level und XP aktualisieren
func update_level(level: int, current_xp: int, xp_to_next: int):
if level_label:
level_label.text = "Lv " + str(level)
if xp_bar:
xp_bar.max_value = xp_to_next
xp_bar.value = current_xp
# Aktions-Slot kurz golden hervorheben (0.1s)
func set_active_slot(index):
action_slots[active_slot].self_modulate = Color(1, 1, 1)
@ -31,3 +330,120 @@ 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 >= 0 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
func _create_castbar():
# Eigene CanvasLayer für die Castbar
var castbar_layer = CanvasLayer.new()
castbar_layer.name = "CastbarLayer"
castbar_layer.layer = 10
add_child(castbar_layer)
castbar_container = PanelContainer.new()
castbar_container.name = "CastbarContainer"
castbar_container.visible = false
castbar_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
var style = StyleBoxFlat.new()
style.bg_color = Color(0.1, 0.1, 0.1, 0.85)
style.set_border_width_all(2)
style.border_color = Color(0.6, 0.6, 0.6)
style.set_corner_radius_all(4)
castbar_container.add_theme_stylebox_override("panel", style)
var vbox = VBoxContainer.new()
vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
castbar_container.add_child(vbox)
castbar_label = Label.new()
castbar_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
castbar_label.add_theme_font_size_override("font_size", 13)
castbar_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
vbox.add_child(castbar_label)
castbar = ProgressBar.new()
castbar.custom_minimum_size = Vector2(300, 16)
castbar.show_percentage = false
castbar.mouse_filter = Control.MOUSE_FILTER_IGNORE
var fill = StyleBoxFlat.new()
fill.bg_color = Color(1.0, 0.7, 0.0, 1.0)
fill.set_corner_radius_all(2)
castbar.add_theme_stylebox_override("fill", fill)
var bg = StyleBoxFlat.new()
bg.bg_color = Color(0.2, 0.2, 0.2, 1.0)
bg.set_corner_radius_all(2)
castbar.add_theme_stylebox_override("background", bg)
vbox.add_child(castbar)
castbar_layer.add_child(castbar_container)
# Castbar anzeigen
func show_castbar(spell_name: String, cast_time: float):
if castbar_container == null:
return
# Spell-Name übersetzen
var display_name = spell_name
match spell_name:
"frostbolt": display_name = "Frostblitz"
castbar_label.text = display_name + " (" + "%.1f" % cast_time + "s)"
castbar.max_value = cast_time
castbar.value = 0
# Mittig über der Aktionsleiste positionieren
var viewport_size = get_viewport().get_visible_rect().size
castbar_container.position = Vector2(viewport_size.x / 2 - 160, viewport_size.y - 120)
castbar_container.visible = true
# Castbar Fortschritt aktualisieren
func update_castbar(elapsed: float, total: float):
if castbar == null:
return
castbar.value = elapsed
var remaining = total - elapsed
if remaining < 0:
remaining = 0
castbar_label.text = castbar_label.text.split("(")[0].strip_edges() + " (%.1f" % remaining + "s)"
# Castbar verstecken
func hide_castbar():
if castbar_container:
castbar_container.visible = false

25
icons/autoattack_icon.svg Normal file
View file

@ -0,0 +1,25 @@
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg">
<!-- Autoattack Icon - Faust -->
<defs>
<linearGradient id="fist" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#ffd4a3;stop-opacity:1" />
<stop offset="100%" style="stop-color:#d4a574;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Circular arrow (autoattack symbol) -->
<path d="M 45 15 A 12 12 0 1 1 33 3" stroke="#4CAF50" stroke-width="3" fill="none" stroke-linecap="round"/>
<polygon points="45,15 50,10 50,20" fill="#4CAF50"/>
<!-- Fist palm -->
<rect x="18" y="35" width="16" height="20" rx="3" fill="url(#fist)" stroke="#b8945f" stroke-width="2"/>
<!-- Thumb -->
<ellipse cx="35" cy="45" rx="4" ry="8" fill="url(#fist)" stroke="#b8945f" stroke-width="2"/>
<!-- Fingers -->
<rect x="18" y="28" width="3" height="10" rx="1.5" fill="url(#fist)" stroke="#b8945f" stroke-width="1"/>
<rect x="22" y="25" width="3" height="12" rx="1.5" fill="url(#fist)" stroke="#b8945f" stroke-width="1"/>
<rect x="26" y="26" width="3" height="11" rx="1.5" fill="url(#fist)" stroke="#b8945f" stroke-width="1"/>
<rect x="30" y="28" width="3" height="9" rx="1.5" fill="url(#fist)" stroke="#b8945f" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bkujmu8r6370p"
path="res://.godot/imported/autoattack_icon.svg-8c48165bd13a879d5f6cdbc811e19882.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/autoattack_icon.svg"
dest_files=["res://.godot/imported/autoattack_icon.svg-8c48165bd13a879d5f6cdbc811e19882.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

14
icons/frostbolt_icon.svg Normal file
View file

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Eisstrahl -->
<polygon points="10,32 32,24 54,32 32,40" fill="#66CCFF" stroke="#3399DD" stroke-width="1.5"/>
<!-- Eiskristall-Spitze -->
<polygon points="54,32 62,32 58,26 58,38" fill="#99DDFF" stroke="#3399DD" stroke-width="1"/>
<!-- Frost-Partikel -->
<circle cx="20" cy="28" r="3" fill="white" opacity="0.7"/>
<circle cx="38" cy="36" r="2.5" fill="white" opacity="0.6"/>
<circle cx="28" cy="22" r="2" fill="white" opacity="0.5"/>
<!-- Schneeflocke Mitte -->
<line x1="32" y1="28" x2="32" y2="36" stroke="white" stroke-width="1.5" opacity="0.8"/>
<line x1="28" y1="30" x2="36" y2="34" stroke="white" stroke-width="1.5" opacity="0.8"/>
<line x1="28" y1="34" x2="36" y2="30" stroke="white" stroke-width="1.5" opacity="0.8"/>
</svg>

After

Width:  |  Height:  |  Size: 822 B

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d3lnef1xxgol3"
path="res://.godot/imported/frostbolt_icon.svg-97e9c6299dcf36b8065da5f6427efafe.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/frostbolt_icon.svg"
dest_files=["res://.godot/imported/frostbolt_icon.svg-97e9c6299dcf36b8065da5f6427efafe.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,26 @@
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg">
<!-- Heavy Strike Icon - Schwert mit Schlageffekt -->
<defs>
<linearGradient id="blade" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#e0e0e0;stop-opacity:1" />
<stop offset="100%" style="stop-color:#808080;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Impact lines (red) -->
<path d="M 10 15 L 20 10" stroke="#ff4444" stroke-width="3" stroke-linecap="round"/>
<path d="M 8 25 L 18 20" stroke="#ff4444" stroke-width="3" stroke-linecap="round"/>
<path d="M 12 35 L 22 30" stroke="#ff4444" stroke-width="3" stroke-linecap="round"/>
<!-- Sword blade -->
<polygon points="25,10 35,10 40,50 20,50" fill="url(#blade)" stroke="#404040" stroke-width="2"/>
<!-- Sword guard -->
<rect x="15" y="48" width="30" height="4" fill="#8B4513" stroke="#654321" stroke-width="1"/>
<!-- Sword handle -->
<rect x="25" y="50" width="10" height="10" fill="#654321" stroke="#3d2817" stroke-width="1"/>
<!-- Pommel -->
<circle cx="30" cy="62" r="4" fill="#DAA520" stroke="#B8860B" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dqkaowq4cvtbl"
path="res://.godot/imported/heavy_strike_icon.svg-ae86ca74d5a61e67598cea5812ee34ac.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/heavy_strike_icon.svg"
dest_files=["res://.godot/imported/heavy_strike_icon.svg-ae86ca74d5a61e67598cea5812ee34ac.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/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

16
icons/wand_icon.svg Normal file
View file

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Zauberstab -->
<line x1="14" y1="52" x2="44" y2="16" stroke="#A07830" stroke-width="4" stroke-linecap="round"/>
<!-- Kristallspitze -->
<polygon points="44,16 50,8 54,14 48,20" fill="#88CCFF" stroke="#4499DD" stroke-width="1.2"/>
<!-- Glühen -->
<circle cx="48" cy="14" r="8" fill="#66BBFF" opacity="0.25"/>
<circle cx="48" cy="14" r="4" fill="white" opacity="0.4"/>
<!-- Funken -->
<circle cx="38" cy="22" r="2" fill="#AADDFF" opacity="0.6"/>
<circle cx="52" cy="8" r="1.5" fill="white" opacity="0.5"/>
<circle cx="42" cy="10" r="1.5" fill="#AADDFF" opacity="0.5"/>
<!-- Griff-Wicklung -->
<line x1="20" y1="44" x2="24" y2="42" stroke="#D4AA50" stroke-width="2.5" stroke-linecap="round"/>
<line x1="22" y1="40" x2="26" y2="38" stroke="#D4AA50" stroke-width="2.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 891 B

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dsa2obffh60qx"
path="res://.godot/imported/wand_icon.svg-5bbc5f62e64992d218efbb5a4fb81c8f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/wand_icon.svg"
dest_files=["res://.godot/imported/wand_icon.svg-5bbc5f62e64992d218efbb5a4fb81c8f.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

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<!-- Stab -->
<line x1="20" y1="56" x2="38" y2="12" stroke="#8B6914" stroke-width="4" stroke-linecap="round"/>
<!-- Kristall oben -->
<polygon points="38,12 44,6 42,16 48,10" fill="#66CCFF" stroke="#3399DD" stroke-width="1"/>
<polygon points="38,12 32,6 36,16 30,10" fill="#99DDFF" stroke="#3399DD" stroke-width="1"/>
<!-- Glühen -->
<circle cx="38" cy="10" r="6" fill="#66CCFF" opacity="0.3"/>
<circle cx="38" cy="10" r="3" fill="white" opacity="0.5"/>
<!-- Wicklung -->
<line x1="25" y1="44" x2="29" y2="42" stroke="#C0A040" stroke-width="2"/>
<line x1="26" y1="40" x2="30" y2="38" stroke="#C0A040" stroke-width="2"/>
<line x1="27" y1="36" x2="31" y2="34" stroke="#C0A040" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 787 B

Some files were not shown because too many files have changed in this diff Show more