Files
Jander_Semester2/Semesterprojekt/docs/superpowers/specs/2026-05-31-endings-design.md
Jean-Luc Makiola 0f40ee637d docs: spec for win-condition & endings (sub-project 3.3)
Priority-ordered condition-driven endings + an end-of-game summary (turns,
quests X/Y, rank). EndingEngine mirrors the quest engine; Game ends the loop
when an ending fires. endings.yaml optional; World gains 5/6/7-arg back-compat
constructors. Two reachable demo endings (victory, flee) with no new mechanics.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 22:44:01 +02:00

4.6 KiB
Raw Blame History

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)

public record Ending(String id, String title, boolean victory,
                     List<Condition> when, String text) { /* when: List.copyOf */ }

endings.yaml (geordnete Liste, erstes passendes gewinnt):

- 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():

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).