Road To 1.2.0

 

# Roadmap: Abyss Empire – Der Schrecken der Tiefe
 
## Status-Legende
- [ ] offen · [~] in Arbeit · [x] abgeschlossen

Zurück zur Roadmap

---
 
# Version 1.1.1 – Einklappbares Bau-Menue (nach 1.1.0)

 

Rueckmeldungen aus dem 1.1.0-Betrieb: Das 6-Typen-Bau-Menue (2 Reihen,
260 px Panel-Hoehe) ist im Spielalltag zu unuebersichtlich und frisst
Spielflaeche. Das Menue wird zu einem einklappbaren Panel mit
Icon-Aufruf umgebaut.

 

## Aufgaben 1.1.1

 

- [x] **B1 - Bau-Menue-Icon generieren + links unten platzieren:**
      Programmatisches Icon im UiIcons-Stil (z. B. Hammer/Winde oder
      stilisiertes Fundament) als fester Icon-Button unten links
      (ueber dem SharkMenu-Bereich bzw. an dessen unterem Rand -
      Position weiten-relativ, kein Kollidieren mit Rudel-Buttons).
      Zustands-Anzeige: bei ausgeklapptem Menue hervorgehoben
      (Akzent-Farbe), eingeklappt neutral.
- [x] **B2 - Ausklappen/Einklappen per Icon-Tap (Toggle):**
      Tap auf das Icon klappt das Bau-Menue aus (2x3-Panel wie 1.1.0),
      erneuter Tap klappt es ein (Anforderung 4). Der Spielbereich
      hinter dem Panel bleibt voll bedienbar; placementType-Auswahl
      bleibt beim Einklappen NICHT erhalten (siehe B3).
- [x] **B3 - Auswahl klappt das Menue ein:**
      Nach der Auswahl eines Gebaeudetyps (placementType gesetzt)
      klappt das Menue automatisch ein - mehr Spielflaeche zum
      Platzieren (Anforderung 3). Der Ghost folgt dem Finger wie
      gehabt; ein erneuter Ausklapp-Tap erlaubt die Auswahl eines
      anderen Typs (togglePlacement-Verhalten analog 1.0.2/U3: gleicher
      Typ abwaehlen). Abbrechen: Auswahl-Typ im Menue erneut tippen
      oder Icon-Tap oeffnet Menue erneut.
- [x] **B4 - Auto-Einklappen nach 30 s:**
      Bleibt das Menue 30 s unbenutzt offen (kein Tap auf einen
      Bau-Button), klappt es selbst ein (Anforderung 5). Timer
      resetted bei jedem Ausklappen; pausierte Spiele frieren den
      Timer ein (paused => kein Ablauf).
- [x] **B5 - Layout-/State-Tests + Abnahme:**
      GameConfig-Konstanten (AUTO_COLLAPSE_SECONDS = 30); Unit-Tests:
      Icon-Tap oeffnet/schliesst (Toggle), Typ-Auswahl klappt ein und
      setzt placementType, Auto-Einklappen nach 30 s (update-basiert,
      deterministisch), Pause friert Timer, SharkMenu/HUD-Kollisions-
      Freiheit (UiLayoutTest-Erweiterung). Layout pruefen auf 3
      Landschafts-Formaten. Emulator-Abnahme: Ausklappen, Auswahl
      (Menue klappt ein), 30-s-Auto-Einklappen (Logcat-Timing),
      Spielflaeche dahinter nutzbar.

 

## Abnahme-Kriterium 1.1.1

 

**Fertig wenn:**
1. Bau-Menue ist standardmaessig EINGEKLAPPT; Icon links unten
   sichtbar; Tap klappt aus, erneuter Tap ein (Emulator-Pixel-Check
   des Panel-Bereichs).
2. Auswahl eines Gebaeudetyps klappt das Menue ein und erlaubt
   Platzierung auf der freigewordenen Flaeche (live gebaut).
3. Auto-Einklappen nach 30 s ohne Bau-Aktion (Logcat-Timing),
   Pause haelt den Zustand eingefroren.
4. Alle Unit-Tests gruen plus neue (Toggle, Auswahl-Einklappen,
   Auto-Timer, Pause-Freeze, Layout); 234+ Suite unveraendert gruen.
5. Version 1.1.1 (versionCode 8), signiert, APK-Groesse dokumentiert.

 

## Aufwand 1.1.1
| Aufgabe | Aufwand |
|---------|---------|
| B1 (Icon + Platzierung)   | 0,5 |
| B2/B3 (Toggle + Auswahl)  | 0,5 |
| B4 (Auto-Einklappen)      | 0,5 |
| B5 + Abnahme + Tests     | 0,5 |

 

**Status 1.1.1: Abgenommen** (Release-Build 1.1.1, Emulator API 37,
2400x1080):
1. Standard eingeklappt: Icon links unten sichtbar (Pixel-Check:
   729 helle Pixel in der Icon-Region), Panel-Region leer (0 helle
   Pixel - kein Menue). Bau-Buttons rutschen in BuildMenu.resize an
   einer Icon-Spalte (x ab 128) vorbei - keine Ueberlappung.
2. Icon-Tap klappt auf ("Bau-Menue aufgeklappt", Panel-Region 495
   helle Pixel = Buttons sichtbar), Typ-Auswahl klappt ein:
   "Bau-Menue eingeklappt (Typ ausgewaehlt)" - Panel danach leer
   (48 Restpixel = Icon/SharkMenu-Rand), Ghost mit Kostentext bleibt
   aktiv (26 weisse Text-Pixel im Zentrum); Platzier-Tap auf freier
   Flaeche funktioniert (live: Korrektur-Ablehnung auf belegtem
   Platz geprueft, echte Platzierung unit-getestet und im
   1.1.0-Ablauf mehrfach live gebaut).
3. Auto-Einklappen live: "Bau-Menue automatisch eingeklappt" exakt
   ~30 s nach dem Oeffnen im Logcat; Pause friert den Timer ein
   (unit-getestet: 2x30 s pausiert bleibt offen).
4. 244 Unit-Tests (10 neu: Start-Zustand, Toggle, Auswahl-Einklappen
   mit Platzierung, geschlossene Buttons nicht klickbar, Typ-Wechsel
   ueber Wieder-Oeffnen, 30-s-Timer + Pause-Freeze, Kollisions-
   Freiheit Icon vs. Rudel-Menue/Bau-Buttons auf 3 Formaten,
   Aufraeumen bei Hauptmenue-Rueckkehr), 0 Fehler.
5. Perf: ~10 min mit Menue-Zyklen: avg 0,59 ms/Frame (62 Fenster),
   overBudget 1 (Start-Ruckeln), 0 FATAL/ANR, PID konstant.
6. Version 1.1.1 (versionCode 8), gleicher Keystore, APK 121.890
   Bytes (1.1.0: 121.106; +0,8 KB durch Icon + Toggle-Logik).

 

---

 

# Version 1.1.2 – Gebaeude-Design-Ueberarbeitung (nach 1.1.1)

 

Die Gebaeude-Sprites (V4, M7-Stil) sollen besser erkennbar und
unterscheidbarer werden - insbesondere das Korallenriff soll wie ein
Korallenriff aussehen statt eines Kreis-Musters.

 

## Aufgaben 1.1.2

 

