diff --git a/.gitignore b/.gitignore index e327c84..4952917 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,3 @@ Thumbs.db # Nextcloud sync .sync-exclude.lst - -# Build artifacts (kept on disk for the submission ZIP, not in git) -release/*.jar diff --git a/Semesterprojekt/.gitignore b/Semesterprojekt/.gitignore index 7b46e53..7b5e9a1 100644 --- a/Semesterprojekt/.gitignore +++ b/Semesterprojekt/.gitignore @@ -45,3 +45,6 @@ saves/ # src/main/resources/music/ is tracked and bundled. Anchored so it # ignores the top-level source folder but not the resources path. /music/ + +# Built jars (kept on disk for the submission ZIP, not tracked) +release/*.jar diff --git a/Semesterprojekt/docs/README.md b/Semesterprojekt/docs/README.md index 5112969..0d7d32f 100644 --- a/Semesterprojekt/docs/README.md +++ b/Semesterprojekt/docs/README.md @@ -1,33 +1,36 @@ # Semesterprojekt – Textadventure -Design- und Architekturdokumentation. Dient als Spec während der Implementierung. +Design- und Architekturdokumentation. Bedienung und Build stehen im +[Haupt-README](../README.md). ## Inhalt | Datei | Inhalt | |---|---| | [architecture.md](architecture.md) | Package-Struktur, Schichten, DTO-vs-Domain-Trennung | -| [conventions.md](conventions.md) | Sprache, ID-Format, Naming, Lombok-Cheatsheet | | [data-structures.md](data-structures.md) | Alle gewählten Collection-Typen mit Begründung | +| [conventions.md](conventions.md) | Sprache, ID-Format, Naming, Lombok-Cheatsheet | | [item-model.md](item-model.md) | Item-Hierarchie (abstract + 3 Subtypen), Lombok-Inheritance | -| [yaml-schemas.md](yaml-schemas.md) | Schemas für `items.yaml`, `rooms.yaml`, `npcs.yaml`, `game.yaml` | -| [loading-flow.md](loading-flow.md) | Lade-Reihenfolge, Referenz-Auflösung, Validierung | | [commands.md](commands.md) | Befehlsparser, Command-Pattern, Befehlsliste | | [npcs.md](npcs.md) | NPC-Modell, Talk- und Give-Interaktion | -| [implementation-status.md](implementation-status.md) | Aktueller Stand, Phasen-Checkliste, festgelegte Entscheidungen | +| [yaml-schemas.md](yaml-schemas.md) | Schemas der YAML-Dateien unter `resources/world/` | +| [loading-flow.md](loading-flow.md) | Lade-Reihenfolge, Referenz-Auflösung, Validierung | +| [music.md](music.md) | Hintergrundmusik in der GUI (Bonus) | ## Pflicht vs. Optional (laut Aufgabenstellung) -- **Pflicht:** ≥4 Räume mit Navigation, ≥3 Gegenstände mit Inventar -- **Optional/Bonus:** NPCs, Swing-GUI +- **Pflicht:** ≥ 4 Räume mit Navigation, ≥ 3 Gegenstände mit Inventar — + erfüllt mit 14 Räumen und 19 Gegenständen. +- **Optional/Bonus:** NPCs (3) und Swing-GUI — beides umgesetzt. -Beide optionalen Teile sind hier eingeplant. +Welche Klasse welche Anforderung umsetzt, steht als Tabelle im +[Haupt-README](../README.md). ## Technologie-Stack -- Java 25 -- Jackson (YAML) für Daten-Loading -- Lombok für Boilerplate-Reduktion +- Java 21 (LTS) +- Jackson (YAML) für das Laden der Spielwelt +- Lombok zur Reduktion von Boilerplate - JUnit 5 + AssertJ + Mockito für Tests -- Logback + SLF4J für Logging -- Swing für GUI (Bonus) +- SLF4J + Logback für Logging +- Swing für die GUI (Bonus) diff --git a/Semesterprojekt/docs/architecture.md b/Semesterprojekt/docs/architecture.md index 7485f51..9dcda4f 100644 --- a/Semesterprojekt/docs/architecture.md +++ b/Semesterprojekt/docs/architecture.md @@ -33,19 +33,22 @@ flowchart LR model --> item item --> item_files["Item (abstract)
ReadableItem
SwitchableItem
PlainItem"] - loader --> loader_files["WorldLoader
ReferenceResolver
WorldValidator"] + loader --> loader_files["WorldLoader
ReferenceResolver
WorldValidator
*Factory"] loader --> dto - dto --> dto_files["RoomDto, ItemDto,
NpcDto, GameDto"] + dto --> dto_files["RoomDto, ItemDto,
NpcDto, GameDto,
QuestDto, EndingDto, …"] command --> cmd_core["Command (Interface)
CommandRegistry
CommandParser"] command --> impl - impl --> impl_files["GoCommand, TakeCommand,
DropCommand, UseCommand,
InventoryCommand, LookCommand,
TalkCommand, GiveCommand,
HelpCommand, QuitCommand"] + impl --> impl_files["GoCommand, TakeCommand,
DropCommand, UseCommand,
ReadCommand, ExamineCommand,
InventoryCommand, LookCommand,
TalkCommand, GiveCommand,
MapCommand, QuestsCommand,
SaveCommand, MenuCommand,
HelpCommand"] - game --> game_files["Game (Loop)
GameContext"] + game --> game_files["Game (Loop)
GameContext/Session/State
QuestEngine, EndingEngine,
EscortEngine, Light,
Pathfinder, MapLayout"] - io --> io_files["GameIO (Interface)
ConsoleIO
SwingIO"] + io --> io_files["GameIO (Interface)
ConsoleIO
SwingIO
io.text (Styled Output)"] ``` +> **Hinweis:** Es gibt **keine** `QuitCommand`-Klasse. `quit`/`exit`/`beenden` sind Aliase, +> die in `App` auf `MenuCommand` registriert werden (siehe `App.play`). + ## DTO vs. Domain-Trennung **Kernprinzip:** YAML wird in Records deserialisiert (DTOs), erst danach werden String-IDs zu Objekt-Referenzen aufgelöst. @@ -67,15 +70,15 @@ flowchart LR ## Game-Loop (vereinfacht) ```java -while (!game.isOver()) { - String input = io.read(); +while (running) { + String input = io.readLine(); + if (input == null) break; // EOF ParsedCommand parsed = parser.parse(input); - Command cmd = registry.get(parsed.verb()); - if (cmd == null) { - io.write("Unbekannter Befehl."); - } else { - cmd.execute(context, parsed.args()); - } + registry.find(parsed.verb()).ifPresentOrElse( + cmd -> cmd.execute(ctx, parsed.args()), + () -> io.write("I don't understand '" + parsed.verb() + "'.")); + publishHud(); // Engines ticken, HUD/Map/Quests updaten + maybeEnd(); // Ending-Bedingungen prüfen } ``` @@ -85,9 +88,25 @@ Konsole und GUI teilen sich `GameIO`: ```java public interface GameIO { - String read(); // blockierende Leseoperation - void write(String text); + String readLine(); // blockierende Leseoperation + void print(StyledText text); // einzige Ausgabe-Primitive + + // Convenience + Presentation-Hooks als default-Methoden: + default void write(String text) { … } // Kurzform für ungestylten Text + default void showRoom(RoomView v) { … } + default void setHud(Hud hud) { … } + default void setMap(MapView map) { … } + default void setQuests(QuestView quests) { … } + default void setMusic(String track) { … } + default int choose(String title, List options) { … } + default void shutdown() { … } } ``` -Damit ist der Game-Loop **identisch** für beide Modi. `SwingIO` blockiert intern mit einer `BlockingQueue`, die vom JTextField-ActionListener gefüllt wird. +Ausgabe läuft **immer** über `print(StyledText)` — eine Liste von `Span`s mit `Style`. +`ConsoleIO` rendert sie als ANSI-Farben (oder ASCII, je nach Settings), `SwingIO` als +`StyledDocument` im `JTextPane`. Die `default`-Methoden geben der GUI Extra-Hooks +(Karte, Quest-Panel, Musik), die die Konsole schlicht ignoriert oder als Text ausgibt. + +Damit ist der Game-Loop **identisch** für beide Modi. `SwingIO` blockiert intern mit einer +`LinkedBlockingQueue`, die vom JTextField-ActionListener gefüllt wird. diff --git a/Semesterprojekt/docs/conventions.md b/Semesterprojekt/docs/conventions.md index 573f296..c5611c8 100644 --- a/Semesterprojekt/docs/conventions.md +++ b/Semesterprojekt/docs/conventions.md @@ -32,24 +32,28 @@ Geltungsbereich: id ist eindeutig **innerhalb seiner Entitätsart**. Ein Item un ``` thb.jeanluc.adventure +├── App, AppGui <- Entry Points (Konsole / Swing) ├── model -│ ├── Direction -│ ├── Room -│ ├── Player -│ ├── Npc +│ ├── Direction, Room, Player, Npc +│ ├── Quest, QuestStage, Ending, Combination, Condition, Effect, … │ └── item <- eigener Subpackage wegen Hierarchie │ ├── Item (abstract) │ ├── ReadableItem │ ├── SwitchableItem │ └── PlainItem -├── io -├── command -├── game -└── loader - └── dto +├── io <- ConsoleIO, SwingIO, GameIO, Music*, MapPanel, QuestPanel +│ └── text <- StyledText, Style, Span, Hud, *View (15 Klassen) +├── command <- Command, CommandParser, CommandRegistry, ParsedCommand +│ └── impl <- die 15 konkreten Kommandos +├── game <- Game-Loop, Engines (Quest, Ending, Escort), Light, +│ Pathfinder, MapLayout, GameState/Session/Context +├── loader +│ └── dto <- die YAML-Records +├── save <- SaveService, SaveCodec, SaveData, Settings* +└── menu <- MainMenu, SettingsMenu, MenuAction ``` -**Wann ein Subpackage:** wenn eine Klassenfamilie ≥ 3 Klassen umfasst und eine eigene Abstraktion bildet (`item`, evtl. später `command.impl`). Sonst flach lassen. +**Wann ein Subpackage:** wenn eine Klassenfamilie ≥ 3 Klassen umfasst und eine eigene Abstraktion bildet. Erfüllt von `item` (4), `command.impl` (15), `io.text` (15), `loader.dto` (16), `save` (7), `menu` (3). Sonst flach lassen — `MapLayout` liegt deshalb in `game` neben `Pathfinder` und **nicht** in einem eigenen `map`-Package (eine Klasse reicht nicht). ## Naming diff --git a/Semesterprojekt/docs/data-structures.md b/Semesterprojekt/docs/data-structures.md index 3cff7dc..f41629d 100644 --- a/Semesterprojekt/docs/data-structures.md +++ b/Semesterprojekt/docs/data-structures.md @@ -15,13 +15,16 @@ Bewusste Wahl jeder Collection — der Dozent bewertet das laut Aufgabenstellung | Spieler-Inventar | `LinkedHashMap` | O(1) | | Befehlsregistry | `HashMap` | O(1) | | NPC-Reaktionen | `HashMap` | O(1) | -| Eingabehistorie (optional) | `ArrayDeque` | O(1) Front/Back | +| Besuchte Räume (für die Karte) | `LinkedHashSet` | O(1) + Besuchsreihenfolge | +| BFS-Queue (Pathfinder, MapLayout) | `ArrayDeque` | O(1) FIFO | +| BFS-Pfadrekonstruktion | `LinkedList` | O(1) `addFirst` | +| GUI-Eingabebrücke (EDT → Worker) | `LinkedBlockingQueue` | blockierend, unbegrenzt | ## Begründungen im Detail ### `EnumMap` für Raum-Ausgänge -- **Direction** ist Enum (`NORTH`, `SOUTH`, `EAST`, `WEST`, evtl. `UP`, `DOWN`) +- **Direction** ist Enum (`NORTH`, `SOUTH`, `EAST`, `WEST`) - `EnumMap` ist array-backed, kein Hashing nötig → schneller und kompakter als `HashMap` - Iteration in Enum-Deklarationsreihenfolge (stabil) @@ -49,11 +52,19 @@ Entscheidung "keine Stapel" (1 Item pro id) macht das Map-basierte Modell sauber - Entspricht dem expliziten Tipp aus der Aufgabenstellung - Vermeidet wachsendes `switch`-Statement -### `ArrayDeque` für Historie (optional) +### `ArrayDeque` als BFS-Queue -- Falls Up-Arrow in der GUI gewünscht oder Befehlsverlauf -- `ArrayDeque` ist `LinkedList` praktisch immer überlegen (bessere Cache-Lokalität, weniger Overhead) -- Beidseitige O(1)-Operationen +- `Pathfinder` (Auto-Travel `go to `) und `MapLayout` (Karten-Layout) sind beides + Breitensuchen → brauchen eine **FIFO-Queue** +- `ArrayDeque` ist die Standardwahl dafür: O(1) an beiden Enden, keine Node-Allokation + wie bei `LinkedList`, bessere Cache-Lokalität + +### `LinkedList` für die Pfadrekonstruktion + +Die eine Stelle, an der `LinkedList` die richtige Wahl ist (`Pathfinder`): +Nach der BFS läuft man über `cameFrom` **rückwärts** vom Ziel zum Start und will den +Pfad in Vorwärtsreihenfolge. `addFirst()` ist bei `LinkedList` O(1) — bei `ArrayList` +wäre `add(0, x)` O(n), weil alles verschoben werden müsste. ## Bewusst NICHT gewählt @@ -62,7 +73,7 @@ Entscheidung "keine Stapel" (1 Item pro id) macht das Map-basierte Modell sauber | `ArrayList` für Inventar | O(n)-Lookup, Duplikat-Handling nötig | | `HashMap` für Inventar | Anzeige-Reihenfolge instabil | | `TreeMap` irgendwo | Keine sortierte Iteration nötig, O(log n) ohne Nutzen | -| `LinkedList` | `ArrayDeque` ist fast immer besser | +| `LinkedList` als Queue | dafür ist `ArrayDeque` besser (s.o.) — als `addFirst`-Liste in der Pfadrekonstruktion aber sehr wohl genutzt | | `Vector` / `Hashtable` | Legacy, synchronisiert (nicht gebraucht), langsamer | | `Map` für Exits in Domain | Direction sollte Enum sein, nicht String | @@ -70,4 +81,9 @@ Entscheidung "keine Stapel" (1 Item pro id) macht das Map-basierte Modell sauber Single-threaded: Game-Loop liest, dispatcht, schreibt — keine parallelen Mutationen. -**Ausnahme:** Bei Swing-GUI läuft Input über den Event-Dispatch-Thread, der Game-Loop in einem Worker-Thread. Hier kommt `BlockingQueue` (`ArrayBlockingQueue` reicht) als Brücke ins Spiel — siehe [architecture.md](architecture.md). +**Ausnahme:** Bei Swing-GUI läuft Input über den Event-Dispatch-Thread, der Game-Loop in +einem Worker-Thread. Als Brücke dient eine `LinkedBlockingQueue` (`SwingIO`): +Der `JTextField`-ActionListener (EDT) legt die Zeile ab, der Worker blockiert in +`take()`. Unbegrenzt statt `ArrayBlockingQueue`, weil es keine sinnvolle Obergrenze für +getippte Zeilen gibt und ein volles `offer()` Eingaben verlieren würde — siehe +[architecture.md](architecture.md). diff --git a/Semesterprojekt/docs/enhancement-ideas.md b/Semesterprojekt/docs/enhancement-ideas.md deleted file mode 100644 index bdb1f85..0000000 --- a/Semesterprojekt/docs/enhancement-ideas.md +++ /dev/null @@ -1,239 +0,0 @@ -# Enhancement-Ideen (Backlog) - -> Roh-Sammlung von Ausbau-Ideen über die Abgabe hinaus. Lebendes Dokument – -> noch kein Design, noch keine Festlegung. Wird in Teilprojekte zerlegt und -> nacheinander über den Brainstorming → Spec → Plan → Implementierung-Zyklus -> verfeinert. - -Stand: 2026-05-31. Ausgangslage: Pflichtumfang ("bare minimum") erfüllt, -alle Phasen 1–7 grün. Ziel: deutlich mehr Tiefe und Politur. - -## Leitziele - -- **Reichere Ausgabe** statt nacktem Konsolentext – sowohl Konsole als auch GUI. -- **GUI soll noch mehr bieten** als die Konsole (mehr Raum für visuelle Mittel). -- **~60 Minuten Spielzeit** anpeilen. Viel mehr Inhalt, viel mehr zu tun. -- Bestehendes **NPC-System zu einem tieferen Quest-System** ausbauen. - -## Themenblöcke - -### 1. Reichere Präsentation / Ausgabe-Schicht ✅ umgesetzt (Branch `feature/presentation-layer`) - -> Umgesetzt: semantisches Modell (`io.text`), `ConsoleIO` (ANSI + Box-Drawing, -> Color/Glyph-Modi), `SwingIO` (4 Regionen, `JTextPane`, gebündelter Font, -> HiDPI-Skalierung + Zoom `Strg +/-/0`), HUD pro Zug, Welcome-Banner. 79 Tests grün. -> Spec/Plan unter `docs/superpowers/`. Karte/Menü/Save/Musik weiterhin offen. - - -- Weg vom reinen `write(String)`: formatierte Ausgabe (Überschriften, Trenner, - Hervorhebungen für Items/NPCs/Richtungen). -- **Konsole**: ANSI-Farben, Box-Drawing-Zeichen für Rahmen, evtl. ASCII-Art / - Banner für Räume oder Spielstart. -- **GUI (Swing)**: mehr als ein `JTextArea` – Farben, Stile, evtl. Panels, - Statusanzeige, Bilder/Icons. - -**Festgelegt (Zielbild Konsole):** Stufe **B als Baseline** (Boxed Heading, -gelabelte Sektionen, persistente HUD-Zeile: Ort · Gold · Zug · Licht), **ASCII-Art -nur für große Momente** (Spielstart, Finale, Schlüsselräume). Semantische -Farb-Rollen: Items / NPCs / Exits / Heading / Gefahr. - -**Designprinzipien:** -- **Nicht überladen / Progressive Disclosure** – Raumeintritt zeigt nur das - Wesentliche (Ort, kurze Beschreibung, Auffälliges); Details auf Abruf - (`examine`, `look`). -- **Info auf Regionen verteilen statt Text stapeln** – die **Karte ersetzt die - „Exits:"-Zeile**. GUI: persistentes Map-Panel macht die Exit-Zeile überflüssig. - Konsole: kompakte Mini-Map (bei `map` bzw. knapp beim Raumeintritt). - -### 2. Nerd Fonts / Glyphen – das Installationsproblem - -- Nerd-Font-Glyphen (Icons) sehen gut aus, müssen aber auf dem System des - Spielers installiert sein, sonst Tofu-Kästchen. -- Offene Frage: gute Lösung finden, damit es überall funktioniert. -- Erste Gedanken: - - **GUI**: Font ließe sich *mitliefern* (`.ttf` in Resources, via - `Font.createFont` laden) → Installationsproblem entfällt für die GUI. - - **Konsole**: Font nicht kontrollierbar → gestufte Fallbacks - (reines ASCII → Unicode-Box-Drawing → Nerd-Glyphen) statt harter Annahme. - -### 3. Karte / Map ✅ umgesetzt (Branch `feature/map`) - -> Umgesetzt: `MapLayout` (BFS-Gitter-Layout + Fog of War), `MapView`-Modell, -> GUI-`MapPanel` (Graphics2D, ersetzt die Exit-Liste), Konsolen-ASCII via -> `AsciiMap` + `map`-Befehl, Besuchte-Räume-Tracking auf `Player`, Map-Push pro Zug. -> Item/NPC-Marker und Türen-Styling offen (Quest-Teilprojekt). Spec/Plan unter -> `docs/superpowers/`. - - -- Übersichtskarte der Räume – in **beiden** Modi. -- Konsole: ASCII-Map. GUI: gezeichnete Map (Grid/Graph). -- Offene Fragen: nur bereits besuchte Räume zeigen? aktuelle Position markieren? - fester Grundriss oder dynamisch aus den Exits berechnet? - -### 4. Gameplay-Ausbau (Inhalt) - -- Deutlich mehr Räume, Items, NPCs. -- Mehr zu *tun*: Rätsel, Mechaniken, Abhängigkeiten zwischen Objekten. -- Zielgröße: ~60 Min. Spielzeit (laut Annahme machbar, da überwiegend Text). - -### 5. Quest-System (NPC-Ausbau) - -> ✅ Fundament umgesetzt (Branch `feature/quest-foundation`): World-Flags + -> Condition/Effect-Engine, verschlossene Exits, zustandsabhängige Beschreibungen, -> bedingte Dialoge, Reaktionen mit requires/effects, Schalter-Effekte. Demo: -> Generator schaltet Strom → Keller-Tür öffnet + Raum wird beleuchtet. -> -> ✅ #3.2 umgesetzt (Branch `feature/quests`): condition-driven Quests -> (Quest/QuestStage), QuestEngine mit Auto-Advance + Ansagen, START_QUEST-Effekt, -> Konsolen-Befehl `quests` und GUI-Quest-Box unter der Karte. Demo-Quest -> `restore_power`. -> -> ✅ #3.3 umgesetzt (Branch `feature/endings`): priorisierte, condition-driven -> Enden (`endings.yaml`) + End-Screen mit Summary (Züge, Quests X/Y, Rang). -> Demo: Sieg-Ende (`manor_secured`) und Flucht-Ende (Front Door → `fled`). -> **Damit ist das Quest-System (#3) komplett.** Offen: Licht/Dunkelheit & -> Item-Kombination sowie #4 Content-Ausbau. - - -- Aktuelles NPC-System: Begrüßung + Geschenk-Reaktion (Item → Item). -- Ausbau Richtung: mehrstufige Quests, Bedingungen, NPC-"Memory" (Zustand), - Quest-Log, evtl. Win-Condition daran gekoppelt. - -**GUI-Layout (festgelegt):** Quest-Log als **dauerhaft sichtbare Box unterhalb der -Karte** im rechten Seitenbereich (Map oben, Quests darunter). Gehört zu Quest- -Teilprojekt #2 (Quest-Log); das Fundament #1 reserviert/plant den Slot. - -### 5b. Spielkern: zentraler Bogen + Mechanik-Spine - -**Leitidee (bestätigt):** Bewusst *kondensiert* – wenige Mechaniken, die sich -vielfach kombinieren, statt vieler Einzel-Gimmicks. Ein 60-Min-Spiel soll -*designt* wirken, nicht gestreckt. - -**Hauptziel:** *Den Strom wieder anschalten* (`power_on`). Der Weg dahin ist eine -**Quest-Kette, die den Spieler Raum für Raum führt** (Raum X freischalten → führt -zu Raum Y → … → Generator/Sicherungskasten). Das Hauptziel selbst ist das Ende -des Bogens; einzelne Teilziele schalten je den nächsten Bereich frei. - -**Mechanik-Spine (5 Bausteine, die sich kombinieren):** - -1. **World-Flags / Zustand** – Rückgrat (`power_on`, `cellar_drained`, - `ghost_banished`). Beschreibungen, Exits und NPC-Reaktionen lesen/schreiben sie. -2. **Verschlossene Exits & Schalter** – Türen brauchen Schlüssel *oder* Flag; - Hebel/Ventile setzen Flags und öffnen Bereiche. -3. **Licht & Dunkelheit** – dunkle Räume brauchen eine brennende Lichtquelle, - sonst keine Sicht auf Exits/Items (Atmosphäre, einfach umzusetzen). - > ✅ Umgesetzt (Branch `feature/light-darkness`): `Room.dark` + `Item.light`, - > `Light`-Helper, Gating in go/look/take/examine (Modell B: kein Eintritt ohne - > Licht), HUD-Licht verdrahtet. Demo: dunkler Dungeon braucht die brennende - > Lampe für den Generator. -4. **Item-Kombination** – z.B. `match + candle → lit candle`. Achtung: v1.0 hat - bewusst argloses `use X` ohne Targets gewählt → Command-Grammatik muss erweitert - werden (jetzt erlaubt, da über MVP hinaus). - > ✅ umgesetzt (Branch `feature/use-x-on-y`): `use X on Y` als datengetriebene - > Rezepte (`combinations.yaml`: requires/consume/produce/effects/response/failText), - > Operanden aus Inventar oder Raum, reihenfolge-unabhängiger Paar-Key. Kein - > Parser-Eingriff (`on`/`with`/`to` sind bereits Filler). Selbst-Kombination - > (a==b) wird beim Laden abgelehnt. Demo: `matches` + `torch` → `lit_torch` - > (dauerhafte Lichtquelle). -5. **Quest-Ketten + mehrere Enden** – Teilziele referenzieren Flags; Kette - abschließen = Win; *welche* Flags gesetzt sind, entscheidet das Ende. - -**Beispiel-Puzzle (zeigt das Stapeln):** *Strom wiederherstellen* – Sicherung im -gefluteten Keller; erst Ventil → `cellar_drained`; Sicherung einsetzen → Schalter -→ `power_on` → oberes Stockwerk/Tür öffnet sich. Ein Puzzle, drei Mechaniken, -schaltet einen ganzen Flügel frei. - -*Weitere Puzzle-Ideen im Backlog (optional, nach Bedarf):* Zahlenschloss mit -Code aus Lore, Caesar-Chiffre-Brief (`decode`), Geist bannen (Reliquien-Fetch), -Melodie-/Sequenz-Rätsel, dunkler Gang. Resource-Meter (Sanity/Öl) **offen** – -vorerst puzzle-fokussiert ohne Survival-Druck. - -### 6. Hauptmenü & Settings - -> ✅ umgesetzt (Branch `feature/main-menu-save-load`). Hauptmenü-Shell oberhalb -> des Game-Loops (Neues Spiel / Laden / Einstellungen / Beenden) über neue -> `GameIO.choose`-Primitive (Konsole: nummeriertes Menü; GUI: Buttons). -> **Minimale Settings**: nur Farb-Modus + Glyphen-Modus (ASCII/Unicode), live -> umschaltbar, in `saves/settings.json` persistiert. Musik-/Typewriter-Toggles -> weiter zurückgestellt (Features existieren noch nicht). - -- **Hauptmenü** vor der Spielschleife: *Neues Spiel*, *Spiel laden*, *Einstellungen*, *Beenden*. -- **Spielstände-Liste**: gespeicherte Spiele mit Metadaten (Name, Raum, Zugzahl, - Zeitstempel) anzeigen und hineinladen. -- **Settings-Untermenü**: z.B. Farben an/aus, Typewriter-Tempo, Musik-Lautstärke - an/aus, ASCII vs. Unicode. -- Sollte in beiden Modi funktionieren: Konsole = nummeriertes Textmenü, GUI = - Buttons/Liste. Architektur: Menü-Schicht *oberhalb* des Game-Loops. - -### 7. Speichern / Laden - -> ✅ umgesetzt (Branch `feature/main-menu-save-load`). JSON-Spielstand als -> Delta über die frisch aus YAML geladene Welt (`SaveData`/`SaveCodec`/ -> `SaveService`, atomare Writes). **Ein aktiver Slot**: manuelles `save`, -> Autosave (Quest-Completion + alle 10 Züge) und `quit`/`menu` (speichern + -> zurück ins Menü) schreiben denselben Slot; Laden nur über das Menü. -> `saves/` ist gitignored. - -- Spielstand serialisieren (Player-Zustand, Inventar, World-Flags, besuchte Räume, - Quest-Fortschritt) – Format z.B. YAML/JSON. -- Mehrere Speicherstände, benennbar; Anbindung an die Spielstände-Liste im Menü. - -### 8. Musik (GUI) - -> ✅ umgesetzt (Branch `feature/music`): OGG Vorbis (vorbisspi/jorbis), pro Raum -> via `music:`-Feld in `rooms.yaml`, externer gitignored `music/`-Ordner (nicht im -> JAR). Idempotenter Track-Wechsel mit ~400 ms Fades; Räume ohne `music` blenden zu -> Stille; gleicher Track läuft nahtlos weiter. Music-Stufe Off/Low/Med/High in den -> Settings (Default Medium), persistiert. Konsole stumm. Testbarer `MusicController` -> über isoliertes `OggMusicBackend`; fehlende Datei → still, kein Absturz. Siehe -> `docs/music.md`. - -- Hintergrundmusik **nur in der GUI**. -- **Pro Raum definierbar via YAML**: in `rooms.yaml` ein optionales Feld - (z.B. `music: `), Dateien in *einem* Ordner. -- **Dateigröße** ist ein Thema → Lösungsrichtung: - - Audiodateien **nicht** in JAR/Repo bündeln, sondern externer `music/`-Ordner; - Audio ggf. via `.gitignore` ausschließen + dokumentieren, wo Dateien hinkommen. - - **Komprimierte Formate** (OGG/MP3) statt WAV, **gestreamt** statt komplett geladen. - - Bibliothek erlaubt → kleinen Audio-Decoder einbinden (Java `javax.sound` kann - OGG/MP3 nicht nativ). - - Loop pro Track, sanfter Wechsel beim Raumwechsel, graceful fallback bei - fehlender Datei, Lautstärke aus den Settings. - - Architektur: `MusicPlayer`-Komponente hinter Interface; Konsole = No-Op. - -## Festgelegte Erweiterungs-Entscheidungen - -> ✅ **Pathfinding** umgesetzt (Branch `feature/pathfinding-go-to`): `go to ` -> per BFS über besuchte Räume (Locks + Licht beachtet), via `GoCommand` -> (Richtung vs. Raumziel). Konsole still + Zusammenfassung (Start/Mitte/Ziel), -> GUI-Minikarte animiert pro Schritt (`GameIO.travelStep`, ~300 ms). -> **Trie-Autocomplete** weiterhin für eine spätere Runde zurückgestellt. - -> ✅ **Tutorial** umgesetzt (Branch `feature/tutorial`): interaktiver Walkthrough -> beim Neuen Spiel (look→examine→take→inventory→use→go→go to), verb-/alias-basierte -> Abschluss-Erkennung (Bewegungs-Schritte verlangen echten Raumwechsel), -> Abschluss-Tipps, jederzeit per `skip` abbrechbar. Datengetrieben (`tutorial.yaml`, -> `TutorialLoader`), als `TutorialGuide` in den Game-Loop eingehängt; nur bei Neuem -> Spiel, nicht beim Laden. - -| Entscheidung | Wert | -|---|---| -| Eigene `uebung`-Datenstrukturen verwenden? | **Nein** – Standard-Collections (vom Prof bestätigt) | -| Algorithmen-Showcase | weiterhin willkommen, aber mit Standard-Datenstrukturen (z.B. BFS `go to `, Trie-Autocomplete) | -| Bibliotheken erlaubt? | **Ja** (Pflichtteil war bewusst ohne; Erweiterung darf welche nutzen) | -| Hauptmenü | ja: Neues Spiel / Laden / Settings / Beenden | -| Speichern/Laden | ja, mehrere benannte Spielstände | -| Musik | ja, **nur GUI**, pro Raum via YAML, externer Ordner, komprimiert + gestreamt | - -## Offene Designfragen (zu klären) - -- Wie weit darf die `GameIO`-Abstraktion aufgebohrt werden, ohne dass Konsole - und GUI auseinanderlaufen? (Gemeinsamer Loop ist ein Kernwert der Architektur.) -- Bleibt alles datengetrieben (YAML), auch Quests und Map-Layout? -- Reihenfolge / Abhängigkeiten der Teilprojekte. - ---- - -*Notiz: Datengetriebenheit (YAML), getrennte DTO/Domain-Schicht und ein für -beide Frontends identischer Game-Loop sind etablierte Architekturwerte – neue -Features sollten sie respektieren, nicht umgehen.* diff --git a/Semesterprojekt/docs/implementation-status.md b/Semesterprojekt/docs/implementation-status.md deleted file mode 100644 index 8d1102d..0000000 --- a/Semesterprojekt/docs/implementation-status.md +++ /dev/null @@ -1,135 +0,0 @@ -# Implementierungsstand & Reihenfolge - -Stand: alle Phasen 1–7 implementiert, 67 Tests grün, End-to-End-Smoke-Test des Walking-Skeletons + YAML-Load erfolgreich. - -## Phasen-Überblick - -```mermaid -flowchart TD - P1["Phase 1
Domain-Fundament"] - P2["Phase 2
Command-Schicht"] - P3["Phase 3
Walking Skeleton
(Konsole + handgebaute Welt)"] - P4["Phase 4
YAML-Loading + Validator"] - P5["Phase 5
Restliche Commands"] - P6["Phase 6
NPCs end-to-end"] - P7["Phase 7
Swing-GUI (Bonus)"] - - P1 --> P2 --> P3 --> P4 --> P5 --> P6 --> P7 -``` - -## Checkliste Phase 1: Domain - -- [x] `Direction` (`model.Direction`) -- [x] `Room` (`model.Room`) -- [x] `Item` abstract (`model.item.Item`) mit `abstract use(GameContext)` -- [x] `Npc` (`model.Npc`) inklusive `shell()`-Factory und `putReaction()` -- [x] `NpcReaction` (`model.NpcReaction`) -- [x] `GameIO` interface (`io.GameIO`) -- [x] `Player` (`model.Player`) -- [x] `World` (`model.World`) -- [x] `GameContext` (`game.GameContext`) -- [x] `ReadableItem` (`model.item.ReadableItem`) -- [x] `SwitchableItem` (`model.item.SwitchableItem`) -- [x] `PlainItem` (`model.item.PlainItem`) - -## Checkliste Phase 2: Commands - -- [x] `Command` interface (`command.Command`) -- [x] `ParsedCommand` (record) -- [x] `CommandRegistry` (`command.CommandRegistry`) -- [x] `CommandParser` (`command.CommandParser`) mit Filler-Words -- [x] `LookCommand` -- [x] `GoCommand` -- [x] `InventoryCommand` - -## Checkliste Phase 3: Walking Skeleton - -- [x] `ConsoleIO` -- [x] `Game` (Loop) -- [x] `App.main` (lädt sofort über YAML; der Walking-Skeleton-Zustand mit hartkodierter Welt wurde übersprungen, weil YAML-Load und Loop schon zusammen funktionieren) -- [x] Probelauf: `look`, `go north`, `inventory` (siehe `GameTest`, `LookCommandTest`) - -## Checkliste Phase 4: YAML-Loading - -- [x] DTOs: `GameDto`, `ItemDto`, `RoomDto`, `NpcDto`, `ReactionDto` -- [x] Test-Fixtures unter `src/test/resources/world/` -- [x] `WorldLoader` (Happy-Path) -- [x] `ReferenceResolver` -- [x] `WorldValidator` (eine Validierungsregel pro Test) -- [x] Echte Welt-YAMLs unter `src/main/resources/world/` -- [x] `App.main` läuft direkt gegen YAML-Load - -## Checkliste Phase 5: Restliche Commands - -- [x] `TakeCommand` -- [x] `DropCommand` -- [x] `UseCommand` -- [x] `ReadCommand` -- [x] `ExamineCommand` -- [x] `HelpCommand` -- [x] `QuitCommand` - -## Checkliste Phase 6: NPCs - -- [x] `Npc` voll ausgebaut (greeting, reactions) -- [x] `NpcReaction` -- [x] `TalkCommand` -- [x] `GiveCommand` -- [x] NPCs in `WorldLoader` integriert -- [x] End-to-End-Test: Lampe geben → Schlüssel bekommen (`TalkGiveCommandTest`) - -## Checkliste Phase 7: Swing-GUI - -- [x] `SwingIO` mit `LinkedBlockingQueue`-Brücke -- [x] `AppGui.main` -- [x] Game-Loop in Worker-Thread - -## Build & Run - -```sh -mvn test # 67 Tests -mvn -DskipTests exec:java -Dexec.mainClass=thb.jeanluc.adventure.App # Konsole -mvn -DskipTests exec:java -Dexec.mainClass=thb.jeanluc.adventure.AppGui # Swing -``` - -## Festgelegte Designentscheidungen - -Nicht mehr offen, nicht nochmal diskutieren: - -| Entscheidung | Wert | -|---|---| -| Item-Hierarchie | abstract Item + ReadableItem/SwitchableItem/PlainItem | -| Item-Package | `model.item` (Subpackage) | -| Switchable-State-Typ | `boolean` (kein Enum) | -| Switchable-Felder | nur `state` (Builder mappt YAML `initialState`); kein separates `initialState`-Feld am Domain-Objekt | -| use-Targets | argless `use X`, kein `use X on Y` | -| Item kennt Standort | nein, „dumme" Items | -| Hidden Items | nein | -| State→Raum-Beschreibung | nein im MVP | -| Room.description | `final`, immutable | -| Room.describe() | nicht auf Room, im LookCommand | -| Room.equals/hashCode | nicht überschreiben, Identity | -| Room-NPCs-Feld | von Anfang an drin | -| Bidirektionale Exits | manuell, kein Auto-Spiegeln | -| GameContext-Inhalt | minimal: World + Player + GameIO | -| IDs | lowercase snake_case slugs, kein UUID | -| ID-Regex | `^[a-z][a-z0-9_]*$` | -| YAML-Aufteilung | `game.yaml`, `items.yaml`, `rooms.yaml`, `npcs.yaml` | -| DTO ↔ Domain | getrennt, Resolver-Phase löst String-IDs zu Referenzen auf | -| Item-Type-Discriminator | YAML-Feld `type: plain|readable|switchable`, in `ItemFactory` als switch | -| Codebase-Sprache | Englisch (Identifier, YAML, User-Strings) | -| Doku-Sprache | Deutsch (Prose), Englisch (Code-Beispiele) | -| Diagramme | Mermaid | -| Lombok-Inheritance | `@SuperBuilder` | -| `@Data` | vermeiden, einzelne Annotations bevorzugen | -| Quit-Wiring | `QuitCommand.bind(Game)` nach Registry-Aufbau | -| Help-Quelle | `HelpCommand` zieht aus `CommandRegistry.distinctCommands()` | -| GameIO-Methodennamen | `readLine()` / `write(String)` (Java-üblich, konsistent) | -| Lombok-Version | 1.18.42 (1.18.36 ist nicht Java-26-kompatibel) | - -## Offen / nicht im MVP - -- **Win-Condition**: Spiel endet nur per `quit`. Optionale Erweiterung: Bedingung in `game.yaml` (`winRoom`, `requiredItem`). -- **Bedingte NPC-Reaktionen**, NPC-Memory, Quests — bewusst ausgelassen (siehe `npcs.md`). -- **Item-Aliases** (z.B. `lamp` ↔ `oil_lamp`) — YAGNI bis konkreter Bedarf. -- **Eingabehistorie** in der GUI — `ArrayDeque` ist vorgesehen, nicht umgesetzt. diff --git a/Semesterprojekt/release/HauntedManor-Console.jar b/Semesterprojekt/release/HauntedManor-Console.jar deleted file mode 100644 index b51b4db..0000000 Binary files a/Semesterprojekt/release/HauntedManor-Console.jar and /dev/null differ diff --git a/Semesterprojekt/release/HauntedManor-GUI.jar b/Semesterprojekt/release/HauntedManor-GUI.jar deleted file mode 100644 index 42470cb..0000000 Binary files a/Semesterprojekt/release/HauntedManor-GUI.jar and /dev/null differ