TRXTD/README.md
Tronax aa68343ac2
feat(balance): 30-wave campaign, 5 new enemy types, steeper per-wave scaling
The campaign was too easy: a handful of un-upgraded towers stopped
everything. This makes the game substantially harder across three axes.

Campaign extended from 20 to 30 hand-tuned waves (endless now starts at
31). Victory messages, HUD label (via store.totalWaves synced from
TOTAL_WAVES), start screen, lobby and README updated.

Five new enemy types debut in later waves, each with a hand-drawn
canvas look:
- Brute (wave 8): fast tank hybrid, 130 HP, 2 lives
- Phantom (wave 12): fast flyer with real HP, 2 lives
- Scorpion (wave 14): very fast ground, 2 lives
- Golem (wave 18): walking bunker, 560 HP, 3 lives
- Dragon (wave 22): flying boss, 950 HP, 3 lives, gets its own top
  boss health bar like the boss

Per-wave HP scaling steepened (1 + 0.16m + 0.025m², was 0.18m + 0.02m²):
wave 10 now ~4.6x (was 4.2x), wave 20 ~13.2x (was 11.6x), wave 30 ~29x.
Late waves mix the new types into heavy compositions; wave 30 is the
final wall (3 bosses, 2 dragons, 4 golems, 12 scorpions). Endless waves
scale all ten types with bosses every 5 and dragons from endless+2.

Balance validated with the headless greedy-bot simulation: the bot
(capped around tower level 5 on fixed spots) previously trivially won
the campaign and now dies at wave 29 — clearing wave 30 requires
evolved towers (level 6+), research bonuses and good placement. The
sim docs were updated to describe this new tuning philosophy.

New render smoke test (npm test): draws all ten enemy kinds through
the real renderer with a stub 2D context, two frames each with slow/
DoT/flash effects active, guarding every drawEnemy code path. 55 checks
green, build clean. Browser-verified: campaign starts and HUD shows
"Welle x/30".
2026-08-17 20:29:47 +02:00