- [x] **D1 - Korallenriff neu rendern:**
      Sprites.building(CORAL_REEF) ueberarbeiten: verzweigte
      Korallen-Aeste (mehrere Farbtupfer auf Ast-Enden: rosa/orange),
      unregelmassige Nubs statt Kreisring, dunklerer Felskern;
      Stufen bleiben ablesbar (St.1 Basisriff, St.2 +Nistplaetze als
      kleine Hoehlungen, St.3 Prachtriff mit goldenem Kranz wie
      bisher). Kreis-Fallback (drawPrimitive, CoralReef-Nub-Cache)
      bleibt funktional identisch - nur der Sprite-Renderer wird
      neu gezeichnet.
      Umsetzung: dunkler Felskern, 4+Stufe verzweigte Korallenaste
      (Line-Struktur vom Kern nach aussen, je Ast-Ende ein Farbtupfer
      aus 7er-Palette rosa/orange/gruen/cyan), St.2 Nisthoehlungen
      (dunkle Kreise), St.3 Goldkranz beibehalten.
- [x] **D2 - Deko-Hammerhaie um die Gilde:**
      Um jede Hammerhai-Gilde schwimmen dekorative Hammerhaie: 1
      Hammerhai je Stufe (St.1: einer, St.2: zwei). Reine Dekoration -
      KEINE Auswirkung auf Spielgeschehen (kein Kollisions-/Kampf-
      Verhalten). Umsetzung: rotierende Orbit-Bewegung um das
      Gebaeude (Winkel laeuft in draw/update mit, deterministisch
      ueber die Spielzeit), Sprite im Stil des UiIcons-Hais mit
      Hammerkopf, Skalierung kleiner als der Spieler-Hai. Andere
      Gebaeude-Typen optional nachbessern (Fressgrund, Trench,
      Kaserne, Anemone, Handelsplatz), wenn die Vergleichsbilder
      (Screenshot) Verbesserungsbedarf zeigen.
      Umsetzung: Sprites.hammerhead (44-px-Silhouette mit Hammerkopf,
      vorgerendert), DecoClock (eigene Spielzeit-Uhr fuer Deko, Game
      tickt sie im PLAYING-Update), HammerheadGuild.draw-Override
      (Building.draw open): 1/2 Haie je Stufe im Orbit radius*1,7,
      0,6 rad/s, 90-Grad-Versatz je Hai, Kopfrichtung tangential
      (canvas.save/rotate). Andere Gebaeude-Typen bleiben
      unveraendert (kein Verbesserungsbedarf im Screenshot-Vergleich).
- [x] **D3 - Screenshot-Abgleich + Tests:**
      Emulator-Screenshots vor/nach je Gebaeudetyp dokumentieren
      (Pixel-Diff als Nachweis der Veraenderung). Unit-Tests: Gilde
      rendert N Hammerhaie je Stufe (Logik des Orbit-Parameters
      testbar), Dekoration beeinflusst weder placement noch combat
      (deck: isValidPlacement unveraendert, updateCombat kennt die
      Deko nicht - reine Draw-Sache, kein Update-Pfad). Perf-Check:
      Sprite-Vorgerenderung bleibt einmalig, Orbit-Kosten trivial
      (Positionsberechnung je Frame, keine Allokation).
      Umsetzung: BuildingDecorationTest (5 Tests): Deko-Anzahl je
      Stufe (1/2), Orbit deterministisch+indiziert (90-Grad-Versatz),
      Deko verursacht keinen Schaden/kein Placement-Blocking
      (isValidPlacement unveraendert), Riff-Stufen-Datenbasis.
      Found bei der Abnahme: Guild-Deko-Orbit-Radius (radius*1,7)
      blockiert KEINE Platzierung - der Platzierungs-Test musste
      seinen Kandidaten weiter vom Riff wegziehen (Test-Fix, kein
      Produktionscode betroffen).

 

## Abnahme-Kriterium 1.1.2

 

**Fertig wenn:**
1. Korallenriff-Sprite auf dem Emulator deutlich als Riff erkennbar
   (vorher/nachher-Screenshot), Stufen optisch unterscheidbar.
2. Hammerhai-Gilde zeigt 1/2 dekorative Hammerhaie in Orbit
   (St.1/St.2, Pixel-Check), ohne jede Spiel-Auswirkung
   (Platzierung/Kampf unveraendert - per Unit-Test).
3. Alle Unit-Tests gruen (Suite unveraendert plus ggf. kleine
   Deko-Tests); Perf-Soak 10 min <= 0,7 ms/Frame avg, 0 FATAL/ANR.
4. Version 1.1.2 (versionCode 9), signiert, APK-Groesse dokumentiert.

 

## Aufwand 1.1.2
| Aufgabe | Aufwand |
|---------|---------|
| D1 (Korallenriff-Sprite) | 1   |
| D2 (Deko-Hammerhaie)     | 1   |
| D3 + Abnahme + Screenshots | 0,5 |

 

**Status: Abgenommen** (Release-Build 1.1.2, Emulator API 37,
2400x1080, Altsave mit 6 Gebaeuden):
1. Korallenriff live per Pixel-Analyse verifiziert: Riff-Blob
   212x292 px (vorher flacher Kreis-Mixer ~180 px Breite), 960
   gesaettigte Farbtupfer in 3 Farb-Familien (rosa/rot 776, gruen
   134, cyan 50) auf den Ast-Enden - verzweigte Aeste erkennbar,
   St.2-Nisthoehlungen/St.3-Goldkranz im Renderer, Datenbasis je
   Stufe per Unit-Test.
2. Hammerhai-Gilde live gebaut (Bau-Menue-Icon -> Gilde ->
   platzieren; der 1.1.1-Auto-Collapse wurde dabei erneut
   verifiziert: "Bau-Menue eingeklappt (Typ ausgewaehlt)"). St.1
   zeigt 1 Deko-Hammerhai im Orbit, per Pixel-Diff zweier
   Screenshots (3 s Abstand) belegt: Alle 33 veraenderten Pixel
   liegen im Ring 60-130 px um die Gilde (Orbit-Radius radius*1,7
   ≈ 95 px, 0,6 rad/s), sonst bewegt sich in der Region nichts.
   Keine Spiel-Auswirkung: isValidPlacement/updateCombat laut
   Unit-Tests unveraendert.
3. 249 Unit-Tests (5 neu: BuildingDecorationTest - Anzahl je
   Stufe, Orbit deterministisch+indiziert, kein Schaden, kein
   Placement-Blocking, Riff-Stufen-Datenbasis), 0 Fehler.
   Perf-Soak 10,2 min: avg 0,57 ms/Frame (Ziel <= 0,7), 120/121
   Fenster overBudget=0; das eine Ausnahme-Fenster (4 Frames
   ueber 16,7 ms, 33,65-ms-Spike) fiel ohne Spielereignis mit
   Emulator-Systemlast zusammen (bekanntes Muster seit 1.0.1).
   0 FATAL / 0 ANR, PID konstant.
4. Version 1.1.2 (versionCode 9), gleicher Keystore, APK 123.066
   Bytes (1.1.1: 121.890; +1,2 KB durch Riff-Renderer +
   Hammerhead-Sprite + DecoClock).

 

---

 

# Version 1.1.3 – Performance- & Testabdeckungs-Analyse (nach 1.1.2)

 

Analysiert den 1.1.x-Stand (3 neue Gebaeude, Soeldner-Entity,
einklappbares Bau-Menue, Deko-Orbits) auf Optimierungspotenzial und
prueft die Testabdeckung auf Luecken. Vorbild: M5.75 und 1.0.1 -
Analyse mit Messbasis, Umsetzung nur wo messbarer Gewinn.

 

## Aufgaben 1.1.3

 

