docs: add ADR-0004 spatial grid, fix Fraction stat mode, extend spec

This commit is contained in:
2026-08-10 14:32:22 +02:00
parent a9486d745f
commit 68a587b948
5 changed files with 781 additions and 42 deletions

View File

@@ -6,8 +6,9 @@
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
[ADR-0003](adr/0003-save-format-ort-und-migration.md) (Save-Format),
[ADR-0004](adr/0004-spatial-grid-fuer-gegner-nachbarschaft.md) (Spatial Grid und weiche
Separation), `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.
@@ -71,10 +72,13 @@ Alles hier ist entschieden. Alles andere ist ‹Platzhalter› oder steht unter
| 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 |
| Slot-Quelle | **Kauf aus der Meta-Währung im Workshop**, exponentiell wachsende Kosten wie bei den Stat-Upgrades. Kein zweiter Freischalt-Mechanismus, keine Meilenstein-Schwelle | diese Spec (löst Widerspruch 2) |
| Utility-Tab | **Ökonomie-Upgrades, keine Kampf-Stats** — vier Zeilen, siehe Feature 6 | #3-Kalibrierung, `docs/WikiData.md`; diese Spec (löst Widerspruch 5) |
| Gegner-gegen-Gegner | **Spatial Grid mit weicher Separation**; dasselbe Grid trägt Zielauswahl, Reichweiten-Scan und Projektil-Kollision | ADR-0004 (löst Lücke 9) |
| 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 |
| Turm-Stats | 16, siehe Tabelle in ADR-0002 §3 samt Nachtrag vom 2026-08-10 | ADR-0002 |
**Prüfkriterium für die fertige Kurve** (#8, gilt für die vier freien Zahlen):
@@ -122,6 +126,14 @@ Der Spawner ist **cap-getrieben, nicht plan-getrieben**: er füllt bis zur `targ
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.
Gegner **stapeln nicht frei übereinander**: Sie drücken sich weich auseinander, nach
[ADR-0004](adr/0004-spatial-grid-fuer-gegner-nachbarschaft.md). Das ist keine Kollisionsauflösung —
Überlappung bleibt erlaubt, verboten ist nur, dass tausende Sprites am Turmring deckungsgleich
liegen und die Soll-Population unlesbar machen. Der Versatz ist auf einen Bruchteil des
Bewegungsschritts geklemmt und kann den radialen Anlauf deshalb nie umkehren. Die Nachbarschaft
kommt aus dem `SpatialGrid`, das einmal pro Frame neu gebaut wird und außerdem Feature 4, das
Auslöse-System und die Projektil-Kollision bedient.
**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.
@@ -129,6 +141,12 @@ auf den Turm zu und greifen ihn im Nahbereich an.
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).
- Der Rebuild des `SpatialGrid` kostet bei 10 000 Gegnern **nicht mehr als `move_enemies` im selben
Frame** (ADR-0004 §3 — die Entscheidung gegen ein inkrementelles Update steht auf dieser Annahme;
reißt sie, ist die ADR fällig, nicht die Zahl).
- Kein Gegner bewegt sich in einem Frame vom Turm **weg**, solange ihn keine Fähigkeit dorthin
drückt — die Separationsklemmung macht das konstruktiv unmöglich, und der Turmring pulsiert
sichtbar nicht.
- Jeder Archetyp ist an Sprite-Farbe oder -Form ohne Text unterscheidbar.
### 4. Turm-Dauerfeuer und geteilter Reichweiten-Scan
@@ -138,23 +156,37 @@ den Reichweiten-Zustand; `tower_fire` und **alle** Auslöse-Bedingungen (Feature
Ergebnis. Das ist keine Optimierung für später, sondern die Bedingung, unter der die
Auslöse-Entscheidung aus #12 tragfähig ist.
Der Reichweiten-Zustand und die Zielauswahl sind **Abfragen an das `SpatialGrid`**, kein linearer
Durchlauf über die Gegner (ADR-0004). #12 hat die siebenfache Ausführung desselben Scans beseitigt;
ADR-0004 beseitigt, dass der eine verbleibende Scan noch O(n) ist — die Zielauswahl läuft
zellenweise von der Turmmitte nach außen und bricht beim ersten Treffer ab. Damit gilt eine
schärfere Regel als bisher: **Genau ein System durchläuft die Gegnermenge, und das ist der
Grid-Rebuild.**
**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.
- Es existiert genau ein System, das die Gegnermenge in Turm-Reichweite bestimmt, und es fragt das
`SpatialGrid`; ein `grep` nach einem zweiten Reichweiten-Scan über die Gegner-Query findet keinen.
- **Außer `rebuild_spatial_grid` iteriert kein System die Gegner-Query, um Nachbarn zu finden** —
weder die Separation, noch `tower_fire`, noch das Auslöse-System, noch die Projektil-Kollision
(ADR-0004 §6).
### 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).
`points`, `mult`), ein Modus-Feld je Stat mit den drei Varianten `Direct` / `Interval` /
`Fraction`, Clamp einmal auf die Prozentpunkt-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.
und grün — mit den dort genannten Platzhalterwerten, nicht mit den späteren echten. Das schließt
den **Crit-Chance-Fall** ein: Drei Quellen mit zusammen 37 pp ergeben 37 %, nicht 0.
- Kein Stat im Modus `Direct` hat `base = 0` und `points` als einzige erwartete Quelle
(ADR-0002 §1, Prüfregel 3) — prüfbar an der Stat-Tabelle, ohne das Spiel zu starten.
- 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`
@@ -171,6 +203,28 @@ kauft der Spieler In-Run-Upgrades in drei Tabs (Attack / Defence / Utility), jed
(Buy x1). Alle verfallen am Run-Ende, weil sie als `RunUpgrades`-Component an der Turm-Entity hängen
(ADR-0002 §6).
**Der Utility-Tab trägt Ökonomie-Upgrades, keine Kampf-Stats.** Das ist keine Verlegenheitslösung,
sondern das, was Utility im Referenzspiel ist: ein Ökonomie-Multiplikator (Kalibrierungsbericht in
#3; `docs/WikiData.md`, Workshop-Abschnitt, listet dort Cash Bonus, Cash/Wave, Coins/Kill Bonus und
Free Upgrades). Vier Zeilen, jede mit ihrem Grund:
| Zeile | Stat | Warum sie da ist |
|---|---|---|
| Coin-Ertrag pro Kill | `CoinGain` | der Grundkanal — Einkommen skaliert linear mit Kills und damit mit der Soll-Population; ohne ihn hat der Tab keinen Anker |
| Coins am Wellenende | `CoinsPerWave` | der einzige Ertrag, der **nicht** mit der Gegnerdichte wächst. Die Cap-Analyse (#1) verlangt, die Economy gegen *Kills pro Welle* zu normieren statt gegen Wellen — ein wellengebundener Sockel ist der Gegenpol dazu und trägt früh, wenn die Population noch klein ist |
| Coin-Bonus auf Crit-Kills | `CritCoinGain` | koppelt Utility an Attack: Er ist wertlos, solange `CritChance` bei 0 steht, und wird stark, sobald der Attack-Tab bedient wurde. Genau das macht die Kaufreihenfolge zu einer Entscheidung statt zu einer Sortierung (#8, siehe Akzeptanz unten) |
| Chance auf ein kostenloses Upgrade | `FreeUpgradeChance` | der einzige Utility-Effekt, der in die anderen Tabs zurückwirkt: Er kauft nicht Coins, sondern **Zeit**, und wirkt damit direkt auf die Tuning-Zielgröße „Zeit zwischen zwei bezahlbaren Käufen". Eine Auswertung je Wellenende, ein kostenloses Level in einem zufällig gewählten Tab — das Original hat drei getrennte Stats dafür, #3 empfiehlt für Utility ausdrücklich „stark vereinfachen" |
Die vier Stats stehen in ADR-0002 §3 (Nachtrag). **`MetaGain` gehört ausdrücklich nicht in diesen
Tab**: Ein aus `Coins` gekaufter Multiplikator auf die Meta-Währung wäre ein Konvertierungspfad
zwischen den beiden Währungen, und den schließt Issue #1 aus. `MetaGain` ist ein Workshop-Upgrade
(Feature 10).
Damit stehen im Panel 5 Attack-, 4 Defence- und 4 Utility-Zeilen — **13 von den ~24 Zeilen, die das
Layout hergibt.** Die Folgerung des HUD-Kommentars „der Spieler kann in einem Run nicht alles
kaufen" trägt deshalb nicht mehr aus dem Layout; sie muss aus den Kosten kommen oder gar nicht
(siehe „Widersprüche", Punkt 5).
**Akzeptanz**
- Ein Kauf ist zu jedem Zeitpunkt eines laufenden Runs möglich, auch mitten in der Spawn-Phase, ohne
dass die Simulation anhält.
@@ -182,6 +236,10 @@ kauft der Spieler In-Run-Upgrades in drei Tabs (Attack / Defence / Utility), jed
- 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).
- Der Utility-Tab enthält **keinen** Kauf, der die Meta-Währung, ihre Drop-Chance oder ihre Menge
verändert — kein Pfad im Run wandelt `Coins` in Meta-Fortschritt.
- Jede der vier Utility-Zeilen hat die Sichtbarkeitsanforderung aus ADR-0002 §11 durchlaufen; für
`CritCoinGain` liegt die dort zu nennende Fortschritts-Stelle **hinter** dem ersten Crit-Kauf.
### 7. Auslöse-System für Fähigkeiten
@@ -197,7 +255,8 @@ Priorität.
- 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.
- Das System liest den Reichweiten-Zustand aus Feature 4 und führt keinen eigenen Scan aus — weder
über die Gegner-Query noch als eigene Abfrage am `SpatialGrid` (ADR-0004 §6).
### 8. Ultimate: Death Wave
@@ -221,6 +280,13 @@ kaufbar und haben keine eigene Währung. Eine Card ist entweder ein Stat-Modifik
**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.
**Slots werden im Workshop aus der Meta-Währung gekauft** (Feature 10) — sie stehen dort neben den
Stat-Upgrades, mit derselben exponentiellen Kostenform. Es gibt keinen zweiten Freischalt-Weg: keine
Wellen-Meilensteine, keine Boss-Zahl, keinen Drop. Cards selbst bleiben unverkäuflich und fallen
weiter als Drop; gekauft wird nur die **Breite** des Loadouts. Damit ist die eigentliche
Meta-Entscheidung des Slice ausgesprochen: Breite (ein Slot mehr) gegen Stärke (ein Stat-Level
mehr), aus demselben Topf.
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).
@@ -233,14 +299,28 @@ entfernt (ADR-0002 §10, Nachtrag zu #6).
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).
**bezahlbar** (#11; beide Bedingungen sind an das Abnahmekriterium des Slice gebunden).
- Es gibt genau einen Pfad, der `card_slots` erhöht, und der ist ein Workshop-Kauf; kein Boss-Kill,
kein Wellen-Rekord und kein Drop verändert die Slot-Zahl.
### 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.
wachsenden Kosten. Unter den Stats liegt auch `MetaGain` — der Multiplikator auf die gedroppte
Meta-Menge, der aus demselben Grund **nicht** im In-Run-Shop steht (Feature 6).
**Der Workshop verkauft außerdem Card-Slots.** Sie stehen als eigene Zeile neben den Stat-Upgrades,
in derselben Währung, mit derselben exponentiellen Kostenform — vier Käufe insgesamt, von Slot 1 auf
Slot 5. Der Spieler entscheidet damit selbst zwischen Breite und Stärke; das ist die eine
Meta-Entscheidung, die der Slice zu zeigen hat. Ein zusätzliches persistiertes Feld braucht es
nicht: `card_slots: u8` aus ADR-0003 §5 ist zugleich Zustand und Kaufstufe.
**Der erste Slot-Preis ist keine freie Zahl.** #11 bindet Slot 2 an das Abnahmekriterium — er muss
innerhalb der ersten paar Runs fallen. Die Kosten für Slot 2 sind damit an den Meta-Ertrag der
ersten ‹drei› Runs gebunden, nicht an die Kostenkurve der Stat-Upgrades; erst ab Slot 3 ist der
Exponent frei. Beide Werte bleiben ‹Platzhalter› und leben in `Balance`.
**Akzeptanz**
- Eine Ausgabe von `Coins` im Run verringert den Meta-Währungs-Stand nicht, und umgekehrt.
@@ -249,6 +329,9 @@ wachsenden Kosten.
- 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).
- Slot 2 ist im Workshop nach höchstens ‹drei› Runs bezahlbar, ohne dass der Spieler auf
Stat-Upgrades verzichtet hat — die Prüfung ist ein gespielter Durchlauf, keine Rechnung.
- Ein gekaufter Slot erhöht `card_slots` um genau 1 und überlebt einen Programm-Neustart.
### 11. Run-Ende und Meta-Übergang
@@ -270,8 +353,10 @@ ihr, ohne Aufräum-Code.
### 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 ·
den Namen des aktiven Tabs, darunter bis zu ~8 Zeilen im Schema `<Stat Name>` / `<Stat Value>` links
und `Buy x1` / `<Cost>` rechts. Belegt sind davon 5 (Attack), 4 (Defence) und 4 (Utility) — das
Panel füllt seine Zeilen also **nicht** aus, und das ist eine Folge der Stat-Liste, kein
Zwischenstand (Feature 6). 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.
@@ -294,9 +379,14 @@ Der Zustand zwischen zwei Runs. Er trägt Workshop-Käufe, das Card-Loadout, den
den Start-Knopf für den nächsten Run. **Sein Aufbau ist offen** (siehe „Offen"); was hier steht, ist
seine Funktion, nicht sein Layout.
Der Slot-Fortschritt ist dabei **kein eigener Bildschirmteil**: Slots werden im Workshop gekauft
(Feature 10) und sind dort eine Zeile neben den Stat-Upgrades. Der Meta-Screen zeigt sie im
Card-Bereich nur an, er verkauft sie nicht an zweiter Stelle.
**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.
- Der Slot-Kauf ist genau einmal erreichbar, nämlich im Workshop.
- 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
@@ -402,7 +492,8 @@ Für keinen dieser Punkte wird hier eine Antwort erfunden.
| **`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 |
| **Slot-Preise**: was ein Card-Slot kostet | Der Entwickler beim Tunen. Die **Mechanik** ist entschieden — Kauf aus der Meta-Währung im Workshop, siehe Feature 10 und „Widersprüche", Punkt 2. Offen ist nur noch der Preis, und der ist für Slot 2 **nicht frei**: er ist an den Meta-Ertrag der ersten ‹drei› Runs gebunden | Zusammen mit den vier freien Zahlen, nach Feature 15 |
| **Zellgröße, Separationsradius, Nachbar-Budget ‹k› und Klemmfaktor ‹f›** | Der Entwickler beim Tunen; Form und Revisions-Auslöser stehen in ADR-0004 | Mit Feature 3. Der Beleg ist ein Blick auf den Turmring, keine Zahl |
---
@@ -411,6 +502,10 @@ Für keinen dieser Punkte wird hier eine Antwort erfunden.
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.
Aufgelöste Punkte werden **nicht gelöscht**, sondern als *entschieden, siehe X* markiert und
behalten ihren Befund. Der Weg zu einer Entscheidung ist so lange wertvoll, wie jemand sie in Frage
stellen könnte. Aufgelöst sind bisher die Punkte **2**, **5**, **9** und **14**.
### Widersprüche
1. **Card-Drops überqueren die Run-Grenze, aber ADR-0003 kennt sie nicht.** ADR-0003 §6 nennt
@@ -423,14 +518,19 @@ widersprechen oder an denen eine Frage niemandem gehört. Sie stehen hier, statt
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.
2. **Die Slot-Kurve gehört niemandem, ist aber Abnahmebedingung.** — **Entschieden, siehe Feature 10
und Feature 9.** *Der Befund bleibt stehen, weil er zeigt, wo die Lücke herkam.* #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, sagte 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 gab keinen Mechanismus, der ihn erhöht.
**Auflösung:** Slots werden **aus der Meta-Währung im Workshop gekauft**, neben den
Stat-Upgrades, mit derselben exponentiellen Kostenform. Kein zweiter Freischalt-Mechanismus,
keine Meilenstein-Schwelle. Der Grund ist nicht Sparsamkeit, sondern dass daraus die
Meta-Entscheidung des Slice wird: Breite gegen Stärke aus einem Topf. Die Randbedingung aus #11
gilt unverändert weiter und macht den ersten Slot-Preis zu einer gebundenen, nicht zu einer
freien Zahl — siehe „Offen". Was diese Auflösung neu aufwirft, steht unten als Punkt 16.
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
@@ -449,9 +549,10 @@ widersprechen oder an denen eine Frage niemandem gehört. Sie stehen hier, statt
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
5. **~24 Shop-Zeilen stehen gegen 12 Stats.** — **Entschieden, siehe Feature 6 und den Nachtrag zu
ADR-0002 §3.** *Der Befund bleibt stehen.* 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
nicht alles kaufen"). ADR-0002 §3 kannte damals 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
@@ -460,8 +561,18 @@ widersprechen oder an denen eine Frage niemandem gehört. Sie stehen hier, statt
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
des Originals an Labs und Perks hängt — beides im Nebel. Der Utility-Tab war damit der schwächste
Teil der HUD-Struktur.
**Auflösung:** Der zweite Ast wird gewählt — ADR-0002 §3 **war** unvollständig und ist um vier
Utility-Stats gewachsen (`CoinsPerWave`, `CritCoinGain`, `FreeUpgradeChance`, `MetaGain`; der
Nachtrag ist in der ADR als solcher markiert). Utility ist damit das, was #3 im Referenzspiel
gefunden hat: ein Ökonomie-Multiplikator, kein Kampf-Stat. Der erste Ast trägt trotzdem mit:
Bei 5 + 4 + 4 = 13 belegten von ~24 möglichen Zeilen füllt das Panel seine Zeilen nicht aus, und
**die Folgerung „der Spieler kann in einem Run nicht alles kaufen" folgt nicht mehr aus dem
Layout.** Sie muss aus der Kostenkurve kommen (#8, Tuning-Zielgröße „Zeit zwischen zwei
bezahlbaren Käufen") oder fallen. Punkt 6 unten hatte für dieselbe Kette schon festgehalten, dass
von der HUD-Rechnung nur noch die Zahl 24 übrig ist — jetzt ist auch die keine Design-Aussage
mehr, sondern eine Layout-Obergrenze.
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)
@@ -491,14 +602,20 @@ widersprechen oder an denen eine Frage niemandem gehört. Sie stehen hier, statt
**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.
9. **Gegner-gegen-Gegner-Kollision ist seit #4 offen und steht auf keiner Liste.** —
**Entschieden, siehe [ADR-0004](adr/0004-spatial-grid-fuer-gegner-nachbarschaft.md).** *Der
Befund bleibt stehen.* #4 und die Cap-Analyse nannten 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 stand **nicht**
im Fog der Karte, in keiner ADR und in keinem der offenen Punkte, die diese Spec zugewiesen
bekommen hat.
*Der Vorschlag dieser Spec war „Stapeln zulassen" — er ist verworfen worden.* Nicht wegen der
Separation, sondern weil das Grid **drei weitere Konsumenten** hat, die bisher jeder für sich
über die Gegnermenge laufen wollten: die Zielauswahl in `tower_fire` (laut #4 der nächste Hotspot
nach dem Rendering), der geteilte Reichweiten-Scan aus #12 und die Projektil-Kollision. Genau der
Scan, dessen Siebenfach-Ausführung #12 als Bruchstelle benannt hat, wird damit zu einer
Grid-Abfrage. Der Rebuild ist gegen die Messungen aus #4 gerechnet, nicht geschätzt: ≤0,10 ms bei
`HARD_ENEMY_CAP`, also ≤0,6 % des Frame-Budgets.
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
@@ -523,3 +640,49 @@ widersprechen oder an denen eine Frage niemandem gehört. Sie stehen hier, statt
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.
### Neu aufgefallen beim Einarbeiten der drei Entscheidungen (2026-08-10)
14. **Die `points`-Stats mit Basis 0 rechnen sich in ADR-0002 auf null.** — **Behoben, siehe
ADR-0002 §1.** *Der Befund bleibt stehen, weil er ein ausgelieferter Defekt gewesen wäre, kein
Beobachtungsposten.* Die `Direct`-Formel aus ADR-0002 §1 lautete
`(base + flat) × (1 + clamp(points)) × mult`. Die Stat-Tabelle in §3 wies `CritChance` und
`DefensePercent` aber `base = 0` **und** `points` als einzigen erwarteten Akkumulator zu.
Eingesetzt: `(0 + 0) × (1 + 0,30) × 1 = 0`. Crit Chance hätte im Spiel **nie über 0 % steigen
können**, egal wie viele Workshop-Level, Cards und In-Run-Upgrades zusammenkommen — und weder
Compiler noch ein Formeltest hätten es angezeigt. Es ist exakt dieselbe Falle, die die ADR zwei
Absätze weiter unten für `HealthRegen` und `DefenseFlat` benennt und begründet, nur dass sie dort
für `mult` gezogen wurde und für `points` nicht. Aufgefallen ist sie beim Einordnen des neuen
`FreeUpgradeChance`, der sich korrekt an die Form von `CritChance` gehalten und den Fehler
deshalb geerbt hat.
**Behebung:** ADR-0002 §1 hat einen dritten Anwendungsmodus **`Fraction`** bekommen —
`clamp((base + flat + points) × mult)` — für Stats, die selbst ein Anteil sind. `CritChance`,
`DefensePercent` und `FreeUpgradeChance` stehen jetzt dort; an ihren `base`-, „Erwartet"- und
`Clamp`-Spalten hat sich nichts geändert. Es ist ein dritter Wert desselben Feldes, kein zweites
Bit; drei billigere Wege sind in der ADR geprüft und mit Begründung verworfen. Eine Prüfregel und
ein fünfter Revisions-Auslöser verhindern die Wiederkehr, das durchgerechnete Beispiel der ADR
trägt den Fall jetzt als Rechnung, und `Interval` ist nicht betroffen.
15. **Nicht-Konvertierbarkeit ist bisher nur in eine Richtung ausgesprochen.** Issue #1 und #11
begründen die zwei Währungen damit, „dass eine In-Run-Ausgabe keinen Meta-Fortschritt kostet" —
also `Meta → Coins`. Die Gegenrichtung hat nie jemand benannt: Ein aus `Coins` gekauftes
In-Run-Upgrade, das die Meta-Drop-Chance oder die Meta-Menge erhöht, wandelt `Coins` in
Meta-Fortschritt und hebelt die Trennung genauso aus, nur unauffälliger. Der Fall wird real,
sobald der Utility-Tab Ökonomie-Upgrades trägt (Feature 6) — er ist genau der Tab, in den so ein
Upgrade thematisch gehören *würde*. Diese Spec zieht die Konsequenz oben ein (`MetaGain` ist
Workshop-only, mit einer Akzeptanzzeile in Feature 6 und einer Zeilen-Anmerkung in ADR-0002 §3),
aber die **Regel selbst** steht damit an einer Stat-Tabelle statt in der Entscheidung, aus der
sie folgt. Wer die Zwei-Währungs-Entscheidung liest, findet die zweite Richtung dort nicht.
16. **Slots aus der Meta-Währung erzeugen genau die Konkurrenz, die #11 für Cards ausgeschlossen
hat.** #11 verwirft eine gemeinsame Währung für Cards mit dem Argument: „beide Achsen
konkurrieren dann linear um denselben Topf, und niemand kauft Cards, bis der Workshop ausgereizt
ist — das Original verhindert das ausschließlich über Nicht-Fungibilität, nicht über Preise."
Die Slot-Entscheidung (Punkt 2) tut fast das: Sie stellt die Card-Achse — nicht die Karten,
aber ihre Breite — in dieselbe Kaufwarteschlange wie die Stat-Upgrades. Zwei Unterschiede halten
sie tragfähig: Cards selbst fallen weiterhin gratis, die Achse ist also nie hinter dem Shop
verschlossen; und Slot 2 muss laut #11 früh bezahlbar sein, was das Versagensmuster „niemand
kauft, bis der Workshop ausgereizt ist" für den einen Kauf ausschließt, der im Abnahmekriterium
steht. Für Slot 3 bis 5 gilt dieser Schutz **nicht** — dort ist das Argument aus #11 unverändert
gültig, und ob die Slots 3–5 im Slice je gekauft werden, ist eine offene Frage an die
Kostenkurve, keine an die Mechanik.