Files
InfiniTower/docs/spec-vertical-slice.md

526 lines
36 KiB
Markdown
Raw 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.
# Vertical-Slice-Spec: InfiniTower
- **Status:** Angenommen
- **Datum:** 2026-08-10
- **Betrifft:** Den vollständigen Funktionsumfang des Vertical Slice — was gebaut wird und woran
wir erkennen, dass es fertig ist
- **Baut auf:** [ADR-0001](adr/0001-balancing-daten-als-rust-konstanten.md) (Balancing-Daten),
[ADR-0002](adr/0002-stat-und-modifier-pipeline.md) (Stat- und Modifier-Pipeline),
[ADR-0003](adr/0003-save-format-ort-und-migration.md) (Save-Format), `CONTEXT.md` (Glossar und
Slice-Scope), Karte in Issue #1
- **Vokabular:** verbindlich aus `CONTEXT.md`. Diese Spec führt keine neuen Begriffe ein, außer den
beiden in Abschnitt „Zwei Grenzwerte, zwei Begriffe" ausgewiesenen.
Diese Spec sagt, **was** gebaut wird. Die ADRs sagen, **warum es so** gebaut wird — ihre Argumente
werden hier nicht wiederholt, sondern verlinkt. Wo eine Zahl noch nicht feststeht, steht sie als
‹Platzhalter›; sie lebt dann in `Balance` (ADR-0001) und wird durch Spielen gefunden, nicht hier.
---
## Der Loop
Der Spieler startet einen Run. Der Turm steht in der Mitte der Arena und schießt von selbst auf
Gegner, die in Wellen radial von außen anlaufen; jeder Kill gibt Coins, und aus Coins kauft der
Spieler jederzeit In-Run-Upgrades, die den Turm für diesen Run stärker machen. Die Wellen wachsen
schneller als das, was er sich leisten kann — nach sechs bis neun Minuten überrennen sie ihn, der
Turm stirbt, und alle In-Run-Upgrades verfallen mit ihm. Was bleibt, ist die Meta-Währung, die die
Gegner unterwegs gedroppt haben, und was der Boss auf Welle 10 fallen gelassen hat. Damit kauft er
zwischen den Runs permanente Workshop-Upgrades und rüstet gedroppte Cards aus. Der nächste Run
beginnt mit einem stärkeren Turm, kommt tiefer, wirft mehr ab — und endet wieder. Das ist das
Spiel: sterben ist der Fortschrittsmechanismus, nicht sein Gegenteil.
---
## Zwei Grenzwerte, zwei Begriffe
Die Karte nennt beides „Cap", meint aber zwei verschiedene Dinge. Diese Spec trennt sie, und der
Code tut es auch:
| Begriff | Code | Bedeutung |
|---|---|---|
| **Harte Obergrenze** | `HARD_ENEMY_CAP` | 10 000 gleichzeitige Gegner. Ein Perf-Grenzwert aus #4, kein Design-Werkzeug. Wird nie überschritten, von nichts. |
| **Soll-Population** | `target_population(wave)` | Die für die aktuelle Welle angestrebte Zahl lebender Gegner. Wächst mit dem Wellenzähler und ist selbst Teil der Schwierigkeitskurve. Bindet ständig — sie ist die Design-Zielgröße. |
Ein Effekt, der „mehr Gegner" verspricht, hebt die **Soll-Population** an, nie eine Spawn-Zahl
(Begründung: Cap-Analyse in Issue #1 — bei bindender Soll-Population wäre ein Spawn-Multiplikator
exakt wirkungslos). Die harte Obergrenze bleibt davon unberührt.
---
## Feststehende Zahlen
Alles hier ist entschieden. Alles andere ist ‹Platzhalter› oder steht unter „Offen".
| Größe | Wert | Quelle |
|---|---|---|
| Wellen-Takt | ‹26 s› Spawn-Phase + ‹9 s› Cooldown-Phase = ‹35 s› bei Normalgeschwindigkeit | Primärtext, als Startwert übernommen (#8) |
| Wellen pro Spielzeit | 10 min ≈ 17 Wellen · 20 min ≈ 34 · 30 min ≈ 51 | Folge des Takts (#8) |
| Boss-Welle | jede 10. Welle | Primärtext (#3), übernommen |
| Ziel für Run 1 | Tod zwischen **Welle 10 und 15** — genau ein Boss gesehen, kurz danach tot; 6–9 Minuten | #8 |
| Wachstumsform | konstant exponentiell pro Welle, plus ein Multiplikator auf Boss-Wellen | #8 |
| Kosten- vs. HP-Verlauf | Kosten wachsen anfangs langsamer als Gegner-HP, ab ca. zwei Dritteln der Runlänge überholt die HP-Kurve. Entsteht von selbst, wenn der Kosten-Exponent das Einkommenswachstum übersteigt — **kein separater Knick** | #8 |
| Meta-Ertrag | Drop-basiert: Chance je Normalgegner, **garantiert je Boss**; gedroppte Menge wellenabhängig | #8 |
| Workshop-Kosten | exponentiell pro Level | #8 |
| Tuning-Zielgröße im Run | Zeit zwischen zwei bezahlbaren Käufen: früh ‹10–20 s›, später seltener | #8 |
| Harte Gegner-Obergrenze | 10 000 gleichzeitig (gemessener Bruch bei ~70 000, ohne Texturen/UI/Audio) | #4, Issue #1 |
| Perf-Budget bei 10 000 | Gameplay-Logik 0,114 ms von 16,67 ms; Rendering 96–98 % | #4 |
| In-Run-Shop-Struktur | vier Tabs: **Attack Shop · Defence Shop · Utility Shop · Ultimates** | HUD-Mockup, #10 |
| Shop-Kapazität | ~8 Zeilen je Tab ohne Scrollen → Größenordnung **~24 In-Run-Upgrades** | HUD-Mockup, #10 |
| Kauf-Modus | Buy x1, Kosten pro Kauf, **jederzeit** im Run — kein Wellenende, keine Freigabe | Issue #1, #8 |
| Card-Seltenheiten | drei: **Common / Rare / Epic** | #11 |
| Card-Slots | Start **1**, Wachstum auf **5**; die Slot-Zahl ist selbst Progression | #11 |
| Card-Quelle | Drop aus Fortschritt (Boss-Kill, neuer Wellen-Rekord). Kein Kauf, keine dritte Währung | #11 |
| Card-Randbedingungen | Slot 2 fällt **früh** (erste paar Runs); **mindestens zwei Epics** | #11 |
| Ultimate | genau eine: **Death Wave**, radialer Sweep, automatisch ausgelöst | #9 |
| Auslöse-Regel | **Cooldown UND ein Weltzustand**, je Fähigkeit fest verdrahtet, nicht einstellbar; feuern mehrere gleichzeitig, feuern alle | #12 |
| Währungen | zwei: `Coins` (In-Run, verfällt) und die Meta-Währung (Workshop). Nicht ineinander konvertierbar | Issue #1, #11 |
| Turm-Stats | 12, siehe Tabelle in ADR-0002 §3 | ADR-0002 |
**Prüfkriterium für die fertige Kurve** (#8, gilt für die vier freien Zahlen):
1. Run 1 endet zwischen Welle 10 und 15.
2. Jeder Run trägt mindestens ein Workshop-Level ein.
3. Die Run-Tiefe wächst über die ersten zehn Runs monoton, ohne dass ein Run mehr als etwa das
Doppelte des vorigen erreicht.
---
## Was im Slice ist
### 1. Arena, Turm und Kamera
2D, `bevy_sprite`, orthographische Kamera. Ein `Tower` fest in der Mitte, Programmer-Art. Kein
Scrollen, kein Zoom, keine Turm-Platzierung.
**Akzeptanz**
- Nach Programmstart und Run-Start ist der Turm sichtbar in der Bildmitte des Gameplay-Fensters, und
die gesamte Spawn-Peripherie liegt innerhalb des sichtbaren Bereichs.
- Die Turm-Entity wird beim Run-Start gespawnt und beim Run-Ende despawnt (ADR-0002 §6); zwischen
zwei Runs existiert keine Entity mit `EffectiveStats`.
### 2. Wellen-Takt
Eine `Wave` besteht aus zwei separat getakteten Phasen. Die nächste Welle beginnt, wenn **beide**
Timer abgelaufen sind — nicht wenn alle Gegner tot sind. Eine einzige endlose Kurve, keine Tiers.
**Akzeptanz**
- Während der Spawn-Phase erscheinen Gegner; nach ‹26 s› hören Spawns auf, während bereits
vorhandene Gegner weiter laufen und angreifen; nach weiteren ‹9 s› beginnt Welle N+1.
- Wird Welle N+1 gestartet, während noch Gegner aus Welle N leben, laufen diese unverändert weiter
und werden nicht entfernt.
- Der aktuelle Wellenzähler ist im HUD ablesbar und stimmt mit dem Telemetrie-Log überein.
- Auf jeder Welle mit `wave % 10 == 0` spawnt genau ein Boss.
### 3. Gegner und Population
Drei bis vier Normal-Archetypen plus ein Boss (#3): ein Tempo/HP-Tradeoff-Paar, ein Fernkämpfer und
**eine** Sondermechanik, die Ziel-Priorisierung erzwingt (Splitter oder Schutz-Aura). Der Boss ist
ein einzelner schwerer, langsamer Meilenstein-Gegner. Keine Elite-Klasse, keine Fleet-Klasse, keine
Immunitätsmatrix.
Der Spawner ist **cap-getrieben, nicht plan-getrieben**: er füllt bis zur `target_population(wave)`
auf, statt N Gegner nach Zeitplan zu setzen (Cap-Analyse, Issue #1). Gegner laufen radial von außen
auf den Turm zu und greifen ihn im Nahbereich an.
**Akzeptanz**
- Bei laufender Spawn-Phase pendelt die Zahl lebender Gegner um `target_population(wave)`, mit einer
Abweichung von höchstens ‹±10 %› über ein 5-Sekunden-Fenster.
- Die Zahl lebender Gegner überschreitet **nie** `HARD_ENEMY_CAP` — auch nicht bei einem
Sprung-zu-Welle auf die höchste im Debug-Panel erreichbare Welle.
- Bei 10 000 gleichzeitigen Gegnern bleibt die Frametime unter 16,67 ms (Nachmessung mit dem Aufbau
aus `examples/perf_spike.rs`, nach dem Wechsel auf echte Sprites zu wiederholen).
- Jeder Archetyp ist an Sprite-Farbe oder -Form ohne Text unterscheidbar.
### 4. Turm-Dauerfeuer und geteilter Reichweiten-Scan
Der Turm schießt selbstständig auf Gegner in Reichweite. Genau **ein** System ermittelt pro Frame
den Reichweiten-Zustand; `tower_fire` und **alle** Auslöse-Bedingungen (Feature 7) lesen dessen
Ergebnis. Das ist keine Optimierung für später, sondern die Bedingung, unter der die
Auslöse-Entscheidung aus #12 tragfähig ist.
**Akzeptanz**
- Bei mindestens einem Gegner in Reichweite feuert der Turm ohne jede Eingabe innerhalb von
`EffectiveStats.AttackInterval` Sekunden.
- Ohne Gegner in Reichweite feuert er nicht.
- Es existiert genau ein System, das die Gegnermenge in Turm-Reichweite bestimmt; ein `grep` nach
einem zweiten Reichweiten-Scan über die Gegner-Query findet keinen.
### 5. Stat- und Modifier-Pipeline
Vollständig nach [ADR-0002](adr/0002-stat-und-modifier-pipeline.md): drei Akkumulatoren (`flat`,
`points`, `mult`), ein `Direct`/`Interval`-Bit je Stat, Clamp einmal auf die Summe, `EffectiveStats`
als `Component` an der Turm-Entity, Neuberechnung bei Änderung. `type Stat = f32`. Alle Zahlen aus
`Res<Balance>` (ADR-0001).
**Akzeptanz**
- Das durchgerechnete Beispiel aus ADR-0002 („Durchgerechnetes Beispiel") ist als Test hinterlegt
und grün — mit den dort genannten Platzhalterwerten, nicht mit den späteren echten.
- Nach einer Änderung an `Workshop`, `EquippedCards` oder `RunUpgrades` läuft die Neuberechnung
genau einmal; ohne Änderung läuft sie nicht (nachweisbar über einen Zähler im Debug-Panel).
- `sync_tower_abilities` ist der einzige Schreiber von Fähigkeits-Components; sein `debug_assert`
am Ende des Abgleichs ist vorhanden und greift im Debug-Build.
- Jeder Modifikator, der in den Slice aufgenommen wird, hat die Sichtbarkeitsanforderung aus
ADR-0002 §11 vor dem Merge durchlaufen — inklusive Punkt 3 (beobachtbare Folge in der
Run-Telemetrie).
### 6. Coins und In-Run-Upgrades
`Coins` fallen bei jedem Kill an und werden **beim Tod gutgeschrieben** — kein einsammelbares Objekt,
keine Entity (Cap-Analyse, Issue #1: bei tausenden Kills pro Welle wäre das ein Entity-Sturm). Damit
kauft der Spieler In-Run-Upgrades in drei Tabs (Attack / Defence / Utility), jederzeit, einzeln
(Buy x1). Alle verfallen am Run-Ende, weil sie als `RunUpgrades`-Component an der Turm-Entity hängen
(ADR-0002 §6).
**Akzeptanz**
- Ein Kauf ist zu jedem Zeitpunkt eines laufenden Runs möglich, auch mitten in der Spawn-Phase, ohne
dass die Simulation anhält.
- Ein Klick auf „Buy x1" zieht genau die angezeigten Kosten ab, erhöht das Upgrade um eine Stufe,
und der betroffene Wert in `EffectiveStats` ändert sich im selben Frame ablesbar.
- Ist der Coin-Stand kleiner als die Kosten, ist die Zeile nicht klickbar und als solche erkennbar.
- Nach einem Run-Ende und Run-Start stehen alle In-Run-Upgrades wieder auf Stufe 0, ohne dass
irgendein System sie zurücksetzt.
- Kosten und Wirkung unterscheiden sich zwischen den drei Tabs merklich genug, dass die
Telemetrie über zehn Runs **nicht** dieselbe Kaufreihenfolge zeigt (#8: sonst ist die
Kaufreihenfolge eine Sortierung, keine Entscheidung).
### 7. Auslöse-System für Fähigkeiten
**Ein** System wertet die Auslöse-Bedingungen aller aktiven Fähigkeiten aus — der Ultimate und jeder
Fähigkeits-Card. Eine Bedingung ist **Cooldown UND ein Weltzustand** (z. B. „‹N› Gegner in
Reichweite"), je Fähigkeit fest verdrahtet und nicht vom Spieler einstellbar. Die Werte kommen wie
alle Zahlen aus `Balance`. Feuern mehrere im selben Frame, feuern alle — keine Sperre, keine
Priorität.
**Akzeptanz**
- Eine Fähigkeit feuert erst, wenn beide Teile der Bedingung erfüllt sind; ein abgelaufener Cooldown
allein löst nachweislich nicht aus (prüfbar, indem man die Arena leerräumt und wartet).
- Sind zwei Fähigkeiten im selben Frame auslösebereit, feuern beide im selben Frame.
- Die Bedingungswerte stehen in `Balance` und sind über das Debug-Panel zur Laufzeit änderbar; keine
Schwelle steht als Literal im Gameplay-Code (ADR-0001 §4).
- Das System liest den Reichweiten-Zustand aus Feature 4 und führt keinen eigenen Scan aus.
### 8. Ultimate: Death Wave
Ein radialer Sweep von der Turmmitte nach außen, automatisch ausgelöst über Feature 7. Kein
Zielsystem, kein Knopf, kein Eingabepfad. Die Systemanforderung, die sie aufdeckt, ist der
**gebatchte Massentod**: ein Sweep kann in einem Tick tausende Gegner treffen.
**Akzeptanz**
- Bei erfüllter Bedingung startet der Sweep ohne Eingabe; ein sichtbarer Ring wandert von der
Turmmitte nach außen und trifft Gegner in der Reihenfolge ihres Abstands.
- Ein Sweep, der ‹5 000› Gegner gleichzeitig tötet, erzeugt keinen Frame über 16,67 ms — der
Massentod wird gebatcht, nicht als tausende Einzelereignisse abgearbeitet.
- Der verbleibende Cooldown ist im Ultimates-Tab ablesbar.
- Flächenwirkung ist über ein **Ziel-Limit pro Auslösung** gedeckelt, nicht über einen schrumpfenden
Radius (Cap-Analyse, Issue #1).
### 9. Cards, Slots und Loadout
Cards fallen als Drop aus Fortschritt: **Boss-Kill** oder **neuer Wellen-Rekord**. Sie sind nicht
kaufbar und haben keine eigene Währung. Eine Card ist entweder ein Stat-Modifikator oder eine
**Fähigkeit** (`CardEffect::Modifier` / `CardEffect::Ability`, ADR-0002 §8) — Fähigkeits-Cards feuern
über dasselbe System wie die Ultimate. Drei Seltenheiten, Slots von 1 auf 5.
Das **Loadout steht vor dem Run fest**. Im laufenden Run sind Cards über den Pause-Button nur
einsehbar, nicht wechselbar (Issue #1). Damit wird im laufenden Run nie eine Fähigkeits-Component
entfernt (ADR-0002 §10, Nachtrag zu #6).
**Akzeptanz**
- Nach einem Boss-Kill oder einem neuen Wellen-Rekord ist die Card-Ausbeute des Runs auf dem
Meta-Screen sichtbar, spätestens beim ersten Betreten nach dem Run-Ende.
- Der Spieler kann vor dem Run so viele Cards ausrüsten, wie er Slots hat, und keine mehr.
- Eine ausgerüstete Fähigkeits-Card erzeugt beim Run-Start genau eine Fähigkeits-Component am Turm;
eine abgelegte erzeugt keine.
- Während eines Runs ändert sich `EquippedCards` nicht — kein Pfad in der UI erlaubt es.
- Der Card-Pool enthält mindestens zwei Epics, und Slot 2 ist innerhalb der ersten ‹drei› Runs
erreichbar (#11; beide Bedingungen sind an das Abnahmekriterium des Slice gebunden).
### 10. Meta-Währung und Workshop
Die zweite Währung fällt aus denselben Kill-Events wie `Coins`, ist aber nicht in sie konvertierbar.
Drop-Chance je Normalgegner, garantiert je Boss, Menge wellenabhängig. Gutschrift beim Tod des
Gegners, kein Pickup. Der `Workshop` kauft davon permanente Stat-Upgrades mit exponentiell
wachsenden Kosten.
**Akzeptanz**
- Eine Ausgabe von `Coins` im Run verringert den Meta-Währungs-Stand nicht, und umgekehrt.
- Ein Boss-Kill schreibt garantiert Meta-Währung gut; die gutgeschriebene Menge auf Welle 20 ist
höher als auf Welle 10.
- Ein Workshop-Kauf erhöht den `flat`- bzw. `mult`-Beitrag des betroffenen Stats sofort und
überlebt ein Run-Ende sowie einen Programm-Neustart.
- Über die ersten zehn Runs trägt jeder Run mindestens ein Workshop-Level ein (#8, Prüfkriterium 2).
### 11. Run-Ende und Meta-Übergang
Fällt die Turm-HP auf 0, endet der Run. **Genau ein System** überführt am Run-Ende die aus dem Run
abgeleiteten Meta-Werte (`highest_wave`, verdiente Meta-Währung, Card-Ausbeute) in die persistierten
Resources (ADR-0003 §6). Danach wird die Turm-Entity despawnt; alles Run-gebundene verschwindet mit
ihr, ohne Aufräum-Code.
**Akzeptanz**
- Bei HP ≤ 0 endet der Run innerhalb eines Frames; danach existiert keine Entity mit
`EffectiveStats`, `RunUpgrades` oder einer Fähigkeits-Component mehr.
- `highest_wave` wächst nur, wenn die erreichte Welle den bisherigen Stand übersteigt, und wird
genau einmal pro Run geschrieben.
- Kein anderes System als dieses eine schreibt aus Run-Zustand in eine persistierte `Resource`
(prüfbar über die Schreibstellen der `SettingsGroup`-Resources).
- Der Spieler landet nach dem Run-Ende auf dem Meta-Screen, ohne Zwischenschritt und ohne dass ein
weiterer Run automatisch startet.
### 12. In-Run-HUD und Pause
Links das Gameplay-Fenster, rechts ein vertikales Panel über die volle Höhe. Das Panel trägt oben
den Namen des aktiven Tabs, darunter ~8 Zeilen im Schema `<Stat Name>` / `<Stat Value>` links und
`Buy x1` / `<Cost>` rechts. Unten vier Tabs: **Attack Shop · Defence Shop · Utility Shop ·
Ultimates**. Der Ultimates-Tab trägt die Cooldown-Anzeige (#12). Ein Pause-Button friert die
**gesamte** Simulation ein und gibt Einsicht in die ausgerüsteten Cards.
Das Mockup (`docs/design/hud-in-run.png`) legt Panel und Tabs fest, zeigt aber weder Turm-HP,
Wellenzähler, Währungsstände noch den Pause-Button. Diese vier gehören ins Gameplay-Fenster oder
dessen Rand; ihre Platzierung ist frei, ihre Existenz nicht.
**Akzeptanz**
- Die vier Tabs sind vorhanden und schaltbar; ein Tab-Wechsel hält die Simulation nicht an.
- Turm-HP, aktuelle Welle, `Coins`-Stand und Meta-Währungs-Stand sind während des Runs jederzeit
ohne Tab-Wechsel ablesbar.
- Der Pause-Button friert Gegnerbewegung, Turmfeuer, Wellen-Timer und Fähigkeits-Cooldowns
gleichzeitig ein; nach dem Fortsetzen laufen alle vier von genau ihrem Stand weiter.
- Im Pause-Zustand sind die ausgerüsteten Cards einsehbar und **nicht** wechselbar.
- Es gibt **keine** „Gespeichert"-Bestätigung — das Programm kann sie nicht belegen (ADR-0003 §8).
### 13. Meta-Screen zwischen den Runs
Der Zustand zwischen zwei Runs. Er trägt Workshop-Käufe, das Card-Loadout, den Slot-Fortschritt und
den Start-Knopf für den nächsten Run. **Sein Aufbau ist offen** (siehe „Offen"); was hier steht, ist
seine Funktion, nicht sein Layout.
**Akzeptanz**
- Ein neuer Run ist nur von hier aus startbar.
- Workshop-Käufe, Card-Loadout und Slot-Stand sind von hier aus erreichbar und änderbar.
- Beim Betreten nach einem Run zeigt der Screen, was dieser Run eingebracht hat: erreichte Welle,
verdiente Meta-Währung, gedroppte Cards.
- Steht `version` nach dem Laden auf `0`, erscheint hier die Meldung aus ADR-0003 §8 — und bis zur
ersten bewussten Aktion des Spielers wird nicht geschrieben.
### 14. Save/Load
Vollständig nach [ADR-0003](adr/0003-save-format-ort-und-migration.md): `progress.toml` über
`bevy_settings`, `version` als Sentinel, Feldnamen als Vertrag, namensbasierte Ablage. Persistiert
werden nur Quellen, nie berechnete Werte (ADR-0002 §5). Kein laufender Run, kein `Balance`, kein
Zeitstempel.
**Akzeptanz**
- Nach einem Programmneustart stehen Workshop-Level, Card-Bestand, Loadout, Slot-Zahl,
Meta-Währungsstand und `highest_wave` unverändert da.
- Ein von Hand beschädigtes `progress.toml` führt zur Meldung aus ADR-0003 §8 und **nicht** zu einem
Schreibvorgang, bevor der Spieler etwas tut. Die beschädigte Datei liegt danach unverändert da.
- Ein `progress.toml` mit `version` größer als `CURRENT` sperrt alle Schreibvorgänge für die Sitzung
und lässt die Datei unangetastet.
- Ein Umbenennen eines Feldes lässt den restlichen Spielstand intakt und setzt genau einen Wert auf
`Default`.
- Die Datei ist in einem Texteditor lesbar und von Hand editierbar — Schummeln ist ausdrücklich
akzeptiert, nicht übersehen.
### 15. Debug-Panel und Run-Telemetrie
**Kein Nice-to-have.** ADR-0001 und #8 benennen dieses Werkzeug als den eigentlichen Hebel, um die
vier freien Zahlen überhaupt finden zu können: ohne Sprung-zu-Welle kostet jeder Datenpunkt einen
Run von Welle 1 an, bei ‹35 s› pro Welle also Minuten. Das Panel mutiert `Balance` im laufenden
Prozess und nutzt dieselbe Naht wie der spätere Asset-Ladepfad (ADR-0001 §3).
Umfang: Sprung zu einer beliebigen Welle · Live-Mutation der `Balance`-Werte · Anzeige von
`EffectiveStats` samt der **ungeclampten** Summen (ADR-0002 §2) · Herkunfts-Aufschlüsselung pro Stat
(ADR-0002 §6) · Run-Telemetrie: Zeit bis Tod, erreichte Welle, Coins pro Minute, Kills pro Welle,
DPS-Verlauf, Zeit zwischen zwei bezahlbaren Käufen.
**Akzeptanz**
- Ein Sprung zu Welle N versetzt den laufenden Run in Welle N mit der für N vorgesehenen
Soll-Population und Gegner-Skalierung, ohne Programmneustart, in unter ‹2 s›.
- Eine Änderung an einem `Balance`-Wert im Panel wirkt ohne Neustart auf die nächste Neuberechnung
von `EffectiveStats`.
- Am Run-Ende liegt ein Telemetrie-Datensatz vor, der alle oben genannten Größen enthält und sich
maschinenlesbar exportieren lässt.
- **Ein Run, in dem das Debug-Panel benutzt wurde, schreibt nichts in `progress.toml`.** Weder
`highest_wave` noch Meta-Währung noch Card-Drops. Ein Sprung zu Welle 500 darf keinen
Wellen-Rekord und keinen Card-Drop erzeugen.
- Das Panel ist im Release-Build nicht erreichbar, ohne dass ein Schalter dafür umgelegt wird.
---
## Was ausdrücklich nicht drin ist
| Nicht im Slice | Warum |
|---|---|
| Pfadfindung, Turm-Platzierung, Maze-Building | die Arena ist radial und hat genau einen Turm — es gibt nichts zu platzieren und keinen Pfad zu finden. |
| Tiers | wir haben uns bewusst für **eine** endlose Kurve entschieden; Tier-Resets sind das Langzeit-Muster des Originals, nicht unser Slice-Problem. |
| Labs, Modules, Perks | dritte, vierte und fünfte Meta-Achse neben Workshop und Cards — der Slice soll zeigen, dass **eine** Progressionsschleife trägt, nicht dass fünf sich addieren. |
| Offline-Progress | braucht einen Zeitstempel im Save, und ein Feld ohne Leser ist genau der Fehler, den ADR-0002 §5 verbietet. |
| Elite- und Fleet-Klasse | ihre Mechanik im Original ist eine Anti-Cheese-Antwort auf ein Fähigkeiten-Arsenal, das wir gar nicht haben (#3). |
| Immunitätsmatrix | dieselbe Begründung — sie schützt vor Fähigkeiten, die der Slice nicht kennt. |
| Wall als zweiter HP-Pool | ein zweiter HP-Pool verdoppelt die Schadenslogik, ohne eine neue Frage zu beantworten (#3). |
| Coin- und Card-Pickups als Entities | bei tausenden Kills pro Welle ist ein einsammelbares Objekt pro Drop ein Entity-Sturm (Cap-Analyse #1). |
| Coin Decay, Heat-up, Enemy-Mass-Wachstum | Coin Decay und Mass sind dichte-neutral, aber ohne Nutzen im Slice; Heat-up ist bei 13-facher Angreiferzahl schlicht nicht übertragbar und müsste neu erfunden statt übernommen werden (Cap-Analyse #1). |
| Gacha-Kopien, Card-Mastery, Pity-System | das Original kauft Cards mit einer Währung, die es bei uns nicht gibt (Ads, IAP) — Drop-Verteilung ersetzt beides ersatzlos (#11). |
| Manuelle Ultimate-Auslösung | ein Knopf wäre der einzige Moment aktiver Kontrolle im ganzen Spiel und würde das Kern-Gefühl „Idle" brechen statt bestätigen (#9). |
| Einstellbare Auslöse-Bedingungen | die Bedingungen liegen bereits als Daten in `Balance`; sie einstellbar zu machen ist reine UI-Arbeit und kauft im Slice nichts (#12). |
| Buy x10 / Buy Max | das HUD-Schema lässt Platz dafür, ohne das Layout zu ändern — der Slice braucht es nicht (#10). |
| Spielgeschwindigkeits-Multiplikator | nicht entschieden und nicht im HUD; das Debug-Panel deckt den Zeitraffer-Bedarf beim Tunen ab (siehe „Lücken"). |
| Bots, Wave Skip, Recovery Packages | Systeme, die eine bereits laufende Schleife garnieren — der Slice muss erst zeigen, dass die Schleife läuft. |
| Guilds, Tournaments, Cloud-Saves, Accounts, IAP, Leaderboards | alles Server-seitige ist out of scope; ein Vergleichswert, der das Gerät verlässt, würde außerdem ADR-0003 §1 (Klartext-Save) neu aufmachen. |
| Mobile- und Web-Port | `bevy_settings` persistiert auf Android und iOS **nichts** und meldet es nur als `warn!` — der Port braucht eigene Arbeit (ADR-0003, Revisions-Auslöser 2). |
| Art-Direction, Audio | Programmer-Art ist eine Entscheidung, kein Zwischenstand; ein Art-Strang beantwortet keine der offenen Fragen. |
| Hot-Reload für Balancing-Daten | der Edit-to-see-Loop kostet gemessene ~11 s, und der teure Teil eines Tuning-Zyklus ist das Spielen (ADR-0001). |
| „Gespeichert"-Bestätigung | das Programm kann sie nicht belegen — es gibt in Bevy 0.19 keinen Rückkanal (ADR-0003 §8). |
---
## Abnahmekriterium für den Slice
Genau eines, überprüfbar, ohne Debug-Panel:
> **In einer ununterbrochenen Sitzung von höchstens 60 Minuten auf einem frisch angelegten
> Spielstand spielt ein Spieler Runs, bis er einen zweiten Card-Slot besitzt und zwischen
> mindestens zwei Cards wählen kann, startet danach einen weiteren Run und erreicht darin eine
> höhere Welle als in Run 1; nach einem Neustart des Programms steht derselbe Meta-Stand wieder da.**
Warum genau dieses: Es prüft alle vier Takte des Loops in einer Beobachtung — Run starten, sterben,
aufwerten, stärker starten — und bindet zusätzlich die Randbedingung aus #11 ein, dass der Slice das
Card-System nicht nur enthalten, sondern **zeigen** muss. Ein Slice, in dem Slot 2 ein Fernziel
bleibt, hat Cards gebaut und nie vorgeführt. Der Neustart am Ende prüft Feature 14 dort, wo es zählt.
---
## Offen — bewusst nicht entschieden
Für keinen dieser Punkte wird hier eine Antwort erfunden.
| Punkt | Wer entscheidet | Wann |
|---|---|---|
| **Die vier freien Zahlen**: Wachstumsfaktor pro Welle, Boss-Multiplikator, Drop-Chance mal Wellenwert, Kosten-Exponent des Workshops | Der Entwickler beim Tunen. Form und Prüfkriterien stehen in #8, die Werte findet nur Spielen | Nach Feature 15 (Debug-Panel), vor dem Abnahmekriterium |
| **Name der Meta-Währung** | `CONTEXT.md` — der Glossar-Eintrag ist der Vertrag, der Save-Feldname folgt ihm | Bevor der erste Workshop-Preis geschrieben wird. `Coins` ist an die In-Run-Währung vergeben; ADR-0003 lässt das Feld unbenannt, weil ein vorläufiger Name nach der Migrationsregel (§4) teuer zu korrigieren wäre |
| **Aufbau des Meta-Screens** (Workshop, Cards, Slot-Fortschritt) | Ein eigenes Design-Ticket, analog zum HUD-Mockup | Vor Feature 13. Nur das In-Run-HUD ist entworfen |
| **`MaxHealth` mitten im Run erhöht** — wächst die aktuelle HP mit, behält sie ihren Anteil, oder bleibt sie unverändert? | Das Health-System, nicht die Pipeline (ADR-0002, „Offene Punkte") | **Vor dem ersten Defence-Shop-Upgrade.** Die ADR-0002-Stat-Tabelle weist `MaxHealth` ausdrücklich `flat` aus In-Run-Upgrades zu — der Fall tritt bei jedem Health-Kauf ein, nicht theoretisch |
| **`SlowAura`** — modifiziert Gegner statt den Turm und ist deshalb kein Stat | Fällt mit der Card-Liste; ADR-0002 §9 legt die Form fest (Component am Turm, das das Gegner-Bewegungssystem liest), nicht den Inhalt | Mit der Card-Liste |
| **Zusammensetzung der Card-Liste**: Anzahl, Wirkungen, Verteilung auf die drei Seltenheiten, Drop-Wahrscheinlichkeiten je Stufe, Stufenzahl je Card | Ein Card-Inhalts-Ticket, gebunden an #8 (Zahlen) und #11 (Randbedingungen) | Vor Feature 9. **Zwei Randbedingungen sind bereits verbindlich**: Slot 2 muss früh fallen, und es braucht mindestens zwei Epics — sonst ist die Epic-Stufe eine Behauptung statt einer Mechanik |
| **Slot-Kurve**: wodurch ein Card-Slot freigeschaltet wird | Niemand — siehe „Lücken", Punkt 2 | offen |
---
## Widersprüche und Lücken
Beim Zusammentragen aller Entscheidungen sind Stellen aufgefallen, an denen zwei Quellen sich
widersprechen oder an denen eine Frage niemandem gehört. Sie stehen hier, statt geglättet zu werden.
### Widersprüche
1. **Card-Drops überqueren die Run-Grenze, aber ADR-0003 kennt sie nicht.** ADR-0003 §6 nennt
ausdrücklich **genau eine** Ausnahme von der Regel „während eines Runs ändert sich keine
persistierte Quelle": `highest_wave` und die verdiente Meta-Währung, überführt genau einmal am
Run-Ende. `card_levels` steht nicht in dieser Ausnahme — obwohl #11 (entschieden 12:48, also vor
ADR-0003 um 13:46) Cards aus **Boss-Kills und neuen Wellen-Rekorden** droppen lässt, beides
Ereignisse *im* Run. Die Spec löst das oben nach ADR-0003 auf: Card-Drops werden im Run gepuffert
und zusammen mit den übrigen Meta-Werten am Run-Ende überführt. Wer das anders bauen will, muss
zuerst die Ausnahmeliste in ADR-0003 §6 erweitern — nicht stillschweigend ein zweites System
schreiben lassen.
2. **Die Slot-Kurve gehört niemandem, ist aber Abnahmebedingung.** #11 entscheidet „Start bei 1,
wächst auf 5, die Slot-Zahl ist selbst Progression" und bindet Slot 2 ausdrücklich ans
Abnahmekriterium des Slice. **Wodurch** ein Slot freigeschaltet wird, sagt niemand: nicht #11
(schiebt es an #8/#10), nicht #8 (die vier freien Zahlen enthalten keine Slot-Kurve), nicht die
Karte (der Fog nennt nur den Meta-Screen-Aufbau). `card_slots: u8` ist in ADR-0003 persistiert,
aber es gibt keinen Mechanismus, der ihn erhöht. Das ist keine Zahl, die man beim Tunen findet —
es ist eine unentschiedene Mechanik: Kauf aus Meta-Währung? Wellen-Meilenstein? Boss-Zahl? Sie
blockiert das Abnahmekriterium und muss vor Feature 9 entschieden werden.
3. **Die Ticket-Überschrift von #8 verspricht 10–20 Minuten, die Entscheidung liefert 6–9.** #8
heißt „Wellen- und Economy-Kurve für einen **10–20-Minuten-Run**", sein erster Kommentar
verlangt Tod „um Welle 20–40", und die Entscheidung im zweiten Kommentar setzt Run 1 auf **Welle
10–15 = 6–9 Minuten**. Die Karte hat die letzte Fassung übernommen, die frühere steht unwidersprochen
daneben. Wahrscheinlich gemeint: 10–20 Minuten gilt für den **eingespielten** Run nach einigen
Workshop-Leveln, 6–9 Minuten für Run 1. Das steht so aber nirgends, und die Kurve wird gegen die
Zahl getunt, die man liest. Diese Spec folgt der Entscheidung (Welle 10–15) und hält fest, dass
für Run *n* keine Ziel-Runlänge existiert.
4. **Die Cap-Analyse balanciert Sub-Caps für Gegnerklassen, die #3 abgeschafft hat.** Der
#1-Kommentar teilt die Sub-Caps sorgfältig in Modifikator-Caps (Protector 8, **Elite pro Typ 8**)
und Event-Caps (Boss 10, **Elite gesamt 20**). #3 wirft Elite und Fleet aber komplett aus dem
Slice. Damit ist die Hälfte der Analyse gegenstandslos: übrig bleiben genau **ein** mitskalierendes
Modifikator-Cap (die eine Sondermechanik-Einheit, Protector-artig) und **ein** absolutes
Event-Cap (Boss). Die Regel bleibt richtig, ihr Anwendungsbereich ist ein Viertel so groß wie
beschrieben — wer sie liest, baut sonst eine Klassifizierung für Klassen, die es nicht gibt.
5. **~24 Shop-Zeilen stehen gegen 12 Stats.** Der HUD-Kommentar in #10 leitet aus 8 Zeilen × 3 Tabs
„etwa 24 In-Run-Upgrades" ab und macht daraus eine Design-Aussage („der Spieler kann in einem Run
nicht alles kaufen"). ADR-0002 §3 kennt aber insgesamt **12** Stats, und davon gehören
`UltimateCooldown` und `WaveCooldown` nicht in die drei Shop-Tabs. Die Aufteilung ergibt grob
Attack 5 (`Damage`, `AttackInterval`, `Range`, `CritChance`, `CritFactor`), Defence 4
(`MaxHealth`, `DefensePercent`, `DefenseFlat`, `HealthRegen`) und **Utility 1** (`CoinGain`). Ein
Utility-Tab mit einer Zeile ist kein Tab. Entweder das Panel füllt nur die Hälfte der Zeilen —
dann trägt die Folgerung „kann nicht alles kaufen" nicht mehr —, oder es braucht In-Run-Upgrades,
die keine Stats aus ADR-0002 sind, und dann ist ADR-0002 §3 unvollständig. Beides ist eine
Entscheidung, keine Detailfrage.
Verschärfend: #3 empfiehlt für Utility ausdrücklich „stark vereinfachen", weil das Utility-System
des Originals an Labs und Perks hängt — beides im Nebel. Der Utility-Tab ist damit der schwächste
Teil der HUD-Struktur.
6. **Die „12–17 Kaufmomente" sind zweimal überholt, stehen aber noch in der Kette.** Der
HUD-Kommentar (13:38) rechnet mit 12–17 Kaufmomenten aus einer 20-Minuten-Session, #8 (13:51)
verwirft die Rechnung ausdrücklich („die trägt nicht mehr", weil jederzeit gekauft wird) **und**
verkürzt Run 1 auf 10–15 Wellen. Was aus dem HUD-Kommentar überlebt, ist allein die Zahl 24 —
sie kommt aus dem Layout, nicht aus der Session-Rechnung. Die Tuning-Zielgröße ist die **Zeit
zwischen zwei bezahlbaren Käufen** (‹10–20 s› früh), nicht eine Kaufzahl pro Run.
### Lücken, die niemandem gehören
7. **Das Debug-Panel kann den echten Spielstand zerstören, und niemand hat es benannt.** Feature 15
springt zu Welle N. Feature 11 schreibt am Run-Ende `highest_wave` und die Meta-Währung in die
persistierte Resource, und #11 macht „neuer Wellen-Rekord" zu einer **Card-Drop-Quelle**. Ein
Sprung zu Welle 500 beendet einen Run also mit `highest_wave = 500`, einem gewaltigen Card-Drop
und einem Meta-Währungsstand, der den ganzen Slice trivialisiert. Weder ADR-0001 noch #8 noch
ADR-0003 erwähnen das. Diese Spec zieht die Konsequenz oben als Akzeptanzkriterium ein: ein Run
mit benutztem Debug-Panel schreibt **nichts**. Wer das Panel baut, muss die Schreibsperre
zuerst bauen.
8. **Die Card-Nachschubquelle „neuer Wellen-Rekord" versiegt eingebaut.** Beide Card-Quellen hängen
an Fortschritt: Boss-Kill und neuer Wellen-Rekord. Solange die Run-Tiefe monoton wächst (#8,
Prüfkriterium 3, gilt für die **ersten zehn** Runs), fließt Nachschub. Danach ist unbestimmt, was
passiert — ein Spieler, der zehn Runs lang auf derselben Welle hängen bleibt, bekommt keine neue
Card mehr und hat gleichzeitig keinen anderen Hebel, tiefer zu kommen als den Workshop. Ob das
ein Feature ist (Cards folgen dem Fortschritt) oder eine Sackgasse (Fortschritt braucht Cards),
hat niemand entschieden. Bei einem Run 1, der auf Welle 10–15 endet, ist außerdem der
**eine** Boss-Kill pro Run in der Frühphase der einzige garantierte Drop überhaupt — er trägt
Meta-Währung und Card-Chance gleichzeitig.
9. **Gegner-gegen-Gegner-Kollision ist seit #4 offen und steht auf keiner Liste.** #4 und die
Cap-Analyse nennen sie als „offene Architekturfrage": Stapeln zulassen oder Spatial Grid mit
weicher Separation. Bei tausenden radial konvergierenden Körpern kommen O(n²) und ein Stau am
Turmring zusammen, den keine DPS-Menge auflöst. Sie steht **nicht** im Fog der Karte, in keiner
ADR und in keinem der offenen Punkte, die diese Spec zugewiesen bekommen hat. Sie entscheidet
aber, ob 10 000 Gegner überhaupt spielbar sind, und sie ist teuer nachzurüsten.
*Vorschlag zur Auflösung, nicht Entscheidung:* Stapeln zulassen — bei Programmer-Art in 2D
vertretbar, und der Spatial Grid ist der teure Weg, der jederzeit nachrüstbar bleibt.
10. **Ziel-Limit für Flächeneffekte und gebatchter Massentod haben keine ADR.** Die Cap-Analyse
ordnet beides ausdrücklich „der Architektur" zu, aber ADR-0002 regelt Stats und
Fähigkeits-Components, nicht Massenwirkung. Death Wave (#9) trifft beide Punkte sofort. Diese
Spec hat sie deshalb als Akzeptanzkriterien in Feature 8 aufgenommen — das ersetzt keine ADR,
macht sie aber wenigstens prüfbar.
11. **Ein Spielgeschwindigkeits-Multiplikator ist weder entschieden noch verworfen.** Die
Cap-Analyse wirft ihn auf („ein Idle-Spiel mit 35-Sekunden-Wellen will fast sicher einen") und
entschärft ihn nur perf-seitig; das Referenzspiel hat ihn; das HUD-Mockup zeigt ihn nicht.
Diese Spec listet ihn oben unter „nicht drin", weil das Debug-Panel den Zeitraffer-Bedarf beim
Tunen abdeckt — aber das ist eine Entscheidung, die hier zum ersten Mal getroffen wird und
jederzeit umkehrbar ist.
12. **`WaveCooldown` hat einen Cap, aber im Slice keine Quelle.** ADR-0002 §3 führt den Stat samt
`caps.wave_cooldown_reduction`; im Original liefert ihn die Epic-Card „Wave Accelerator". Enthält
die noch offene Card-Liste keine entsprechende Card, sind Stat und Cap toter Code. Kein Fehler,
aber ein Punkt, der bei der Card-Liste mitentschieden werden sollte.
13. **Die Fehlermeldung aus ADR-0003 hat keinen Ort.** ADR-0003 §3 und §8 verlangen bei `version == 0`
und bei `version > CURRENT` eine Meldung an den Spieler, bevor irgendetwas geschrieben wird. Der
Slice hat nur das In-Run-HUD (entworfen) und den Meta-Screen (offen). Diese Spec hängt die
Meldung an den Meta-Screen — womit der offene Meta-Screen-Entwurf eine Anforderung erbt, die
aus einer ganz anderen Ecke kommt.