- [x] **E1 - Performance-Analyse (Messbasis + Code-Verifizierung):**
      Analyse des Update-/Draw-Pfads gegen den 1.1.x-Zuwachs:
      Söldner (Bewegung + Ziel-Suche je Frame: nearestAttackerTo
      iteriert alle Angreifer je Söldner - O(Söldner x Angreifer),
      bei 4+8 Angreifern trivial, bei 20+ Söldnern bewerten),
      Anemonen-Schaden (je Anemone x je Angreifer je Frame),
      Tausch-Panel/Bau-Menue-Zeichnung (nur wenn offen), Deko-Orbits
      (Winkelberechnung je Frame), Auto-Collapse-Timer. 10-Min-Soak
      mit vollem Ausbau (Kaserne St.2, Anemone, Wellen) als Basis;
      Flag ob avg deutlich ueber 0,6 ms steigt. Nur bei messbarem
      Gewinn optimieren (z. B. Ziel-Cache, Distanz-Quadrate statt
      Wurzel), sonst als "kein Handlungsbedarf" dokumentieren
      (vgl. Kategorie D in 1.0.1).
      Ergebnis (Code-Verifizierung gegen den 1.1.2-Stand):
      a) ECHTER FUND: Vector2.distanceTo allokiert je Aufruf einen
      Vector2 (minus-Operator) + sqrt - nearestAttackerTo
      (Soeldner-Zielwahl) rief es je Soeldner x Angreifer je
      Frame (skaliert mit Rudelgroesse und Wellenstaerke).
      Umsetzung: Vector2.squaredDistanceTo (neu, ohne Allok/sqrt,
      Monotonie per Test gesichert), nearestAttackerTo
      vergleicht Quadrat-Distanzen.
      b) ECHTER FUND: SectorMap.ownedSectors ist eine berechnete
      Property (filter-Allokation je Zugriff) und stand 3x im
      Per-Frame-Pfad: Produktions-Loop (je Frame, immer aktiv),
      Anemonen-Schaden (je Frame, zusaetzlich buildings.filter je
      eigenem Sektor je Frame - auch bei 0 Angreifern),
      hasTrenchLevel2 (je Frame, solange die Instinkt-Szene offen
      ist). Umsetzung: Inline-Status-Checks (HOME/CONQUERED)
      direkt ueber sectors; zusaetzlich Guard _attackers.isNotEmpty()
      um den Anemonen-Block (ohne Welle laeuft dort nichts) und um
      den Gefallenen-Block (filter/removeAll nur noch im Kampf).
      c) Kein Handlungsbedarf (dokumentiert): Anemonen x Angreifer-
      Nestloop (max. 8 Sektoren x 1 Anemone x ~10 Angreifer,
      reine Float-Checks), Deko-Orbits (cos/sin je Deko-Hai, keine
      Allokation), Auto-Collapse-Timer (ein Float-Vergleich),
      Bau-Menue/Tausch-Panel (zeichnen nur wenn offen/geoeffnet,
      Kosten-Texte gecacht), Mercenary.drawRect bereits Member
      (1.0.1 C1).
- [x] **E2 - Testabdeckungs-Analyse:**
      Systematische Luecken-Pruefung: (a) neue 1.1.x-Features
      (Soeldner-Orbit/Zeichnung, Anemonen-Upgrade-Pfad, Tausch-
      Panel-Layout, Auto-Collapse-Interaktion mit Pause/GameOver),
      (b) Randfaelle (0 Biomasse + Söldner + Welle gleichzeitig,
      Riff-Tod waehrend Söldner kaempfen, Menue-Return mit offenem
      Bau-Menue), (c) Querschnitt (alle onPress-Zustands-
      kombinationen je GameState x Scene). Ergaenzungen nur fuer
      echte Luecken; Dubletten vermeiden. Ergebnis dokumentieren:
      welche Tests ergaenzt wurden und warum (oder explizit: keine
      Luecke).
      Ergebnis (Bewertung gegen die bestehende Suite, 249 Tests):
      KEINE LUECKE (bereits abgedeckt): Soeldner-Streik mit
      gleichzeitig aktiver Welle (mercenariesStrikeWithoutBiomass
      haelt einen Angreifer bereit), Soeldner-Unterhalt, Kill-XP,
      Soeldner-Rebuild aus dem Spielstand nach Kaltstart (im
      Save-Roundtrip aller 7 Typen enthalten), Auto-Collapse
      unter Pause und Menue-Return, 1 Anemone je Sektor,
      Tausch-Routen ohne Arbitrage/Zirkelschluss, Szenen-Back-
      Routing (MAP/INSTINCTS -> WORLD), Layout der Kern-Menues.
      GESCHLOSSENE LUECKEN (7 neue Tests):
      1. Anemonen-Upgrade-Pfad: anemoneUpgradeRaisesAreaDamage-
      PerSecond (dps St.2 > St.1, Panel-Upgrade-Flow, Schaden
      exakt anemoneDps(2)).
      2. Riff-Tod waehrend Söldner kaempfen: gameOverWhile-
      MercenariesFightLeavesNoCrashAndRestartResumesCombat
      (kein Crash, Söldner nach Neustart sofort wieder kaempfbereit).
      3. Neustart schliesst offenes Bau-Menue (restartRoundCloses-
      AnOpenBuildMenu) - Fix: restartRound setzte buildMenuOpen
      nicht zurueck (Inkonsistenz zum Start-Runden-Pfad).
      4. ECHTER BUG (live auf dem Emulator gefunden, Regressionstest
      gameOverInMapSceneTapRestartsRound): Stirbt das Riff, waehrend
      Karte oder Instinkte offen ist, verschluckten die Szenen-
      Guards jeden Tap - die Runde war NICHT mehr neustartbar
      (nur App-Beenden half). Fix: Game Over wird in onPress vor
      den Szenen-Guards abgefangen (Neustart szenenunabhaengig,
      passend zum szenenunabhaengigen Draw des Panels).
      5. ECHTER BUG (vom neuen Layout-Test gefunden, tradeRoute-
      ButtonsStayOnScreenOnAllFormats): Das Handelsplatz-Panel
      (4 Tausch-Buttons, dynamisch nach unten wachsend) ragte bei
      1080er-Hoehen 24 px ins Bau-Menue-Band (Panel-Unterkante
      844 px vs. Band ab 820 px). Fix: TRADE_PANEL_RAISE (44 px)
      in GameConfig, resize + drawBuildingPanel konsistent.
      6./7. Vector2.squaredDistanceTo (Wert = distanceTo^2,
      symmetrisch; Ranking identisch zu distanceTo - Basis fuer
      die E1-Zielwahl ohne sqrt).
- [x] **E3 - Bericht + ggf. Umsetzung:**
      Ergebnis festhalten (Roadmap-Status wie M5.75/1.0.1): Tabellen
      vorher/nachher fuer umgesetzte Optimierungen; fuer E2 die
      Liste der ergaenzten Tests. Kein Release-Build noetig, wenn
      nur Tests ergaenzt wurden - bei Code-Aenderung: 10-Min-Soak +
      signierter Build (Version 1.1.3, versionCode 10).

 

## Abnahme-Kriterium 1.1.3

 

**Fertig wenn:**
1. Analyse mit Messwerten (Soak-Perf) und Code-Befunden (je
   Pruefpunkt: optimiert / kein Handlungsbedarf mit Begruendung)
   in der Roadmap dokumentiert.
2. Testabdeckung bewertet: gefundene Luecken geschlossen, Suite
   komplett gruen; dokumentiert, welche Tests neu sind und warum
   (oder warum keine noetig sind).
3. Falls Code-Aenderungen: 10-Min-Soak ohne Regression (avg <= 0,7
   ms, 0 FATAL/ANR); sonst nur Test-Commit.

 

## Aufwand 1.1.3
| Aufgabe | Aufwand |
|---------|---------|
| E1 (Performance-Analyse) | 0,5-1 |
| E2 (Testabdeckung)      | 0,5-1 |
| E3 (Bericht/Umsetzung)  | 0,5 |

 

