Initial commit — Godot space roguelite source
- Touch controls: direct InputEventScreenTouch in shop_ui (bypass relay) - ItemDB: static preload list instead of DirAccess scan (export fix) - All 18 items with EN localisation (name_en, desc_en, category_en) - Ship playstyles: NOVA-1 shield, INFERNO ram, AURORA agile/tank - Quasar: SMBH visual, jet boost, merge, push, BH-eating - Atlas & UI text updated EN+DE Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+431
@@ -0,0 +1,431 @@
|
||||
# spacel — Architektur-Dokumentation
|
||||
|
||||
## Überblick
|
||||
|
||||
**spacel** ist ein 2D-Space-Shooter mit Roguelite-Elementen, gebaut in **Godot 4.6** (Forward Plus, D3D12). Das Spiel unterstützt 1–2 Spieler (Split-Keyboard). Es gibt keine externen Assets — alles wird prozedural gezeichnet und synthetisiert (keine Sprites, keine Audio-Dateien).
|
||||
|
||||
---
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
spacel/
|
||||
├── project.godot # Projekt-Konfiguration, Autoloads, Input-Map
|
||||
├── scenes/
|
||||
│ ├── main.tscn # Root-Scene (enthält alles)
|
||||
│ ├── game_world.tscn # Spielwelt / Hintergrund-Canvas
|
||||
│ ├── hud.tscn # HUD (CanvasLayer)
|
||||
│ └── ship_select.tscn # Schiff-Auswahlbildschirm
|
||||
├── scripts/
|
||||
│ ├── main.gd # State Machine — Spielfluss-Controller
|
||||
│ ├── game_world.gd # Spielwelt: Simulation, Rendering, Kollisionen
|
||||
│ ├── spaceship.gd # Spieler-Schiff (Datenklasse)
|
||||
│ ├── enemy_ship.gd # Gegner-Schiff mit KI
|
||||
│ ├── boss_ship.gd # Bosse: WRAITH (Welle 5) & LEVIATHAN (Welle 8)
|
||||
│ ├── bullet.gd # Projektil
|
||||
│ ├── black_hole.gd # Schwarzes Loch mit Gravitation + Supernova
|
||||
│ ├── big_wipe.gd # Big-Wipe-Event (Bildschirm-Reset)
|
||||
│ ├── cosmic_objects.gd # Alle Weltraum-Objekte (Star, Planet, ...)
|
||||
│ ├── ship_stats.gd # Roguelite-Stat-System
|
||||
│ ├── item_db.gd # Item-Datenbank (20 Items, 4 Seltenheiten)
|
||||
│ ├── hud.gd # HUD-Logik
|
||||
│ ├── hud_draw.gd # HUD-Rendering
|
||||
│ ├── settings.gd # Einstellungen (Autoload)
|
||||
│ ├── sound_manager.gd # Procedurales Audio (Autoload)
|
||||
│ ├── music_player.gd # Musik
|
||||
│ ├── shop_ui.gd # Shop zwischen den Wellen
|
||||
│ ├── ship_select.gd # Schiff-Auswahl UI
|
||||
│ ├── main_menu.gd # Hauptmenü
|
||||
│ ├── pause_menu.gd # Pause-Menü
|
||||
│ └── tr.gd # Lokalisierung (Autoload, de/en)
|
||||
└── addons/godot_mcp/ # MCP-Plugin für KI-gestützte Entwicklung
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Autoloads (Singletons)
|
||||
|
||||
| Name | Script | Funktion |
|
||||
|------|--------|----------|
|
||||
| `Settings` | `settings.gd` | Lautstärke, Grafik, Sprache — persisted in `user://settings.cfg` |
|
||||
| `SoundManager` | `sound_manager.gd` | Alle SFX prozedural generiert (Sinus/Säge/Square-Wellen, kein Audio-File) |
|
||||
| `Tr` | `tr.gd` | Übersetzungen DE/EN |
|
||||
| MCP-Dienste | `addons/godot_mcp/` | Nur für Entwicklung (Editor-Plugin) |
|
||||
|
||||
---
|
||||
|
||||
## State Machine (`main.gd`)
|
||||
|
||||
`main.gd` ist der zentrale Controller. Er verwaltet alle UI-Panels und schaltet zwischen folgenden Zuständen um:
|
||||
|
||||
```
|
||||
MAIN_MENU → SELECT → (SELECT_P2) → LAUNCHING → PLAYING ⟷ PAUSED
|
||||
↓
|
||||
RETURNING → SHOP → LAUNCHING (nächste Welle)
|
||||
↓
|
||||
GAMEOVER → MAIN_MENU
|
||||
WAVE_CLEAR → SHOP
|
||||
```
|
||||
|
||||
**Wichtige Variablen in `main.gd`:**
|
||||
- `lives_p1 / credits_p1 / stats_p1 / owned_items_p1` — Run-Zustand Spieler 1
|
||||
- `wave_number` — aktuelle Wellennummer (steigt nach jedem Shop)
|
||||
- `SHIPS[]` — 4 Schiffe (NOVA-1, INFERNO, AURORA, TITAN) mit Farbpaletten und Basis-Stats
|
||||
|
||||
**Schiff-Spielstile** (`_apply_ship_base_stats()` in `main.gd`):
|
||||
| ID | Name | Besonderheit |
|
||||
|----|------|--------------|
|
||||
| `classic` | NOVA-1 | 1 Schutzschild — ausgewogener Einstieg |
|
||||
| `inferno` | INFERNO | Speed +28 %, Feuerrate +55 %, Kurve −28 % · Passiv: Ramm-Schaden ab ≥ 4.5 px/s |
|
||||
| `aurora` | AURORA | Kurve +55 %, Speed −20 %, 2 Schilde, BH-Resist 55 %, 2× Unverwundbarkeitszeit |
|
||||
| `titan` | TITAN | Speed −28 %, Kurve −15 % · Aktiv: Boost-Impuls (SHIFT, 5 s Cooldown) |
|
||||
|
||||
`ship_id: String` in `ShipStats` wird beim Spielstart gesetzt und ermöglicht runtime-Verzweigungen (z. B. INFERNO-Rammen in `game_world.gd`).
|
||||
|
||||
**Hauptlogik:**
|
||||
- `_set_state(new_state)` — zeigt/versteckt UI, initialisiert `game_world`
|
||||
- `_process()` — Countdown vor Spielstart, Return-/Wave-Timer
|
||||
- `add_credits(player, amount)` — Credits dem Spieler gutschreiben
|
||||
- `on_game_over() / on_wave_complete()` — von `game_world` aufgerufen
|
||||
|
||||
---
|
||||
|
||||
## Spielwelt (`game_world.gd`)
|
||||
|
||||
Das Herzstück des Spiels. `game_world.gd` ist ein `Node2D` und macht **alles selbst** per `_draw()` — keine Child-Nodes für Spielobjekte.
|
||||
|
||||
### Physik-Loop
|
||||
|
||||
Fixed-Timestep bei **60 Hz** (`PHYS_DT = 1/60`):
|
||||
|
||||
```gdscript
|
||||
_process(delta):
|
||||
_phys_accum += delta # akkumuliert reale Zeit
|
||||
while _phys_accum >= PHYS_DT:
|
||||
_phys_accum -= PHYS_DT
|
||||
frame += 1
|
||||
_tick(PHYS_DT)
|
||||
queue_redraw()
|
||||
```
|
||||
|
||||
### `_tick(dt)` — Reihenfolge pro Frame
|
||||
|
||||
1. `_handle_input(dt)` — Spieler-Input lesen, Schüsse erzeugen
|
||||
2. `_update_objects(dt)` — Alle kosmischen Objekte updaten, Gegner KI
|
||||
3. `_update_bullets()` — Projektile bewegen, tote entfernen
|
||||
4. Boss-Update (falls vorhanden) → erzeugt Bullets
|
||||
5. `_update_particles(dt)` — Partikeleffekte
|
||||
6. `_check_collisions()` — Bullet/Enemy, Bullet/Player, Antimatter/Player, Bullets/Boss
|
||||
7. `_check_big_wipe()` — Threshold prüfen (> 500 Objekte)
|
||||
8. BigWipe-Update
|
||||
9. Schwierigkeit + Wave-Timer + Credit-Trickle
|
||||
10. All-Dead-Check → `main_node.on_game_over()`
|
||||
11. Kometen- und Antimatter-Spawn-Timer
|
||||
|
||||
### Arrays der Spielobjekte
|
||||
|
||||
```gdscript
|
||||
var stars, planets, nebulae, comets, galaxies: Array
|
||||
var black_holes, quasars, white_holes, neutron_stars: Array
|
||||
var antimatter, antimatter_stars: Array
|
||||
var bullets, particles: Array
|
||||
var players, enemies: Array
|
||||
```
|
||||
|
||||
### Rendering (`_draw()`)
|
||||
|
||||
Alles wird mit Godot's Canvas-API gezeichnet — **keine Sprites**:
|
||||
- `draw_rect()` für Pixel/Rumpfe
|
||||
- `draw_circle()` für Schwarze Löcher, White Holes
|
||||
- `draw_arc()` für Akkretionsscheiben, Ringe
|
||||
- `draw_line()` für Trails
|
||||
|
||||
**Zeichenreihenfolge:** Nebulae → Sterne → Galaxien → Planeten → Kometen → Quasare → White Holes → Neutronensterne → Schwarze Löcher → Antimatter → Bullets → Enemies → Boss → Spieler → Partikel → BigWipe-Overlay
|
||||
|
||||
---
|
||||
|
||||
## Datenklassen (alle `extends RefCounted`)
|
||||
|
||||
Alle Spielobjekte sind **keine Nodes** — sie sind reine Datenklassen, die von `game_world` in Arrays verwaltet werden. Das verhindert den Node-Overhead und ermöglicht den eigenen Physik-Loop.
|
||||
|
||||
### `Spaceship` (`spaceship.gd`)
|
||||
|
||||
Spieler-Schiff.
|
||||
|
||||
| Eigenschaft | Wert | Beschreibung |
|
||||
|-------------|------|--------------|
|
||||
| `THRUST` | 0.28 | Beschleunigung pro Frame |
|
||||
| `TURN_SPEED` | 0.08 | Drehgeschwindigkeit (rad/frame) |
|
||||
| `MAX_SPEED` | 7.5 | Maximale Geschwindigkeit |
|
||||
| `DRAG` | 0.985 | Trägheit (Reibung) |
|
||||
| `INVULN_FRAMES` | 90 | ~1.5s Unverwundbarkeit nach Treffer |
|
||||
| `stats.ship_id` | String | Schiff-ID für runtime-spezifische Mechaniken |
|
||||
|
||||
**Methoden:**
|
||||
- `update(thrust, turn, W, H, delta)` — Bewegung, Screen-Wrap, Trail
|
||||
- `shoot_burst() → Array[Bullet]` — erzeugt 1–N Bullets je nach `stats.bullet_count`
|
||||
- `draw(canvas, frame)` — malt Rumpf aus `HULL_PIXELS`-Tabelle mit Heading-Rotation
|
||||
|
||||
**Screen-Wrap:** Wenn Schiff den Bildschirmrand verlässt, erscheint es auf der anderen Seite (Toroidal).
|
||||
|
||||
### `EnemyShip` (`enemy_ship.gd`)
|
||||
|
||||
KI-Gegner. 2 Farbvarianten (idx 0 = Rot, idx 1 = Cyan). Jeder Gegner hat eine **Role** (AGGRO / CIRCLE / FLANK), die per `role_id % 3` beim Spawn bestimmt wird und über Respawns hinweg erhalten bleibt. `role_id` wird in `game_world.gd` als laufender Index vergeben.
|
||||
|
||||
**Rollen:**
|
||||
- **AGGRO**: stürmt direkt auf den nächsten Spieler zu
|
||||
- **CIRCLE**: hält Abstand (~180 px) und kreist kontinuierlich um den Spieler (orbit_offset dreht sich)
|
||||
- **FLANK**: nähert sich aus ±90°-Flankenwinkel statt frontal
|
||||
|
||||
**Gemeinsamkeiten:**
|
||||
- Schuss immer direkt auf Spieler gezielt (unabhängig von der Bewegungsrichtung) → CIRCLE/FLANK-Gegner bleiben gefährlich
|
||||
- **Separation Force**: Gegner stoßen sich gegenseitig ab (Radius 110 px) → kein Clumping
|
||||
- **Patrol**: außerhalb Reichweite navigieren Gegner zu zufälligen Zielpunkten auf dem Bildschirm (nicht mehr reines Heading-Drehen) → natürliche Verteilung
|
||||
- **Schwarzloch-Ausweichen**: spürt BH-Radius und weicht ab
|
||||
- Feuer-Timer ist pro `role_id` gestaffelt (FIRE_INTERVAL + rid×17) → Salven verteilt
|
||||
- Nach Tod: respawnt nach 4–8s vom zufälligen Bildschirmrand, behält Role bei
|
||||
|
||||
**Wellenskalierung:**
|
||||
- Alte Formel: `2 + (wave-1)/2` (cap 6→8 nach letztem Balancing-Pass)
|
||||
- **Neue Formel:** `min(2 + (wave - 1), MAX_ENEMEYS)` → Welle 1: 2, Welle 5: 6, Welle 10: 11, Welle 20: 21
|
||||
|
||||
### `BossShip` (`boss_ship.gd`)
|
||||
|
||||
Zwei Bosse — gleiche Klasse, unterschiedliche Parameter:
|
||||
|
||||
| | WRAITH (Welle 5) | LEVIATHAN (Welle 8) |
|
||||
|--|--|--|
|
||||
| HP | 20 | 50 |
|
||||
| Farbe | Magenta | Orange/Feuerrot |
|
||||
| Orbit-Geschwindigkeit | 0.55 rad/s | 0.44 / 0.72 (Phase 2) |
|
||||
| Feuer-Intervall | 72 Frames | 60 / 42 (Phase 2) |
|
||||
| Schüsse | 3-Way | 5-Way / 8-Way |
|
||||
| Pixel-Größe | 5 | 7 |
|
||||
|
||||
**Mechaniken:**
|
||||
- Orbitiert elliptisch um Bildschirmmitte
|
||||
- Heading zeigt immer zur Mitte (schießt nach innen)
|
||||
- LEVIATHAN: Phase-2-Übergang bei ≤ 50% HP → Black Hole spawnt, Musik verdichtet
|
||||
|
||||
### `Bullet` (`bullet.gd`)
|
||||
|
||||
- Geschwindigkeit: 9.6 px/frame
|
||||
- Lebensdauer: 240 Frames, Fade ab Frame 210
|
||||
- Besitzer-Typen: `"p1"`, `"p2"`, `"enemy"`, `"boss"` (bestimmt Kollisions-Check und Farbe)
|
||||
- `pierce`: true wenn `damage_mult >= 2.0` → trifft bis zu 2 Ziele
|
||||
|
||||
### `BlackHole` (`black_hole.gd`)
|
||||
|
||||
Komplexestes Objekt im Spiel.
|
||||
|
||||
**Parameter:**
|
||||
- `PULL_RADIUS`: 160px Gravitationsfeld
|
||||
- `SWALLOW_RADIUS`: 14px Verschluck-Radius
|
||||
- `FORCE_MULT`: 45.0 (Gravitationsstärke)
|
||||
- `SUPERNOVA_AT`: 30 verschluckte Objekte → Supernova
|
||||
|
||||
**Mechaniken:**
|
||||
- Wandert langsam umher (Micro-Drift)
|
||||
- 18% Chance: jagt den nächsten Spieler (Hunting-Mode)
|
||||
- Wächst mit jedem verschluckten Objekt (radius, pull_radius, gravity)
|
||||
- Bei 30 Verschluckungen: **Supernova** → stirbt, spawnt Quasar + neue BHs + White/Neutron Star + Sterne/Planeten
|
||||
- **SMBH**: nach 12 verschluckten Galaxien → Super-Massive BH, nach 45s kollabiert er
|
||||
- BH-BH-Verschmelzung: größerer frisst kleineren
|
||||
|
||||
### `BigWipe` (`big_wipe.gd`)
|
||||
|
||||
Notfall-Reset wenn > 500 kosmische Objekte vorhanden sind.
|
||||
|
||||
**Phasen:**
|
||||
1. `COLLAPSE` (2.33s): Bildschirm verdunkelt sich, Spieler müssen Wipe-Taste halten
|
||||
2. `FLASH` (0.4s): Weißer Flash
|
||||
3. Alle Objekte außer Planeten/Sternen werden gelöscht
|
||||
|
||||
**Spieler-Reaktion:** Signal `p1_wipe` / `p2_wipe` drücken während COLLAPSE → überleben. Wer nicht drückt, stirbt. Belohnung: 25 Credits (+ Credit-Bonus).
|
||||
|
||||
### `CosmicObjects` (`cosmic_objects.gd`)
|
||||
|
||||
Statische äußere Klasse mit inneren Klassen:
|
||||
|
||||
| Klasse | Beschreibung |
|
||||
|--------|-------------|
|
||||
| `Star` | Wandert nach oben, kann gravitationell angezogen werden, spiralt ins BH |
|
||||
| `Planet` | Kreist um Orbit-Punkt, kann von BH gefangen und zerrissen werden |
|
||||
| `Nebula` | Rein dekorativ, bewegt sich langsam |
|
||||
| `Comet` | Fliegt von Bildschirmrand zu Bildschirmrand |
|
||||
| `Galaxy` | Spiral-Galaxie, kann von BH konsumiert werden → SMBH |
|
||||
| `Quasar` | Entsteht aus Supernova, lebt 30s, rein dekorativ |
|
||||
| `WhiteHole` | Gegenteil des BH: stößt Spieler ab, ejiziert Sterne/Planeten, lebt 60s |
|
||||
| `NeutronStar` | Rotierender Pulsar-Strahl, stößt Objekte im Beam-Bereich ab |
|
||||
| `Antimatter` | Partikel, tötet Spieler/Gegner bei Berührung, ziehen sich an |
|
||||
| `AntimatterStar` | Entsteht wenn 5+ Antimatter-Partikel clustern, repulsiert Spieler, lebt 50s |
|
||||
|
||||
---
|
||||
|
||||
## Roguelite-System
|
||||
|
||||
### `ShipStats` (`ship_stats.gd`)
|
||||
|
||||
Alle Stats sind **multiplikativ** (ausgenommen additive: `bullet_count`, `shield_charges`, `bh_resist`):
|
||||
|
||||
| Stat | Standard | Effekt |
|
||||
|------|----------|--------|
|
||||
| `speed_mult` | 1.0 | Schub und Max-Speed |
|
||||
| `turn_mult` | 1.0 | Drehgeschwindigkeit |
|
||||
| `fire_rate_mult` | 1.0 | Teilt Cooldown (höher = schneller) |
|
||||
| `damage_mult` | 1.0 | Trefferzone; ≥ 2.0 → Pierce |
|
||||
| `bullet_speed_mult` | 1.0 | Projektil-Geschwindigkeit |
|
||||
| `bullet_count` | 1 | Projektile pro Schuss |
|
||||
| `shield_charges` | 0 | Absorbiert N Treffer |
|
||||
| `invuln_mult` | 1.0 | Unverwundbarkeitszeit nach Treffer |
|
||||
| `bh_resist` | 0.0 | 0–0.9: Anteil des BH-Zugs der negiert wird |
|
||||
| `wipe_mult` | 1.0 | BigWipe-Haltezeit-Faktor |
|
||||
| `credit_bonus` | 1.0 | Multiplikator auf alle Credits |
|
||||
|
||||
### `ItemDB` (`item_db.gd`)
|
||||
|
||||
Zwei Pools:
|
||||
- **Legacy `ITEMS`** (6 Einträge) — nur noch für Enemy-Boosts ab Welle 10 (`game_world.gd` rollt 1× daraus und wendet es auf jeden Gegner an).
|
||||
- **Werkstatt-Plugin-Pool** (auto-discover unter `res://items/`, derzeit 17 ItemDefs) — der eigentliche Shop-Pool.
|
||||
|
||||
Seltenheiten:
|
||||
|
||||
| Seltenheit | Gewicht | Farbe |
|
||||
|------------|---------|-------|
|
||||
| STANDARD (Common) | 60 | Grau |
|
||||
| SELTEN (Uncommon) | 25 | Grün |
|
||||
| EPISCH (Rare) | 12 | Blau |
|
||||
| LEGENDÄR (Epic) | 3 | Lila |
|
||||
|
||||
Shop zeigt nach jeder Welle 4 zufällige Items (gekaufte werden aus Folge-Rolls ausgeschlossen). Credits kommen aus:
|
||||
- Kill: 15 Credits × `credit_bonus`
|
||||
- Passiv: 5 Credits alle 10 Sekunden
|
||||
- BigWipe überlebt: 25 Credits × `credit_bonus`
|
||||
- Boss getötet: 150 (Miniboss) / 300 (Boss) Credits
|
||||
|
||||
### Werkstatt-Flow (`shop_ui.gd`)
|
||||
|
||||
Zwei Phasen pro Shop-Öffnung:
|
||||
|
||||
1. **Attribut-Phase** — nur in **ungeraden Wellen** (1, 3, 5, …). 3 zufällige Gratis-Buffs, einer wird gewählt. Werte `+8 % bis +18 %`. Gerade Wellen (2, 4, …) starten direkt in der Shop-Phase → dämpft frühes Snowballing.
|
||||
2. **Shop-Phase** — 4 Karten aus dem Plugin-Pool. Kauf per ENTER, Reroll per R, SPACE/ESC zum Verlassen.
|
||||
|
||||
**Reroll-Kosten:** `60 + 42 × reroll_count` CR → `60 / 102 / 144 / 186 / 228 …`. Der `reroll_count` wird in `main.gd` (`reroll_count_p1` / `_p2`) gehalten und **wellenübergreifend** persistiert — verteuerter Reroll schützt den Rest des Runs vor billigem Ketten-Reroll. Reset nur bei neuem Run / Game Over.
|
||||
|
||||
---
|
||||
|
||||
## Input-Mapping
|
||||
|
||||
| Aktion | Spieler 1 | Spieler 2 |
|
||||
|--------|-----------|-----------|
|
||||
| Schub | Pfeil Oben | W |
|
||||
| Links | Pfeil Links | A |
|
||||
| Rechts | Pfeil Rechts | D |
|
||||
| Schießen | Leertaste | F |
|
||||
| BigWipe | N | E |
|
||||
|
||||
---
|
||||
|
||||
## Wellen-Progression
|
||||
|
||||
| Welle | Dauer | Gegner | Boss |
|
||||
|-------|-------|--------|------|
|
||||
| 1 | 20s | 2 | — |
|
||||
| 3 | 36s | 3 | — |
|
||||
| 5 | 52s | 4 | WRAITH (Miniboss) |
|
||||
| 7 | 68s | 5 | — |
|
||||
| 8 | 76s | 5 | LEVIATHAN (Full Boss) |
|
||||
| 10+ | ≤120s | 6 (mit Stats!) | — |
|
||||
| 13+ | 120s | 8 (Cap) | — |
|
||||
|
||||
Formel: `enemy_count = min(2 + (wave-1)/2, 8)`. Enemy-Stats bleiben Welle 1–9 auf Basis; ab Welle 10 bekommt jeder Gegner zusätzlich 1 zufälliges Legacy-Item.
|
||||
|
||||
**Schwierigkeits-Ramp:** `difficulty = clamp(game_time / 300.0, 0.0, 1.0)` — steuert Kometen-Spawn-Rate, BH-Cap und mehr.
|
||||
|
||||
---
|
||||
|
||||
## Balancing-Notizen
|
||||
|
||||
Die Power-Kurve ist bewusst **langsam**. Quellen wurden 2026-04-20 global gesenkt, um zu verhindern, dass der Spieler Welle 3 bereits dominiert.
|
||||
|
||||
### Plugin-Items (Kern-Multiplikatoren)
|
||||
|
||||
| Kategorie | Item | Kern-Effekt | Kosten |
|
||||
|-----------|------|-------------|--------|
|
||||
| Waffe | `wk_burst` (EPIC) | fire_rate ×1.80, damage ×0.45 | 160 |
|
||||
| Waffe | `wk_laser` | fire_rate ×1.35, damage ×0.70 | 115 |
|
||||
| Waffe | `wk_plasma` | damage ×1.55, proj_speed ×0.60 | 130 |
|
||||
| Waffe | `wk_ion` | damage ×1.25, +1 Projektil, speed ×0.70 | 140 |
|
||||
| Waffe | `wk_rail` | proj_speed ×1.50, fire_rate ×0.50 | 125 |
|
||||
| Waffe | `wk_sniper` | proj_speed ×1.30, damage ×1.18, fire_rate ×0.55 | 115 |
|
||||
| Waffe | `wk_shotgun` | +2 Projektile, proj_speed ×0.40, damage ×0.80 | 105 |
|
||||
| Waffe | `wk_scatter` | +1 Projektil, damage ×0.75 | 115 |
|
||||
| Antrieb | `drive_overdrive` | speed ×1.35, −1 Schild | 95 |
|
||||
| Antrieb | `drive_quantum` | speed ×1.22, turn ×1.18, proj_speed ×0.80 | 135 |
|
||||
| Antrieb | `drive_steer` | turn ×1.35, speed ×0.90 | 85 |
|
||||
| Hülle | `hull_giant` (EPIC) | +2 Schild, invuln ×1.20, speed ×0.70, turn ×0.80 | 220 |
|
||||
| Hülle | `hull_plating` | +1 Schild, speed ×0.85 | 120 |
|
||||
| Hülle | `hull_reaktor` | invuln ×1.45, fire_rate ×0.85 | 115 |
|
||||
| Hülle | `hull_nullfeld` | bh_resist +0.45, speed ×0.80 | 125 |
|
||||
| Spezial | `special_credit_mag` | credit_bonus ×1.18 | 170 |
|
||||
| Spezial | `special_wipe_core` | wipe_mult ×0.65 | 150 |
|
||||
|
||||
### Legacy-ITEMS (nur Enemy-Boosts Welle 10+)
|
||||
|
||||
| id | Effekt | Kosten |
|
||||
|----|--------|--------|
|
||||
| thrust_1 | speed ×1.12 | 50 |
|
||||
| firerate_1 | fire_rate ×1.15 | 50 |
|
||||
| damage_1 | damage ×1.15 | 50 |
|
||||
| shield_1 | +1 Schild | 55 |
|
||||
| firerate_2 | fire_rate ×1.22 | 90 |
|
||||
| damage_2 | damage ×1.22, proj_speed ×1.10 | 100 |
|
||||
|
||||
### Attribute (Gratis, ungerade Wellen)
|
||||
|
||||
`ATTR_POOL` in `shop_ui.gd` — Werte: speed/turn/fire/damage/proj je **1.08–1.12**, invuln 1.18, credit_bonus 1.08, Schild +1.
|
||||
|
||||
---
|
||||
|
||||
## Audio-System (`sound_manager.gd`)
|
||||
|
||||
Kein einziger Audio-File im Projekt. Alle Sounds werden mit `AudioStreamGenerator` pro Frame synthetisiert:
|
||||
|
||||
- `_play_tone(freq, duration, wave, vol_db, end_freq)` — Sinus/Säge/Square mit Frequenz-Sweep und Hüllkurve
|
||||
- `_play_noise(duration, vol_db)` — Weißes Rauschen mit Decay
|
||||
- `_play_chord_fanfare()` — Drei Töne mit kurzem Delay (BigWipe überlebt)
|
||||
|
||||
SFX-Typen: `player_shoot`, `enemy_shoot`, `enemy_die`, `player_die`, `antimatter_hit`, `bh_swallow`, `wipe_start`, `wipe_flash`, `wipe_survived`, `smbh_spawn`, `antimatter_swarm`
|
||||
|
||||
---
|
||||
|
||||
## Grafik-System
|
||||
|
||||
**Viewport:** 960×600px, Stretch-Modus `canvas_items / expand`
|
||||
**Hintergrundfarbe:** `#0a0a14` (Tiefschwarz-Blau)
|
||||
**FPS-Cap:** 60
|
||||
**Pixel-Filter:** Nearest-Neighbor (`default_texture_filter=0`)
|
||||
|
||||
Alle Schiffe benutzen `HULL_PIXELS`-Arrays — lokale Koordinaten-Offsets, die per `cos(heading)/sin(heading)` in Weltkoordinaten gedreht werden. Jeder "Pixel" ist ein 3×3-`draw_rect`.
|
||||
|
||||
---
|
||||
|
||||
## Signalfluss
|
||||
|
||||
```
|
||||
game_world → main_node.on_game_over() # alle Spieler tot
|
||||
game_world → main_node.on_wave_complete() # Wave-Timer abgelaufen
|
||||
game_world → main_node.add_credits(player, n) # Credits vergeben
|
||||
game_world → main_node.start_boss_music(final) # Boss spawnt
|
||||
game_world → main_node.boss_phase_changed(2) # Leviathan Phase 2
|
||||
big_wipe.wipe_complete → game_world._on_wipe_complete()
|
||||
shop_ui.shop_closed → main._on_shop_closed()
|
||||
pause_menu.resume_requested → main._on_pause_resume()
|
||||
main_menu.mode_selected → main._on_mode_selected(multi)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MCP-Plugin (`addons/godot_mcp/`)
|
||||
|
||||
Das Godot-MCP-Pro-Plugin verbindet den Godot-Editor per WebSocket mit externen KI-Tools (Claude Code). Ermöglicht das Lesen/Schreiben von Szenen, Scripts und Eigenschaften direkt aus der KI-Konversation heraus. Nur für die Entwicklung relevant — kein Einfluss auf das Spiel. Referenz dazu in der CLaude.md
|
||||
Reference in New Issue
Block a user