198 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TRXTD Tower Defense im Browser
Ein vollständiges Tower-Defense-Spiel, gebaut mit **Vue 3 + TypeScript + Vite**.
Die Spiellogik läuft in reinem TypeScript auf einem HTML-Canvas (60 FPS), Vue kümmert sich um HUD, Menüs und Panels.
Enthält **Solo-Kampagne**, **2-Spieler-Coop** und **1v1-Duell** (Online/Lokal per WebSocket).
## Schnellstart
1. Abhängigkeiten installieren:
```bash
npm install
```
2. Frontend (Vite) und Multiplayer-Server starten:
```bash
# Terminal 1: Dev-Server
npm run dev # → http://localhost:5173
# Terminal 2: Multiplayer-Relay (nur für Coop / 1v1 nötig)
npm run server # → ws://localhost:3001
```
3. Browser öffnen unter `http://localhost:5173`.
---
## 🐳 Docker
Das Spiel gibt es als **All-in-One-Container**: Frontend (statisch) und Multiplayer-Server teilen sich **einen Port** ideal für Self-Hosting.
```bash
# Image bauen (Multi-Stage-Build: Build → schlankes Runtime-Image)
docker build -t trxtd:latest .
# Container starten
docker run -d --name trxtd -p 3001:3001 trxtd:latest
# Oder mit Docker Compose (empfohlen)
docker compose up -d
```
Danach läuft das komplette Spiel inkl. Online-Multiplayer unter **http://localhost:3001**.
**Enthaltene Härtung:**
- Multi-Stage-Build (kein Build-Tooling im Runtime-Image)
- Läuft als unprivilegierter Nutzer (`node`)
- `HEALTHCHECK` über `/health`
- `/assets/` mit immutablen Cache-Headern, SPA-Fallback für alle anderen Routen
- WebSocket-Sicherheit: Payload-Limit (4 KB), Rate-Limiting (30 Aktionen/s), Schema-Validierung, Ping/Pong-Heartbeat
**Nützliche Umgebungsvariablen:**
| Variable | Default | Bedeutung |
| --- | --- | --- |
| `PORT` | `3001` | HTTP- und WebSocket-Port |
| `DIST_DIR` | `./dist` | Ordner mit den statischen Dateien |
| `ALLOWED_ORIGINS` | | Zusätzlich erlaubte WebSocket-Origins (Komma-Liste, z. B. `https://spiel.example.com`) |
**Öffentliches Hosting hinter Reverse Proxy (Nginx Proxy Manager / NPMplus):**
1. Container **nur im internen Netz** erreichbar machen (Port 3001 **nicht** am Router freigeben!), z. B.:
```yaml
# docker-compose.yml — gemeinsames Netzwerk mit dem Proxy
services:
trxtd:
build: .
restart: unless-stopped
expose: ["3001"] # intern, kein ports:-Mapping nach außen
# networks: proxy_net ...
```
2. In NPM/NPMplus einen **Proxy Host** anlegen:
- **Domain:** `spiel.example.com` (DNS-A-Record auf den Server)
- **Scheme:** `http` · **Forward Hostname:** Containername (gemeinsames Docker-Netz) oder Server-LAN-IP · **Port:** `3001`
- **Websockets Support: ✔ aktivieren** (zwingend erforderlich für Multiplayer!)
- Block Common Exploits: ✔ · Cache Assets: ✘ (der Server setzt eigene Cache-Header)
- **SSL-Tab:** Let's-Encrypt-Zertifikat anfordern, *Force SSL* + *HTTP/2* aktivieren
3. Fertig das Spiel (inkl. Coop/1v1 über `wss://`) läuft unter `https://spiel.example.com`.
**Serverseitige Härtung (aktiv):** CSP-/Frame-/Referrer-Header · Origin-Check gegen Cross-Site-WebSocket-Hijacking · `X-Forwarded-For`-Auswertung nur aus privaten Proxy-Netzen · max. 500 WebSocket-Verbindungen total / 20 pro IP · max. 300 Räume · Payload-Limit 4 KB · Rate-Limit 30 Aktionen/s · Schema-Validierung · Ping/Pong-Heartbeat · Raum-Timeout 30 min.
**Manuell ohne Docker im Produktionsmodus starten:**
```bash
npm run build
npm run server # serves dist/ + ws auf :3001
```
### 🛡️ Admin-Dashboard (`/admin`)
Live-Übersicht über den Server: wer gerade **Solo spielt** (Karte, Schwierigkeit, Welle — angemeldete Spieler senden dazu alle 20 s einen Heartbeat), offene **Multiplayer-Räume**, Account-**Statistiken** und die **Nutzerliste**. Aktualisiert sich alle 5 s automatisch.
Freischalten über die Umgebungsvariable `ADMIN_USERS` (Komma-getrennte Benutzernamen):
```yaml
environment:
- ADMIN_USERS=deinaccount
```
Dann als eines dieser Konten unter `https://deine-domain/admin` anmelden. Solo-Spiele laufen zwar lokal im Browser, aber angemeldete Spieler melden ihren Spielstatus an den Server; Gäste bleiben unsichtbar.
---
## Spielmodi
### 1. Solo-Kampagne
Verteidige deine Basis gegen 20 handgetunte Wellen (Leicht: 30 Leben, Normal: 20 Leben, Schwer: 12 Leben). Nach Welle 20 geht es auf Wunsch im **Endlos-Modus** weiter.
### 2. 🤝 2-Spieler-Coop
- **Gemeinsame Basis & gemeinsames Gold** Sprecht euch ab!
- **Eigene Türme:** Deine Türme haben einen **blauen Ring**, die deines Partners einen **orangenen Ring** (jeder kann nur seine eigenen Türme upgraden oder verkaufen).
- Gegner haben 70 % mehr Leben, um der doppelten Feuerkraft standzuhalten.
- Wellen starten automatisch nach Countdown oder per Klick auf „Nächste Welle" (beide können klicken).
### 3. ⚔ 1v1-Duell (Tower-Battles-Prinzip)
- Jeder Spieler verteidigt sein **eigenes Spielfeld**.
- Über ein **Live-Miniaturfenster (PiP)** siehst du in Echtzeit das Spielfeld deines Gegners mit allen Türmen und Gegnern.
- **Rush-Mechanik:** Schicke gegen Gold (`60 🪙 + 8 🪙/Welle`) eine Welle schneller Läufer auf die Basis des Gegners!
- **Siegbedingungen:**
- Basis des Gegners fällt auf 0 Leben → Sofortsieg!
- Nach 20 Wellen gewinnt, wer mehr Leben übrig hat (bei Gleichstand entscheiden die Punkte).
### Multiplayer-Raum-System
- Klicke auf dem Startbildschirm auf **„Coop-Raum erstellen"** oder **„1v1-Raum erstellen"**.
- Gib deinem Mitspieler den **4-stelligen Raum-Code** (z. B. `5SK7`).
- Der Mitspieler tippt seinen Namen + den Code ein und klickt auf **„Beitreten"**.
- Der Host startet das Spiel, sobald beide in der Lobby sind.
- Funktioniert im lokalen Netzwerk (LAN / WLAN) für Freunde über das Internet einfach Port 3001 freigeben oder über ngrok/Tailscale leiten.
---
## 🌟 9-Level-Evolutionssystem
Jeder Turm hat **9 Ausbaustufen**, aufgeteilt in **3 Evolutions-Stufen**:
| Tier | Level | Farbe | Bedeutung |
| --- | --- | --- | --- |
| **Basis** | 13 | 🟡 Gelb | Höherer Grundschaden, Reichweite & Feuerrate |
| **Evolution I** | 46 | 🟠 Orange | Neue Spezialfähigkeit schaltet sich frei |
| **Evolution II** | 79 | 🔵 Blau | Finale Evolutionsstufe mit verheerenden Effekten |
### Die Evolutionen im Detail
| Turm | 🟡 Basis (L13) | 🟠 Evolution I (L46) | 🔵 Evolution II (L79) |
| --- | --- | --- | --- |
| 🏹 **Bogenturm** | Schneller Einzelschuss | **Mehrfachschuss:** Feuert 23 Pfeile gleichzeitig auf verschiedene Ziele | **Giftpfeile + 4-Fach:** Bis zu 4 Pfeile + starker Gift-DoT (28 Schaden/s) |
| 🧨 **Kanone** | Flächenschaden | **Brandgeschosse:** Hinterlässt brennenden DoT an allen getroffenen Gegnern | **Flak-Geschütz:** Trifft ab L7 auch **Flugeinheiten** mit voller Explosionskraft! |
| ❄️ **Eisturm** | Verlangsamender Puls | **Permafrost:** Jeder 3. Puls **friert alle Gegner komplett ein** (Bewegung = 0) | **Zerbrechlichkeit:** Eingefrorene Ziele nehmen **+50 % Schaden aus ALLEN Quellen**! |
| ⚡ **Teslaturm** | Kettenblitz | **Betäubungs-Schock:** Kettenblitze betäuben getroffene Ziele kurzzeitig | **Gewittersturm:** Jeder 4. Schuss schlägt bei **ALLEN Gegnern in Reichweite** gleichzeitig ein! |
| 💫 **Laserturm** | Soforttreffer | **Prisma-Brechung:** Der Laserstrahl bricht und springt auf 23 Nebenziele über | **Durchschlag:** Der Strahl durchdringt ALLES in einer Linie bis zum Rand der Reichweite |
Türme ab Level 4 erhalten im Spiel eine pulsierende **Evolutions-Aura** (orange ab L4, blau ab L7) und die Sterne im Turm-Panel zeigen die jeweilige Tier-Farbe.
---
## Gegner
**Kampagne: 30 Wellen** (danach Endlos). Die Gegnerstärke skaliert pro Welle quadratisch — späte Wellen brauchen evolvierte Türme (Level 6+) und Forschung.
- **🟢 Kriecher:** Standard-Einheit, gut für frühes Gold
- **🟡 Läufer:** Sehr schnell Bogentürme oder Eisturm nötig
- **⬜ Panzer:** Zäh, kostet **2 Leben** bei Durchbruch
- **🟣 Flieger:** Ignorieren den Weg und fliegen auf halber Höhe geradeaus (nur Bogenturm, Eisturm, Tesla, Laser!)
- **🔴 Boss:** Riesige Lebensleiste, kostet **5 Leben** (Wellen 10, 15, 20, 25, 30)
- **🟤 Brute** *(ab Welle 8):* Schneller Panzer — zäh UND flott, kostet 2 Leben
- **⚪ Phantom** *(ab Welle 12):* Schneller Flieger mit viel Leben — Luftverteidigung Pflicht
- **🦂 Skorpion** *(ab Welle 14):* Extrem schnell am Boden, kostet 2 Leben
- **🗿 Golem** *(ab Welle 18):* Laufender Bunker, riesige Lebensleiste, kostet 3 Leben
- **🐲 Drache** *(ab Welle 22):* Fliegender Boss, kostet 3 Leben — eigene Boss-Leiste am oberen Rand
---
## Hindernisse
Bäume (60 🪙) und Felsen (40 🪙) blockieren strategisch wichtige Bauplätze.
- **Klick auf das Hindernis** → „⛏ Entfernen" öffnet sich oben rechts.
- Nach dem Entfernen wird das Feld sofort bebaubar.
---
## Steuerung
- **15** Turm zum Bauen auswählen, Klick auf freies Feld platziert ihn
- **Klick auf Turm** Details, Upgrade, Verkauf, Zielmodus
- **Klick auf Hindernis** Entfernen gegen Gold
- **Leertaste** nächste Welle früh starten (Frühstart-Bonus!) / Pause (nur Solo)
- **U** Upgrade · **V** Verkaufen · **T** Zielmodus wechseln
- **P** Pause (nur Solo) · **M** Ton an/aus · **Esc** Abbrechen
- **Rechte Maustaste** Bauauswahl aufheben
---
## Technische Details
- **Client-Side Simulation:** Determinister Lockstep-Tick-Loop (30 Hz) beide Clients berechnen den exakten Spielverlauf parallel; der Server (`server/server.mjs`) vermittelt nur die Räume und leitet Aktionen weiter (minimaler Traffic, kein Lag bei der Bewegung).
- **Renderer:** Offscreen-gepufferter HTML-Canvas mit dynamischer Vollbild-Skalierung, Retina-DPR-Support, Partikelsystem und Mündungsfeuer.
- **Sound:** Web Audio API mit synthetisierten Klängen (keine externen Audio-Dateien).
- **Automatisierte Tests:**
- `npm run sim` Headless-Bot spielt die Solo-Kampagne zur Balancing-Prüfung durch
- `npx tsx scripts/test-mp.mts` Lockstep-Determinismus-Test (3600 Ticks, byte-identischer Zustand auf beiden Seiten)
- `npx tsx scripts/test-obstacle.mts` Feature-Test: Hindernis-Entfernung