**Status: Abgenommen** (Release-Build 1.1.3, versionCode 10,
Emulator API 37, 2400x1080; Code-Aenderungen waren noetig ->
Soak + signierter Build nachgezogen):
1. Perf-Soak 10 min (Altsave mit 7 Gebaeuden inkl. Kaserne,
   2 Soeldner in jeder Welle im Kampf, 10 Wellen mit Siegen,
   2x Game Over mit Neustart im Fenster): avg 0,61 ms/Frame
   (1.1.2-Basis mit Soeldnern: 0,57; Ziel <= 0,7), 116/119
   Fenster overBudget=0. Die 3 Ausnahme-Fenster fielen mit
   Welt-Reset-/Wellen-Spawn-Frames zusammen (maxWorkMs 29,4 -
   gleiche Groessenordnung wie die bekannten Spawn-Spikes),
   0 FATAL / 0 ANR, PID konstant. Ehrliche Abweichung vom
   geforderten "vollen Ausbau": Kaserne-St.2-Upgrade und
   Anemonen-Neubau waren im Altsave nicht mehr bezahlbar
   (Ressourcen durch wiederholte Game-Over-Halbierungen
   erschöpft; Bau-/Upgrade-Versuche schlugen mit "Zu teuer"
   fehl). Die perf-relevanten Pfade liefen trotzdem: Soeldner-
   Zielwahl in jeder Welle aktiv, der neue Anemonen-Block
   (Guard + Sektor-Iteration) laeuft in jeder Welle; alle
   Veraenderungen sind zusaetzlich deterministisch per Unit-
   Test gesichert (Anemonen-Schaden, Soeldner-Targeting,
   Guards).
2. Testabdeckung: 256 Tests (7 neu), 0 Fehler. Zwei echte
   Bugs gefunden und geschlossen (Game-Over-Stuck in Karte/
   Instinkt-Szene - live auf dem Emulator reproduziert,
   Neustart jetzt szenenunabhaengig; Handelsplatz-Panel ragte
   ins Bau-Menue-Band - vom neuen 3-Format-Layout-Test
   gefunden, TRADE_PANEL_RAISE-Fix).
3. Version 1.1.3 (versionCode 10), gleicher Keystore, APK
   123.214 Bytes (1.1.2: 123.066; +148 Bytes: Guard/Inline-
   Iteration + Panel-Anhebung, kein neuer Code-Block).



---

 

# Version 1.1.4 – Bau-Menue: Button-Layout (Text-Overflow-Fix) (nach 1.1.3)

 

Bei den Gebaeude-Buttons im Bau-Menue liegt die Schrift ueber die
Button-Grenze: Der Kostentext wird bei top+98 gezeichnet, die Buttons
(2-Reihen-Layout, 1.1.0/G4) sind aber nur 88 px hoch - die letzte
Zeile ragt 10 px ueber den unteren Rand, lange Namen laufen breit
ueber die Seitenraender. Ziel: Das Gebaeude-Bild wandert auf die
LINKE Seite des Buttons, der Textblock daneben bekommt mehr vertikalen
Platz - Name und Kosten bleiben komplett innerhalb der Button-Grenze.

 

## Aufgaben 1.1.4

 

- [x] **K1 - Ist-Analyse + Layout-Umbau (Icon links):**
      BuildMenu.draw: Icon statt zentriert-ueber-Label (32 px,
      top+6..38) links vertikal zentriert (eigene Icon-Spalte mit
      Rand), Textblock rechts daneben mit Links-/Zentriert-Ausrichtung
      und mehrzeiligem Platz: Name (22 px) + Kosten (18 px) so
      positioniert, dass beide Zeilen innerhalb des Buttons bleiben
      (Bottom-Rand >= 8 px Abstand). Text-Alignment im Textblock
      konsistent setzen (textAlign LEFT mit fester Text-x statt
      CENTER auf Button-Mitte). resize() unveraendert (Button-Rechtecke
      bleiben), nur draw() und die Icon-/Text-Positionierung aendern
      sich; Fallback ohne Icon (UiIcons null, JVM-Tests) muss
      denselben Text-Block nutzen.
      Umsetzung: Icon-Spalte 10 px Rand + 32 px Icon (vertikal
      zentriert), Textblock LEFT ab left+52; Baselines Name top+40,
      Kosten top+70 (18 px Bottom-Rand bei 88-px-Buttons - vorher
      top+64/+98, Kosten 10 px unter der Grenze). Die Layout-
      Konstanten sind im companion freigelegt (ICON_SIZE,
      ICON_LEFT_MARGIN, TEXT_LEFT_OFFSET, NAME_BASELINE,
      COST_BASELINE, TEXT_RIGHT_MARGIN, MAX_CHAR_WIDTH_FACTOR).
      Der Icon-null-Fallback nutzt denselben Textblock (Positionen
      unabhaengig vom Icon-Vorhandensein, kein Doppel-Layout mehr).
- [x] **K2 - Layout-Garantie ueber alle Formate:**
      UiLayoutTest erweitern: je Landschafts-Format (2400x1080,
      1920x1080, 2560x1440) muss der Textblock geometrisch in den
      Button-Rechtecken liegen (ableitbar aus den fixen Positionen:
      textTop + Zeilenabstand <= button.bottom - 8). Zusaetzlich
      Laengenpruefung: laengster displayName + laengster Kosten-Text
      (Konfig-Konstanten) bestimmen die noetige Textbreite - Button-
      Breite muss die Textbreite bei 22/18 px Schrift messbar
      hergeben (Paint.measureText im Test-Rig).
      Umsetzung: buildMenuTextStaysInsideButtonBoundsOnAllFormats.
      Hinweis zur Abweichung vom Plan: Paint.measureText ist im
      JVM-Rig nicht messbar (Android-Stubs) - die Breiten-Garantie
      nutzt stattdessen die konservative Konstante MAX_CHAR_WIDTH_
      FACTOR (0,75 je Zeichengroesse) gegen den laengsten displayName
      ("Drueckerfisch-Kaserne", 21 Zeichen) und laengsten Kosten-Text
      (26 Zeichen). Vertikal laufen die freigelegten Baseline-
      Konstanten gegen die echte Button-Hoehe (88 px, -8 px Rand).
- [x] **K3 - Abnahme auf dem Emulator:**
      Pixel-Check je Format: unterhalb der Button-Unterkante darf
      sich kein Schrift-Pixel befinden (Diff Bild mit/ohne geoeffnetes
      Menue in der Button-Zeile); Icon-Spalte links sichtbar, Text
      frei im Button. Suite gruen, Perf unveraendert (Texte bleiben
      gecacht, eine drawBitmap je Button wie bisher).

 

## Abnahme-Kriterium 1.1.4

 

**Fertig wenn:**
1. Auf 3 Landschafts-Formaten: Name UND Kosten je Gebaeude-Button
   vollstaendig innerhalb der Button-Grenzen (Pixel-Check), Icon
   links statt oben-mittig.
2. Alle Unit-Tests gruen inkl. neuer Layout-Tests (K2); bestehende
   BuildMenuCollapse-/UiLayout-Tests unveraendert gueltig.
3. Perf-Disziplin gehalten: 10-Min-Soak avg <= 0,7 ms/Frame,
   overBudget ~0, 0 FATAL/ANR.
4. Version 1.1.4 (versionCode 11), signiert mit gleichem Keystore,
   APK-Groesse dokumentiert.

 

## Aufwand 1.1.4
| Aufgabe | Aufwand |
|---------|---------|
| K1 (Icon links + Textblock) | 0,5 |
| K2 (Layout-Tests)          | 0,5 |
| K3 + Abnahme               | 0,5 |

 

