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 |
|
| RMB gehalten | Kamera drehen, Spieler schaut mit |
|
||||||
| Linksklick auf Gegner | Ziel markieren |
|
| Linksklick auf Gegner | Ziel markieren |
|
||||||
| Rechtsklick auf Gegner | Ziel markieren + Autoattack starten |
|
| 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 |
|
| Leertaste | Springen |
|
||||||
| T | (Test) 10 Schaden am Spieler |
|
| T | (Test) 10 Schaden am Spieler |
|
||||||
|
|
||||||
|
|
@ -32,7 +35,7 @@ Gegner bekämpfen und ihre Charaktere mit verschiedenen Klassen und Ausrüstunge
|
||||||
## Szenen-Struktur
|
## Szenen-Struktur
|
||||||
|
|
||||||
### world.tscn
|
### world.tscn
|
||||||
Hauptszene der Spielwelt.
|
Hauptszene der Spielwelt. Zeigt bei Start das Hauptmenü (Einstellungen), dann Klassenauswahl.
|
||||||
```
|
```
|
||||||
World (Node3D)
|
World (Node3D)
|
||||||
├── Player (player.tscn)
|
├── Player (player.tscn)
|
||||||
|
|
@ -45,21 +48,25 @@ World (Node3D)
|
||||||
```
|
```
|
||||||
|
|
||||||
### player.tscn
|
### player.tscn
|
||||||
Der Spielercharakter.
|
Der Spielercharakter mit allen UI-Panels.
|
||||||
```
|
```
|
||||||
Player (CharacterBody3D)
|
Player (CharacterBody3D)
|
||||||
├── [Kenney GLB Modell]
|
├── PlayerModel (warrior.fbx — Mixamo Charakter mit Skeleton + AnimationPlayer)
|
||||||
├── CollisionShape3D
|
├── CollisionShape3D
|
||||||
├── CameraPivot (Node3D)
|
├── CameraPivot (Node3D)
|
||||||
│ └── Camera3D
|
│ └── 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
|
### enemy.tscn
|
||||||
Ein Gegner.
|
Ein Gegner mit Level-basiertem Stats-System.
|
||||||
```
|
```
|
||||||
Enemy (CharacterBody3D)
|
Enemy (CharacterBody3D)
|
||||||
├── [Kenney GLB Modell]
|
├── EnemyModel (warrior.fbx — Mixamo Charakter mit Skeleton + AnimationPlayer)
|
||||||
├── CollisionShape3D
|
├── CollisionShape3D
|
||||||
├── Area3D
|
├── Area3D
|
||||||
│ └── CollisionShape3D
|
│ └── CollisionShape3D
|
||||||
|
|
@ -67,188 +74,414 @@ Enemy (CharacterBody3D)
|
||||||
└── HealthLabel (Label3D)
|
└── 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
|
### CharacterClass (character_class.gd)
|
||||||
Steuert den Spielercharakter.
|
Resource-Klasse die eine spielbare Klasse definiert.
|
||||||
|
|
||||||
**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).
|
|
||||||
|
|
||||||
| Property | Typ | Beschreibung |
|
| Property | Typ | Beschreibung |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| name | String | Waffenname |
|
| class_name_de | String | Deutscher Klassenname |
|
||||||
| weapon_type | WeaponType | UNARMED, SWORD, AXE, MACE, DAGGER, STAFF, BOW |
|
| main_stat | MainStat | STRENGTH, AGILITY oder INTELLIGENCE |
|
||||||
| min_damage | int | Minimaler Schaden |
|
| base_strength/agility/intelligence/stamina | int | Basis-Stats auf Level 1 |
|
||||||
| max_damage | int | Maximaler Schaden |
|
| strength/agility/intelligence/stamina_per_level | float | Stat-Zuwachs pro Level |
|
||||||
| attack_speed | float | Angriffsgeschwindigkeit (Cooldown) |
|
| unarmed_min_damage | int | Unbewaffneter Mindestschaden |
|
||||||
| range | float | Angriffsreichweite |
|
| unarmed_max_damage | int | Unbewaffneter Maximalschaden |
|
||||||
| icon | Texture2D | Icon für die Aktionsleiste |
|
| 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)
|
**Konstanten:**
|
||||||
Definiert einen Angriff — Schaden kommt von der ausgerüsteten Waffe.
|
- `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 |
|
| Property | Typ | Beschreibung |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| name | String | Angriffsname |
|
| item_name | String | Name des Tranks |
|
||||||
| damage_type | DamageType | PHYSICAL, FIRE, ICE, LIGHTNING, POISON |
|
| effect_type | EffectType | HEAL_HP, RESTORE_MANA, HEAL_AND_MANA |
|
||||||
| icon | Texture2D | Icon für die Aktionsleiste |
|
| 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
|
## Kampfsystem
|
||||||
|
|
||||||
### Autoattack
|
### Global Cooldown (GCD)
|
||||||
- Rechtsklick auf Gegner → Ziel markieren + Autoattack startet
|
Alle Aktionen (Autoattack + Skills) teilen sich einen GCD.
|
||||||
- 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
|
|
||||||
|
|
||||||
### Schadenstypen (geplant)
|
**Formel:** `GCD = Waffen-Attackspeed / (1 + Haste)`
|
||||||
- **PHYSICAL** – normaler Schaden
|
|
||||||
- **FIRE** – Feuerschaden (z.B. Magier)
|
Beispiel: Waffe mit 1.5s + 50% Haste → `1.5 / 1.5 = 1.0s`
|
||||||
- **ICE** – Eisschaden
|
|
||||||
- **LIGHTNING** – Blitzschaden
|
### Schadensberechnung
|
||||||
- **POISON** – Giftschaden (Schaden über Zeit geplant)
|
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
|
## Geplante Features
|
||||||
|
- [ ] Wut-Ressource für Krieger
|
||||||
### Klassen-System
|
- [ ] Ressourcen-System für Gegner (nicht alle haben Mana)
|
||||||
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
|
|
||||||
- [ ] Spell-System (Feuerbälle etc.)
|
- [ ] Spell-System (Feuerbälle etc.)
|
||||||
- [ ] Inventar und Ausrüstungssystem
|
- [ ] Schadenstypen (Physical, Fire, Ice, Lightning, Poison)
|
||||||
- [ ] Waffen aufheben und ausrüsten
|
|
||||||
- [ ] Erfahrungspunkte und Level-Up
|
|
||||||
- [ ] Mehrere Gegnertypen
|
- [ ] Mehrere Gegnertypen
|
||||||
- [ ] Dungeon-Level mit Wänden und Räumen
|
- [ ] Dungeon-Level mit Wänden und Räumen
|
||||||
- [ ] Multiplayer (bis zu 6 Spieler)
|
- [ ] Multiplayer (bis zu 6 Spieler)
|
||||||
- [ ] Gegner-Respawn
|
|
||||||
- [ ] Boss-Gegner
|
- [ ] Boss-Gegner
|
||||||
|
- [ ] Item-Shop / Händler
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Projektstruktur
|
## Projektstruktur
|
||||||
```
|
```
|
||||||
DungeonCrawler/
|
DungeonCrawler/
|
||||||
├── assets/ # 3D-Modelle (Kenney GLB)
|
├── assets/ # 3D-Modelle und Animationen
|
||||||
├── resources/
|
│ ├── models/ # Mixamo Charakter-Modelle (warrior.fbx + Texturen)
|
||||||
│ ├── attack.gd # Attack-Ressourcen-Klasse
|
│ ├── animations/ # Mixamo Animationen (Walking, Attack, etc.)
|
||||||
│ └── weapon.gd # Weapon-Ressourcen-Klasse
|
│ └── kenney_blocky-characters_20/ # Kenney Block-Chars (nicht mehr aktiv)
|
||||||
├── basic_attack.tres # Autoattack-Ressource
|
├── classes/ # Klassen-Definitionen (.tres)
|
||||||
├── camera_pivot.gd # Kamera-Script
|
│ ├── warrior.tres # Krieger (Ressource: NONE)
|
||||||
├── enemy.gd # Gegner-Script
|
│ ├── rogue.tres # Schurke (Ressource: ENERGY, 100)
|
||||||
├── enemy.tscn # Gegner-Scene
|
│ └── mage.tres # Magier (Ressource: MANA, 100)
|
||||||
├── hud.gd # HUD-Script
|
├── consumables/ # Verbrauchbare Items (.tres)
|
||||||
├── hud.tscn # HUD-Scene
|
│ ├── small_hp_potion.tres
|
||||||
├── player.gd # Spieler-Script
|
│ └── small_mana_potion.tres
|
||||||
├── player.tscn # Spieler-Scene
|
├── equipment/ # Equipment-Items (.tres)
|
||||||
├── world.gd # Welt-Script
|
│ ├── iron_sword.tres
|
||||||
├── world.tscn # Hauptszene
|
│ ├── steel_sword.tres
|
||||||
└── PROJEKTDOKU.md # Diese Dokumentation
|
│ ├── 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
|
# Steuert den Gegner: KI-Bewegung zum Spieler, Angriff, HP, Zielanzeige
|
||||||
extends CharacterBody3D
|
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 SPEED = 3.0
|
||||||
|
const PATROL_SPEED = 1.5
|
||||||
const GRAVITY = 9.8
|
const GRAVITY = 9.8
|
||||||
const ATTACK_DAMAGE = 5
|
|
||||||
const ATTACK_RANGE = 1.5
|
const ATTACK_RANGE = 1.5
|
||||||
const ATTACK_COOLDOWN = 2.0
|
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
|
# Level-Differenz Konstanten
|
||||||
var current_hp = 50
|
const LEVEL_DIFF_DAMAGE_MOD = 0.1 # 10% mehr/weniger Schaden pro Level-Differenz
|
||||||
var target = null # Ziel des Gegners (normalerweise der Spieler)
|
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 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
|
@onready var health_label = $HealthLabel
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
_calculate_stats()
|
||||||
|
current_hp = max_hp
|
||||||
health_label.visible = false
|
health_label.visible = false
|
||||||
_update_label()
|
_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
|
# HP-Label Text aktualisieren
|
||||||
func _update_label():
|
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)
|
# HP-Label anzeigen (wenn Gegner markiert wird)
|
||||||
func show_health():
|
func show_health():
|
||||||
|
|
@ -35,45 +175,145 @@ func hide_health():
|
||||||
func take_damage(amount):
|
func take_damage(amount):
|
||||||
current_hp -= amount
|
current_hp -= amount
|
||||||
_update_label()
|
_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:
|
if current_hp <= 0:
|
||||||
die()
|
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
|
# Gegner aus der Szene entfernen
|
||||||
func die():
|
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()
|
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):
|
func _physics_process(delta):
|
||||||
if not is_on_floor():
|
if not is_on_floor():
|
||||||
velocity.y -= GRAVITY * delta
|
velocity.y -= GRAVITY * delta
|
||||||
|
|
||||||
if target == null:
|
if target == null:
|
||||||
|
# Ohne Spieler-Referenz nur patrouillieren
|
||||||
|
_do_patrol()
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
return
|
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:
|
# State-Wechsel basierend auf Distanz
|
||||||
# In Reichweite: angreifen
|
match current_state:
|
||||||
velocity.x = 0
|
State.PATROL:
|
||||||
velocity.z = 0
|
if distance_to_player <= AGGRO_RANGE:
|
||||||
if can_attack:
|
current_state = State.CHASE
|
||||||
_attack()
|
print("Gegner hat Spieler entdeckt!")
|
||||||
else:
|
else:
|
||||||
# Direkt auf Ziel zubewegen
|
_do_patrol()
|
||||||
var direction = (target.global_position - global_position)
|
State.CHASE:
|
||||||
direction.y = 0
|
if distance_to_player <= ATTACK_RANGE:
|
||||||
direction = direction.normalized()
|
current_state = State.ATTACK
|
||||||
velocity.x = direction.x * SPEED
|
else:
|
||||||
velocity.z = direction.z * SPEED
|
_chase_player()
|
||||||
look_at(Vector3(target.global_position.x, global_position.y, target.global_position.z))
|
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()
|
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
|
# Angriff mit Cooldown
|
||||||
func _attack():
|
func _attack():
|
||||||
can_attack = false
|
can_attack = false
|
||||||
target.take_damage(ATTACK_DAMAGE)
|
_play_anim("autoattack")
|
||||||
print("Gegner greift an!")
|
# 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
|
await get_tree().create_timer(ATTACK_COOLDOWN).timeout
|
||||||
can_attack = true
|
can_attack = true
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
[gd_scene format=3 uid="uid://cvojaeanxugfj"]
|
[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"]
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4gyqm"]
|
||||||
radius = 0.6
|
radius = 0.6
|
||||||
|
|
@ -11,8 +12,9 @@ radius = 0.6
|
||||||
height = 3.0
|
height = 3.0
|
||||||
|
|
||||||
[node name="Enemy" type="CharacterBody3D" unique_id=332011146]
|
[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)
|
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]
|
[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
|
# 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
|
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_bar = $Control/HealthBar
|
||||||
@onready var health_label = $Control/HealthBar/HealthLabel
|
@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 = [
|
@onready var action_slots = [
|
||||||
$Control/ActionBar/A1,
|
$Control/ActionBar/A1,
|
||||||
$Control/ActionBar/A2,
|
$Control/ActionBar/A2,
|
||||||
|
|
@ -17,6 +33,281 @@ extends CanvasLayer
|
||||||
]
|
]
|
||||||
|
|
||||||
var active_slot = 0
|
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
|
# HP-Leiste und Text aktualisieren
|
||||||
func update_health(current_hp, max_hp):
|
func update_health(current_hp, max_hp):
|
||||||
|
|
@ -24,6 +315,14 @@ func update_health(current_hp, max_hp):
|
||||||
health_bar.value = current_hp
|
health_bar.value = current_hp
|
||||||
health_label.text = str(current_hp) + " / " + str(max_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)
|
# Aktions-Slot kurz golden hervorheben (0.1s)
|
||||||
func set_active_slot(index):
|
func set_active_slot(index):
|
||||||
action_slots[active_slot].self_modulate = Color(1, 1, 1)
|
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)
|
action_slots[active_slot].self_modulate = Color(1, 0.8, 0)
|
||||||
await get_tree().create_timer(0.1).timeout
|
await get_tree().create_timer(0.1).timeout
|
||||||
action_slots[active_slot].self_modulate = Color(1, 1, 1)
|
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 |