# Spec: Win-Condition & Enden (Teilprojekt 3.3) Stand: 2026-05-31. Capstone des Quest-Systems. Baut auf Fundament (3.1) + Quests (3.2) auf: Conditions/Effects/Flags + QuestLog sind vorhanden. ## 1. Kontext & Ziel Das Spiel endet bisher nur per `quit`. Ziel: **mehrere, priorisierte Enden**, condition-driven (Ansatz wie Quests), plus ein **End-Screen mit Zusammenfassung** (Züge, abgeschlossene Quests, Rang). Bestätigt: **A mit Summary**. ## 2. Scope **In Scope:** - `Ending`-Datenmodell + optionale `endings.yaml` (Reihenfolge = Priorität). - `EndingEngine` (erstes passendes Ende ermitteln + End-Screen rendern). - `Game`-Anbindung: pro Zug nach dem Quest-Tick prüfen; bei Treffer Ende + Summary ausgeben und `stop()`. - End-Summary: Züge, Quests X/Y, Rang. - Loader: `EndingDto`, `EndingFactory`, `World.endings`. - Demo: zwei spielbar erreichbare Enden ohne neue Mechanik. **Out of Scope:** Scoreboard/Persistenz; zugbasierte Conditions; Spielstand-Reset. ## 3. Datenmodell (model) ```java public record Ending(String id, String title, boolean victory, List when, String text) { /* when: List.copyOf */ } ``` `endings.yaml` (geordnete Liste, erstes passendes gewinnt): ```yaml - id: victory title: "The Manor Reclaimed" victory: true when: [{ flag: manor_secured }] text: | The lights hold steady, the whispers fade. You've made the manor yours. - id: fled title: "Into the Night" victory: false when: [{ flag: fled }] text: | You bolt through the front door. Safe — but the manor keeps its secrets. ``` ## 4. Engine (game) - `EndingEngine.triggered(GameContext ctx)` → erstes `Ending`, dessen `when` via `Conditions.all` gilt, sonst `null`. Rein, testbar (Priorität/erster Treffer). - `EndingEngine.render(Ending e, GameContext ctx, int turns)` → `StyledText`: Titel-Banner + `text` + Summary. - **Summary**: `Turns: N`, `Quests completed: X / Y` (`X = questLog.completed().size()`, `Y = world.getQuests().size()`), plus **Rang**: - victory & alle Quests → „Master of the Manor" - victory → „Manor Reclaimed" - sonst → „Escaped — the manor keeps its secrets" ## 5. Game-Anbindung In `Game.run` nach `publishHud()` (Initial **und** pro Schleifendurchlauf) ein `maybeEnd()`: ```java private void maybeEnd() { Ending e = EndingEngine.triggered(ctx); if (e != null) { ctx.getIo().print(EndingEngine.render(e, ctx, turn)); stop(); } } ``` `stop()` setzt `running=false`; die Schleife endet sauber, kein zusätzlicher Prompt. Konsole: Programm endet. GUI: Worker-Thread endet, Fenster bleibt mit End-Screen. ## 6. Loading - `EndingDto(id, title, Boolean victory, List when, String text)`. - `EndingFactory.fromDto` baut `Ending` (`when` via `ConditionDto.toModelList`). - `WorldLoader` liest `endings.yaml` **optional** (`readListOptional`), baut eine **geordnete** `List` (Reihenfolge der YAML-Liste), `requireUniqueIds("ending", …)`. - `World` bekommt Feld `endings` (List) + **rückwärtskompatible 5- und 6-Arg-Konstruktoren** (leere Quests/Endings), damit bestehende `new World(...)`- Aufrufe (Tests, bisheriger Loader-Pfad) unverändert kompilieren. Der Loader nutzt den vollen 7-Arg-Konstruktor. ## 7. Fehlerbehandlung | Fall | Verhalten | |---|---| | `endings.yaml` fehlt | leere Liste → Spiel endet nur per `quit` (wie bisher) | | mehrere Conditions treffen | erstes Ende in Listenreihenfolge gewinnt | | Ending ohne `when` | trifft sofort (leere Condition-Liste = true) – Autoren sollten Reihenfolge beachten | | keine Quests definiert | Summary zeigt `0 / 0`, Rang nach victory-Flag | ## 8. Demonstration (zwei erreichbare Enden, keine neue Mechanik) - **victory**: `manor_secured` wird gesetzt, wenn die `restore_power`-Quest endet (bereits vorhanden) → Sieg-Ende. - **fled**: neuer schaltbarer Gegenstand **Front Door** (`effects: setFlag fled`) in der Küche → Flucht-Ende. `victory` steht vor `fled`, also schlägt das Sichern der Villa die Flucht. ## 9. Testing - `EndingEngine.triggered`: Priorität/erster Treffer; kein Treffer → `null`. - `EndingEngine.render`: enthält Titel, Text, Züge, Quests-Zähler, Rang. - `EndingFactory.fromDto`: Mapping inkl. victory/when/text. - End-to-End (Konsole): Villa sichern → Sieg-Ende + Summary, Spiel endet; alternativ Front Door benutzen → Flucht-Ende. - `Game`-Integration: nicht separat unit-getestet (Loop/IO); über Konsolen-Smoke. ## 10. Offene Detailfragen (in Implementierung) - Genaue Banner-Optik des End-Screens (HEADING-Rahmen). - Rang-Schwellen (zunächst nur victory + alle-Quests-Abfrage).