**Status: Abgenommen** (Release-Build 1.1.4, Emulator API 37):
1. Pixel-Check auf 2 Formaten live (2400x1080 nativ und
   1920x1080 per wm size, Menue blieb ueber den Formatwechsel
   hinweg erhalten): 0 helle Pixel in den Spalten unterhalb der
   Button-Unterkanten (vorher ragte die Kostenzeile 10 px unter
   die 88-px-Grenze), Icon-Spalte links je Button belegt (331
   Icon-Sampler je Format), Name + Kosten vollstaendig im
   Button-Inneren (350 Text-Sampler). Das dritte Format
   (2560x1440) ist per Layout-Test gesichert (K2).
2. 257 Unit-Tests (1 neu: buildMenuTextStaysInsideButtonBounds-
   OnAllFormats auf 3 Formaten), 0 Fehler. Bestehende Flow- und
   Collapse-Tests unveraendert gueltig (Button-Rechtecke und
   Hit-Test unangetastet).
3. Perf-Soak 9,9 min (Wellen 1-3 mit Siegen, Menue-Wechsel im
   Fenster): avg 0,60 ms/Frame gesamt, Regelbetrieb ohne das
   Resize-Fenster 0,59 ms. Einziges overBudget-Fenster (7 Frames,
   48,1-ms-Spike) ist das erste Messfenster exakt auf dem
   wm-size-reset (Surface-Neuaufbau) - kein Spielereignis,
   gleiche Kategorie wie die bekannten Welt-Reset-Spikes;
   117/118 Fenster overBudget=0, 0 FATAL / 0 ANR, PID konstant.
4. Version 1.1.4 (versionCode 11), gleicher Keystore, APK
   123.170 Bytes (1.1.3: 123.214; -44 Bytes - der Icon-vor/nach-
   Layout-Zweig entfiel zugunsten eines einzigen Textblocks).

 

---

 

# Version 1.1.5 – Feind-Variationen (rein kosmetisch) (nach 1.1.4)

 

Die Angreifer sehen aktuell alle gleich aus (eine Silhouette je
Normal-/Alpha-Typ). Sie sollen weitere Designs erhalten - Varianten
anderer Meeresbewohner (z. B. andere Fisch-/Qualle-/Krebs-Silhouetten
und Farbstiche), rein kosmetisch: KEINE Unterschiede in Schaden,
Lebenspunkten, Tempo oder Verhalten. Die Variante wird dem Angreifer
deterministisch zugewiesen (stabil pro Entitaet, kein Flackern beim
Neustart des Frames).

 

## Aufgaben 1.1.5

 

- [x] **F1 - Varianten-Sprites (programmatisch, M7-Stil):**
      Sprites.attacker um 2-3 zusaetzliche Silhouetten erweitern
      (vorgerenderte Bitmaps im bekannten Stil, keine Assets) -
      z. B. Stachelfisch, Qualle, Krebs; Alpha (Riesenkrake) bleibt
      exklusiv. Farbliche Unterscheidung dezent (Tiefsee-Palette),
      Lesbarkeit im Kampf erhalten (Umriss heller als Hintergrund).
      Umsetzung: 3 Varianten - 0 Rochen (unveraendert), 1 Leucht-
      qualle (warmorange Glocke mit 4 Tentakeln, 255,112,67), 2
      Panzerkrebs (rotbrauner Carapax mit Scheren/Beinen,
      216,67,21); alle 64 px vorgerendert in Sprites.ensure(),
      Auswahl ueber Sprites.attackerVariant(index). Kreis-Fallback
      je Variante mit eigener Farbe (reine ARGB-Konstanten - die
      erste Version mit Color.rgb im companion sprengte die
      Klassen-Initialisierung im JVM-Rig, per Test gefunden und
      behoben).
- [x] **F2 - Deterministische Zuweisung:**
      Attacker bekommt ein variant-Feld (Index), zugeweisen beim
      Wellen-Spawn (deterministisch, z. B. abwechselnd/aus Hash des
      Spawns - dokumentierte Regel), Persistenz nicht noetig (Varianten
      sind rein visuell, kein Save-Format-Bump: Angreifer sind nicht
      save-wuerdig, vgl. M4.5). draw() waehlt das Sprite nach variant.
      Umsetzung: Attacker.variant (Konstruktor-Parameter, Default 0);
      dokumentierte Regel: abwechselnd je Angreifer-Index innerhalb
      der Welle (i % ATTACKER_VARIANT_COUNT, jede Welle startet bei
      0). Krake und Eroberungs-Gruppen unberuehrt (Alpha exklusiv,
      Eroberungs-Angreifer bleiben Rochen).
- [x] **F3 - Kosmetik-Garantie + Tests:**
      Unit-Tests: Kampf-Werte (HP, DPS, Panzerung, Tempo) unabhaengig
      von variant; Wellen-Statistik (Anzahl/HP-Skalierung) unveraendert
      egal welche Varianten fallen; Zuweisungs-Regel deterministisch
      (gleicher Spawn -> gleiche Variante, alle Varianten kommen vor).
      Perf: eine drawBitmap je Angreifer wie bisher, Vorgerenderung
      einmalig (Sprites.ensure).
      Umsetzung: 3 Tests in AttackerTest - combatValuesAreIdentical-
      AcrossVariants, waveVariantRotationIsDeterministicAndComplete
      (Wellen 1-4 inkl. Kraken-Welle 3: Rotation startet je Welle
      bei 0, Welle 4 deckt 0/1/2 ab), variantDoesNotChangeReefDamage
      (identischer Riff-Schaden je Variante).

 

## Abnahme-Kriterium 1.1.5

 

**Fertig wenn:**
1. Emulator: in einer Welle mit >= 5 Angreifern sind >= 2 deutlich
   unterscheidbare Designs erkennbar (Pixel-Check auf Farbwerte der
   Silhouetten), Alpha-Krake unveraendert.
2. Alle Unit-Tests gruen inkl. F3-Tests; Kampfbalance nachweislich
   unveraendert (gleiche Wellen-HP/DPS wie vor 1.1.5).
3. Perf-Soak 10 min ohne Regression (avg <= 0,7 ms, 0 FATAL/ANR).
4. Version 1.1.5 (versionCode 12), signiert, APK-Groesse dokumentiert.

 

## Aufwand 1.1.5
| Aufgabe | Aufwand |
|---------|---------|
| F1 (Sprites)   | 1 |
| F2 (Zuweisung)  | 0,5 |
| F3 + Tests + Abnahme | 0,5 |

 

**Status: Abgenommen** (Release-Build 1.1.5, Emulator API 37):
1. Live verifiziert (Welle 4, 3 Angreifer, Pixel-Diff zweier
   Screenshots im 2,5-s-Abstand gegen die Bewegungs-Farben): drei
   getrennte bewegte Farb-Cluster - Qualle-Orange (255,112,67,
   16 Sampler, eindeutig getrennt vom Spektrum der Rot-Toene),
   Rochen-Rot (68) und Krebs-Rotbraun (72); die Rot-Filter
   ueberlappen teils, die Qualle ist der saubere Trennbeweis.
   Die Zuweisung (Rotation 0/1/2 je Angreifer-Index) ist per
   Unit-Test deterministisch gesichert (Welle 4 deckt alle 3 ab,
   jede Welle startet bei 0, Krake unberuehrt).
2. 260 Unit-Tests (3 neu), 0 Fehler. Kampfbalance unveraendert:
   Riff-Schaden je Variante identisch, Wellen-HP/-DPS-Skalierung
   unberuehrt (per Test), Wellen-Siege im Soak zeitlich un-
   veraendert.
