Merge branch 'master' of http://git.andresaiko.de:3000/Maplpapl/DungeonCrawler
553
PROJEKTDOKU.md
|
|
@ -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 (1–9)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
```
|
||||
|
|
|
|||
BIN
assets/Warrior+Animation/castle_guard_01.fbx
Normal file
BIN
assets/Warrior+Animation/idle.fbx
Normal file
BIN
assets/Warrior+Animation/jump.fbx
Normal file
BIN
assets/Warrior+Animation/left strafe walking.fbx
Normal file
BIN
assets/Warrior+Animation/left strafe.fbx
Normal file
BIN
assets/Warrior+Animation/left turn 90.fbx
Normal file
BIN
assets/Warrior+Animation/left turn.fbx
Normal file
BIN
assets/Warrior+Animation/right strafe walking.fbx
Normal file
BIN
assets/Warrior+Animation/right strafe.fbx
Normal file
BIN
assets/Warrior+Animation/right turn 90.fbx
Normal file
BIN
assets/Warrior+Animation/right turn.fbx
Normal file
BIN
assets/Warrior+Animation/running.fbx
Normal file
BIN
assets/Warrior+Animation/walking.fbx
Normal file
BIN
assets/animations/Autoattack.fbx
Normal file
44
assets/animations/Autoattack.fbx.import
Normal 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
|
||||
BIN
assets/animations/Dying Backwards.fbx
Normal file
44
assets/animations/Dying Backwards.fbx.import
Normal 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
|
||||
BIN
assets/animations/Heavy Strike.fbx
Normal file
44
assets/animations/Heavy Strike.fbx.import
Normal 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
44
assets/animations/Idle.fbx.import
Normal 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
|
||||
BIN
assets/animations/Jumping.fbx
Normal file
44
assets/animations/Jumping.fbx.import
Normal 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
|
||||
BIN
assets/animations/Left Strafe Walking.fbx
Normal file
44
assets/animations/Left Strafe Walking.fbx.import
Normal 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
|
||||
BIN
assets/animations/Right Strafe Walking.fbx
Normal file
44
assets/animations/Right Strafe Walking.fbx.import
Normal 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
|
||||
BIN
assets/animations/Start Walking.fbx
Normal file
44
assets/animations/Start Walking.fbx.import
Normal 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
|
||||
BIN
assets/animations/Stop Walking.fbx
Normal file
44
assets/animations/Stop Walking.fbx.import
Normal 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
|
||||
BIN
assets/animations/Walking Backwards.fbx
Normal file
44
assets/animations/Walking Backwards.fbx.import
Normal 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
|
||||
BIN
assets/animations/Walking.fbx
Normal file
44
assets/animations/Walking.fbx.import
Normal 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
|
||||
BIN
assets/animations/Walking_0.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
44
assets/animations/Walking_0.png.import
Normal 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
|
||||
BIN
assets/animations/Walking_1.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
43
assets/animations/Walking_1.png.import
Normal 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
|
||||
BIN
assets/animations/Walking_2.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
44
assets/animations/Walking_2.png.import
Normal 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
44
assets/models/warrior.fbx.import
Normal 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
|
After Width: | Height: | Size: 1.9 MiB |
44
assets/models/warrior_0.png.import
Normal 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
|
After Width: | Height: | Size: 1.2 MiB |
43
assets/models/warrior_1.png.import
Normal 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
|
After Width: | Height: | Size: 1.5 MiB |
44
assets/models/warrior_2.png.import
Normal 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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
uid://ci45xxb5vn857
|
||||
203
character_panel.gd
Normal 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
|
|
@ -0,0 +1 @@
|
|||
uid://7pdlor66gi51
|
||||
124
character_panel.tscn
Normal 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
|
|
@ -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()
|
||||
1
class_selection_menu.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://db8m2uw42hqfc
|
||||
80
class_selection_menu.tscn
Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
uid://b25cvd8swhsg3
|
||||
14
consumables/small_hp_potion.tres
Normal 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
|
||||
14
consumables/small_mana_potion.tres
Normal 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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
uid://re0xiie1udfq
|
||||
13
equipment/iron_helm.tres
Normal 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
|
|
@ -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")
|
||||
13
equipment/leather_chest.tres
Normal 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")
|
||||
20
equipment/steel_sword.tres
Normal 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")
|
||||
20
equipment/wooden_shield.tres
Normal 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")
|
||||
14
equipment/wooden_staff.tres
Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -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 |
43
icons/autoattack_icon.svg.import
Normal 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
|
|
@ -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 |
43
icons/frostbolt_icon.svg.import
Normal 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
|
||||
26
icons/heavy_strike_icon.svg
Normal 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 |
43
icons/heavy_strike_icon.svg.import
Normal 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
|
|
@ -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 |
43
icons/hp_potion_icon.svg.import
Normal 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
|
|
@ -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 |
43
icons/iron_helm_icon.svg.import
Normal 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
|
|
@ -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 |
43
icons/iron_sword_icon.svg.import
Normal 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
|
||||
14
icons/leather_chest_icon.svg
Normal 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 |
43
icons/leather_chest_icon.svg.import
Normal 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
|
||||
12
icons/mana_potion_icon.svg
Normal 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 |
43
icons/mana_potion_icon.svg.import
Normal 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
|
||||
11
icons/steel_sword_icon.svg
Normal 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 |
43
icons/steel_sword_icon.svg.import
Normal 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
|
|
@ -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 |
43
icons/wand_icon.svg.import
Normal 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
|
||||
17
icons/wooden_shield_icon.svg
Normal 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 |
43
icons/wooden_shield_icon.svg.import
Normal 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
|
||||
14
icons/wooden_staff_icon.svg
Normal 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 |