# 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<Condition> 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<ConditionDto> when, String text)`.
- `EndingFactory.fromDto` baut `Ending` (`when` via `ConditionDto.toModelList`).
- `WorldLoader` liest `endings.yaml` **optional** (`readListOptional`), baut eine
  **geordnete** `List<Ending>` (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).