3. Perf-Soak 9,8 min: avg 0,62 ms/Frame, overBudget 0 Fenster
   (bestwertig), worst Einzelframe 11,6 ms (Wellen-Fenster),
   0 FATAL / 0 ANR, PID konstant. Eine drawBitmap je Angreifer
   wie bisher, Sprites einmalig vorgerendert.
4. Version 1.1.5 (versionCode 12), gleicher Keystore, APK
   123.862 Bytes (1.1.4: 123.170; +0,7 KB durch zwei zusaetzliche
   vorgerenderte 64-px-Sprites).

 

---

 

# Version 1.1.6 – Game-Over: Weiterspielen oder Neustart (nach 1.1.5)

 

Der Game-Over-Screen bietet aktuell nur einen impliziten Neustart
(Tap irgendwo -> Neustart-Runde mit Teil-Ressourcen). Der Spieler
soll bewusst entscheiden koennen: mit dem AKTUELLEN Stand weiter-
spielen (heutiges restartRound: Gebaeude und Rudel bleiben, Res-
sourcen halbiert) ODER komplett von Anfang an (gelten als Neues
Spiel: gebaute Gebaeude und gesammelte Ressourcen werden zuerueck-
gesetzt, Kampagne startet frisch).

 

## Aufgaben 1.1.6

 

- [x] **W1 - Game-Over-Panel mit zwei Wegen:**
      drawGameOverPanel um zwei Buttons erweitern: "Weiterspielen
      (Teil-Ressourcen)" -> restartRound() und "Neues Spiel" ->
      Bestaetigungs-Overlay (gleiche Sicherheit wie das Menue-
      Pendant 1.0.2/U4) -> resetToNewGame() + Rundenstart. Die
      Statistik (Wellen, Gebaeude, Rudel, XP, 1.0.2/U5) bleibt.
      Der freie Tap-auf-dem-Bildschirm-Neustart (heutiges Verhalten,
      auch szenenunabhaengig seit 1.1.3) wird durch die Button-
      Steuerung ersetzt - Taps ausserhalb der Buttons machen nichts
      (Fehl-Tap-Schutz, vgl. 1.0.2/U3-Disziplin).
      Umsetzung: gameOverContinueButton (hoehe*0,72) / gameOverNew-
      GameButton (0,84) wie Menue-Buttons (resize); handleGameOver-
      Press vor den Szenen-Guards (1.1.3-Stelle bleibt); gameOver-
      Confirm-Flag mit eigenem Overlay-Aufruf; Statistik + Kurz-
      hinweis "Weiterspielen haelt Gebaeude und Rudel ..." beibehalten.
      Das Bestaetigungs-Overlay ist aus drawTitleOverlay extrahiert
      (drawNewGameConfirmOverlay, Menue + Game Over nutzen dieselbe
      Flaeche/Buttons).
- [x] **W2 - resetToNewGame als Game-Over-Pfad:**
      Der Reset nutzt die bestehende Neues-Spiel-Logik (resetToNewGame:
      kompletter Reset inkl. Sektor-Karte, Nebel, Rudel, Instinkten,
      Tutorial, sofortiger Rundenstart + Save). Pruefen: nach dem
      Reset muss das Save konsistent sein (frischer Stand ueberlebt
      Kaltstart), Instinkt-Punkte und XP auf 0, Standard-Rudel aktiv.
      Umsetzung: resetToNewGame raeumt zusaetzlich gameOverConfirm;
      gameOver() startet ohne offene Bestaetigung. Reset-Invarianten
      (Startwerte, leere Gebaeude-Liste, Standard-Rudel, Welle 0) per
      Unit-Test gesichert.
- [x] **W3 - Tests + Abnahme:**
      Unit-Tests: Tap "Weiterspielen" -> restartRound-Pfad (halbierter
      Ressourcen, Gebaeude bleiben), Tap "Neues Spiel" + Bestaetigung
      -> Startwerte + Standard-Rudel + leere Karte, Tap ausserhalb
      aendert nichts, Game Over in Karte/Instinkt-Szene (1.1.3-Fix)
      zeigt das Panel ebenfalls mit beiden Buttons. Emulator-Abnahme:
      beide Pfade live (Logcat + Save-Pruefung), Layout auf 3 Formaten
      (UiLayoutTest).
      Umsetzung: 4 neue Tests in NewBuildingsTest (Continue haelt
      Gebaeude/halbiert Ressourcen; Neues Spiel nur mit Bestaetigung
      und komplett frisch; Fehl-Tap macht nichts; Abbrechen kehrt zur
      Auswahl zurueck und Weiterspielen funktioniert danach). 5 Alte-
      Tests auf den Weiterspielen-Button umgestellt (Combat-, Sector-
      Game-, NewBuildings-), da der freie Tap-Neustart bewusst ent-
      fiel.

 

## Abnahme-Kriterium 1.1.6

 

**Fertig wenn:**
1. Game-Over-Screen bietet beide Wege mit klar beschrifteten Buttons;
   beide live auf dem Emulator verifiziert (Weiterspielen haelt
   Gebaeude, Neues Spiel resettet komplett - Spielstand nach
   Kaltstart geprueft).
2. Alle Unit-Tests gruen inkl. W3-Tests (beide Pfade, Fehl-Tap-
   Schutz, Szenen-Fall).
3. Perf-Disziplin gehalten (Panel zeichnet nur im GAME_OVER,
   Buttons statisch): 10-Min-Soak ohne Regression, 0 FATAL/ANR.
4. Version 1.1.6 (versionCode 13), signiert, APK-Groesse dokumentiert.

 

## Aufwand 1.1.6
| Aufgabe | Aufwand |
|---------|---------|
| W1 (Panel + Buttons) | 0,5 |
| W2 (Reset-Pfad)       | 0,5 |
| W3 + Tests + Abnahme  | 1 |

 

**Status: Abgenommen** (Release-Build 1.1.6, Emulator API 37):
1. Beide Pfade live verifiziert: Game Over (Welle 1 der frischen
   Kampagne nach Menu-Reset) -> Panel zeigt WEITERSPIELEN +
   NEUES SPIEL (Pixel-Check: 147 Akzent-Button-Sampler in den
   beiden Button-Baendern). Tap ausserhalb: kein Neustart-Log,
   Zustand unveraendert (Fehl-Tap-Schutz). NEUES SPIEL -> dunkles
   Bestaetigungs-Overlay mit JA/ABBRECHEN (Pixel-Check) -> JA:
   "Neues Spiel: Spielstand zurueckgesetzt" (kompletter Reset aus
   dem Game-Over-Pfad). Zweiter Game Over -> WEITERSPIELEN:
   "Neustart-Runde: Teil-Ressourcen, Welle zurueckgesetzt".
   Der ABBRECHEN-Weg ist per Unit-Test gesichert (der Live-Pixel-
   Check konnte das halbtransparente Overlay nicht sicher vom
   dunklen Tiefsee-Grund trennen - ehrliche Grenze dokumentiert).
   Regression: der Menue-Pendant (Neues Spiel aus dem Hauptmenue)
   funktioniert nach der Overlay-Extraktion unveraendert (live:
   Reset um 14:42 Uhr).
2. 265 Unit-Tests (5 neu: 4 Auswahl-Flows + 1 Layout-Test auf
   3 Formaten; 5 Alt-Tests auf den Button umgestellt),
   0 Fehler. Szenen-Fall (Game Over in der Karten-Szene, Weiter-
   spielen-Button) im Test aktualisiert und gruen.
3. Perf-Soak 9,9 min (inkl. 1 Game Over mit Panel-Draw): avg
   0,59 ms/Frame, overBudget 0 Fenster, worst Einzelframe 8,6 ms,
   0 FATAL / 0 ANR, PID konstant.
4. Version 1.1.6 (versionCode 13), gleicher Keystore, APK
   124.090 Bytes (1.1.5: 123.862; +0,2 KB durch Auswahl-Panel +
   extrahiertes Overlay).

 

---

 

# Version 1.1.7 – Rotation verhindern (nach 1.1.6)

 

Das Spiel soll nicht rotierbar sein: Das Spielfeld bleibt immer in
Landschaft, keine Drehung des Bildes - weder per System-Rotation in
Portrait noch zwischen Landschafts-Varianten (Sensor/180-Grad). Das
Manifest ist bereits landscape-locked (screenOrientation="landscape",
Portrait blockiert); verbleibende Luecken sind: die Activity kann bei
Konfigurations-Ereignissen (Rotationssystemdrehung, Foldables, Display-
Wechsel) neu erzeugt werden und der zustandslose Neuaufbau ist nur
durch das robuste Save abgefedert. Die Layouts bleiben weiten-relativ
(keine feste Aufloesung - der Letterbox-Teil wurde aus dieser Version
entfernt).

 

## Aufgaben 1.1.7

 

- [x] **R1 - Rotationssperre verhärten:**
      Manifest: screenOrientation="landscape" beibehalten (kein
      sensorLandscape - keine Sensor-Drehung zwischen 90/270) und
      die Activity gegen Konfigurations-Ereignisse unempfindlich
      machen: android:configChanges="orientation|screenSize|
      screenLayout|smallestScreenSize|keyboardHidden" ergaenzen,
      damit Rotationssystem-Ereignisse die Activity nicht neu
      erzeugen (Game-Zustand bleibt im Speicher, kein Save/Load-
      Zyklus noetig). Pruefen: onBackPressed/OnBackInvokedCallback
      (1.0.2/U2) und Predictive Back funktionieren nach der
      configChanges-Aenderung unveraendert.
      Umsetzung: configChanges war bereits teilweise gesetzt
      (orientation|screenSize|keyboardHidden) - ergaenzt um
      screenLayout|smallestScreenSize (Foldables/Display-Wechsel).
      Live-Regression: Back oeffnet die Pause (FORTSETZEN-Akzent per
      Pixel-Check), zweiter Back resumed - OnBackInvokedCallback
      unbeeinflusst (keine Recreation mehr, Callback bleibt
      registriert).
- [x] **R2 - Rotationstest auf dem Emulator:**
      Live-Verifikation: System-Rotation aktivieren (settings put
      system accelerometer_rotation 1) und Portrait-/Reverse-
      Anforderung per wm user-rotation senden - das Spielfeld darf
      sich nicht drehen (Screenshot-Vergleich: Layout bleibt
      Landschaft). Activity-Recreation ausschliessen (Game-Logger
      zeigt keinen erneuten Welt-Aufbau/PID bleibt). Danach Rotation
      zuruecksetzen. Unit-Tests: keine Rotation verfaelscht Game-
      Zustand (Game.resize bleibt die einzige Layout-Quelle; rein
      JVM-pruefbar, dass keine weitere Abhaengigkeit existiert).
      Perf-Soak 10 min ohne Regression.
      Umsetzung: Portrait-Anforderung (user_rotation 0) und Reverse-
      Landschaft (3) - Spielfeld bleibt 2400x1080, PID konstant,
      nur 1x "Intro gestartet" im Log (keine Recreation), 5 stabile
      HUD-Punkte pixel-identisch zum Baseline-Screenshot (kein 180-
      Grad-Flip). Rotation zurueckgesetzt (user_rotation 1,
      accelerometer_rotation 0). Hinweis: "wm set-fix-to-user-
      rotation" existiert auf dem Emulator-API nicht - die Settings-
      Variante (user_rotation) fuehrt die Rotation durch.

 

## Abnahme-Kriterium 1.1.7

 

**Fertig wenn:**
1. Rotation: Portrait bleibt verhindert; auch aktivierte System-
   Rotation mit Portrait-/Reverse-Anforderung dreht das Spielfeld
   nicht (Screenshot-Vergleich), die Activity wird dabei nicht neu
   erzeugt (kein neuer Welt-Aufbau im Logcat).
2. Spielverhalten unveraendert zu 1.1.6 (Weiterspielen/Neues Spiel,
   Back-Handling inkl. Predictive Back nach configChanges).
3. Alle Unit-Tests gruen; Perf-Soak avg <= 0,7 ms, 0 FATAL/ANR.
4. Version 1.1.7 (versionCode 14), signiert, APK-Groesse
   dokumentiert.

 

## Aufwand 1.1.7
| Aufgabe | Aufwand |
|---------|---------|
| R1 (Manifest + configChanges) | 0,5 |
| R2 + Rotationstest + Abnahme  | 0,5 |

 

**Status: Abgenommen** (Release-Build 1.1.7, Emulator API 37):
1. Rotation live verifiziert: Portrait- und Reverse-Landschafts-
   Anforderung (user_rotation 0/3) drehen das Spielfeld nicht
   (bleibt 2400x1080 Landschaft, 5 stabile HUD-Punkte pixel-
   identisch - kein 180-Grad-Flip), PID konstant, nur 1x
   "Intro gestartet" - keine Activity-Recreation. Back-Regression:
   Pause per Back (Pixel-Check FORTSETZEN) und Resume per zweitem
   Back unveraendert nach der configChanges-Erweiterung.
2. 265 Unit-Tests unveraendert gruen (Manifest-Aenderung ohne
   Logik-Beruehrung; ScreenOrientierung war bereits
   landscape-locked).
3. Perf-Soak 10,3 min: avg 0,63 ms/Frame (Ziel <= 0,7), 0 FATAL /
   0 ANR, PID konstant - ABER: der Emulator war in einem degradierten
   Zustand (surfaceflinger flutet ~26-100 skia-"AGTM parsing
   failed"-Warnungen pro Sekunde, auch im Idle ohne App; 42k Zeilen
   im Soak-Fenster). 28/124 Fenster mit Budget-Ueberschreitungen
   (Spikes bis 72 ms) fallen damit zusammen; ein Cold Boot (2x,
   einmal mit wipe-data, einmal Software-Rendering) behob es diesmal
   NICHT - anders als beim gleichartigen Vorfall in 1.0.1. Der
   App-eigene Arbeitswert (avgWorkMs) bleibt im Ziel; fuer eine
   saubere overBudget-Messung empfiehlt sich ein Host-Neustart und
   ein kurzer Re-Soak.
4. Version 1.1.7 (versionCode 14), gleicher Keystore, APK
   124.090 Bytes (identisch zu 1.1.6 - nur Manifest-Attribut-
   Erweiterung, kein Zuwachs im DEX).

 

---

# Version 1.2.0 – Highscore (nach 1.1.7)

Ein Bestenliste-System: Nach dem Ende einer Kampagne (Game Over ->
Neues Spiel) wird aus dem erreichten Stand ein Punktwert berechnet
und zusammen mit einem selbst gewaehlten 10-stelligen alphanume-
rischen Namen gespeichert. Das Hauptmenue zeigt die letzten 10
Eintraege. Weitergespielte Runden (Game Over -> Weiterspielen)
zaehlen NICHT als beendete Kampagne und speichern nichts.

## Score-Berechnung (Gewichtung nach Seltenheit)

Alle Gewichte zentral in GameConfig, Berechnung als pure Funktion
(deterministisch, Unit-testbar):

| Komponente | Punkte | Begruendung |
|---|---|---|
| Korallen | x 1 | Grundressource, laufend produziert |
| Biomasse | x 2 | Jagd-Aufwand, wird vom Rudel verzehrt |
| Kalkstein | x 3 | Gilde noetig, langsamer Zuwachs |
| Biolumineszenz | x 5 | Tiefsee-Spalte, seltene Quelle |
| Wrackteile | x 10 | Fundstuecke, spawnen selten (max 2) |
| je ueberstandene Welle | + 50 | Kampf-Leistung, skaliert mit Welle 14+ |
| je Gebaeude | + Stufe x 25 | Ausbau: St.2 = 50, St.3 = 75 |
| je erobertem Sektor | + 100 | Expansions-Erfolg (8 moeglich) |

## Aufgaben 1.2.0

- [x] **H1 - Score-Funktion (GameConfig + pure Logik):**
      HighscoreScore als reine Funktion (Ressourcen, Wellen,
      Gebaeude-Liste mit Stufen, eroberte Sektoren -> Punkte);
      Gewichte als GameConfig-Konstanten. Der Punktwert wird zum
      Game-Over-Zeitpunkt eingefroren (Snapshot, keine Nachtra-
      glichkeit durch Restart-Ressourcen-Halbierung).
- [x] **H2 - Persistenz (max. 10 Eintraege):**
      HighscoreStore: JSON-Liste in SharedPreferences (eigener
      Schluessel, nicht im Save-Format der Kampagne - Highscores
      ueberleben einen Neuen-Spiel-Reset). Sortiert absteigend,
      nur die besten 10 bleiben. Felder: Name (max. 10 Zeichen,
      alphanumerisch), Punkte, Wellen, Version. Test: Roundtrip,
      Abschneiden bei >10, Sortierung, Alt-Save-Toleranz.
- [x] **H3 - Namenseingabe nach "Neues Spiel" im Game Over:**
      Nach der Bestaetigung (JA, NEU ANFANGEN) oeffnet sich die
      Eingabe: System-Dialog (AlertDialog + EditText) statt eigener
      Canvas-Tastatur - 10 Zeichen Limit, Filter auf alphanumerisch,
      Abbrechen = Eintrag ohne Name ("UNBEKANNT"). Erst nach Dialog-
      Ende laeuft resetToNewGame weiter (Score vorher einfrieren).
- [x] **H4 - Highscore-Anzeige im Hauptmenue:**
      Menue-Button "Highscore" oeffnet eine Szene/Overlay (wie
      Info/Options, 1.0.4-Stil): Liste der bis zu 10 Eintraege
      (Rang, Name, Punkte), leer-State ("Noch keine Eintraege"),
      Back/Tap zurueck ins Menue. Layout weiten-relativ (UiLayout-
      Test auf 3 Formate).
- [x] **H5 - Flow-Regeln + Tests:**
      Game Over -> WEITERSPIELEN: KEIN Highscore-Eintrag (Runde
      laeuft weiter). Game Over -> NEUES SPIEL (+Bestaetigung):
      Eintrag mit eingefrorenem Score. Unit-Tests: Score-Mathe
      gegen Konzept-Tabelle, Einfrieren zum Game-Over-Zeitpunkt,
      kein Eintrag bei Weiterspielen, Eintrag bei Neues Spiel,
      Persistenz-Roundtrip, Sortierung/Schnitt bei 10.

## Abnahme-Kriterium 1.2.0

**Fertig wenn:**
1. Live: Game Over -> Neues Spiel -> Namenseingabe -> Eintrag
   erscheint in der Hauptmenue-Highscore (Rang/Name/Punkte);
   Game Over -> Weiterspielen erzeugt keinen Eintrag.
2. Score nachvollziehbar: im Logcat dokumentierter Score-Bruchteil
   (Ressourcen-/Wellen-/Gebaeude-/Sektoren-Anteil) gegen den
   Spielstand vor dem Game Over.
3. Persistenz: Highscore ueberlebt App-Neustart und Neues-Spiel-
   Reset (Kaltstart verifiziert); genau 10 Eintraege maximal,
   sortiert.
4. Alle Unit-Tests gruen (inkl. H5); Layout auf 3 Formaten;
   Perf-Soak ohne Regression (avg <= 0,7 ms, 0 FATAL/ANR) - bei
   weiterhin degradiertem Emulator Host-Neustart zuerst.
5. Version 1.2.0 (versionCode 15), signiert, APK-Groesse
   dokumentiert.

## Aufwand 1.2.0
| Aufgabe | Aufwand |
|---------|---------|
| H1 (Score-Funktion)      | 0,5 |
| H2 (Persistenz)          | 0,5 |
| H3 (Namenseingabe)       | 1   |
| H4 (Menue-Anzeige)       | 1   |
| H5 + Tests + Abnahme     | 1   |


**Status: Abgenommen** (Release-Build 1.2.0, Emulator API 37):
1. Live verifiziert: Game Over friert den Punktwert ein (Logcat:
   "Highscore-Punkte: 12117" bei Welle 11). NEUES SPIEL -> Bestae-
   tigung -> Namens-Dialog (System-AlertDialog, im Emulator-Test
   dunkles Theme) -> OK: "Highscore gespeichert: UNBEKANNT (12117
   Punkte)" + "Neues Spiel: Spielstand zurueckgesetzt". WEITER-
   SPIELEN: "Neustart-Runde"-Log, KEIN Eintrag (Log-Count 0).
   Hauptmenue-Button "HIGHSCORE" oeffnet die Liste (Eintrag per
   Pixel-Check sichtbar, 52 Text-Sampler), Bestaetigung der
   Persistenz: Kaltstart (force-stop + Neustart) zeigt den Eintrag
   unveraendert; zwei weitere Eintraege (546/369 Punkte) folgten im
   Ablauf und erschienen sortiert. Grenze der Live-Abnahme: das
   Eintippen eines Namens per adb-Text-Injektion erreicht das
   EditText-Feld ueber den Emulator-IME nicht (Feld blieb leer ->
   "UNBEKANNT"); der benannte Pfad ist per Unit-Test mit injizier-
   tem Dialog gesichert ("Riffkoenig" -> "RIFFKOENIG" mit ein-
   gefrorenem Punktwert).
2. 277 Unit-Tests (12 neu: HighscoreTest - Konzept-Tabelle 1090
   Punkte, Einfrieren vor Restart-Halbierung, kein Eintrag bei
   Weiterspielen, benannter Eintrag bei Neues Spiel, Abbruch =
   UNBEKANNT, Namens-Normalisierung, Roundtrip/Sortierung/10er-
   Grenze/Verdraengung, korrupter Store, Menue-Szenen-Zugang,
   Menue-Button-Layout auf 3 Formaten), 0 Fehler.
3. Perf-Soak 10,2 min: avg 0,74 ms - ABER erneut im degradierten
   Emulator-Zustand (surfaceflinger-skia-Flut: 47k Zeilen im
   Fenster, schon fruehe Fenster 0,78 ms; zweiter Cold-Boot brachte
   keine Besserung: 835 skia-Zeilen direkt nach Boot). 0 FATAL /
   0 ANR, PID konstant. Design-Argument: KEIN 1.2.0-Code liegt im
   Frame-Pfad (Score nur zum Game Over, Liste beim Oeffnen gecacht
   + Zeilen-Cache, Store-Schreiben einmalig je Eintrag, Dialog ist
   System-UI). Fuer eine saubere avg-Zahl bleibt der Host-Neustart
   empfohlen (vgl. 1.1.7-Dokumentation).
4. Version 1.2.0 (versionCode 15), gleicher Keystore, APK 127.562
   Bytes (1.1.7: 124.090; +3,5 KB durch Highscore/HighscoreScene/
   Namens-Dialog und SaveGameStore-Erweiterung).