docs(readme): restructure around the two ways to run the game

The README buried the "just run it" path below the Maven section and left out
several commands the game actually supports.

- Split the quick start into Weg A (prebuilt jars, Java only, works offline) and
  Weg B (Maven wrapper: test, package, exec:java@run / @gui)
- State the Java 21 prerequisite up front, with how to check it
- Note that the console version runs headless while the GUI needs a desktop, and
  where saves/ is written
- Document the missing commands: 'go to <raum>', 'save'/'speichern', and the
  skippable tutorial
- Add the scope up front (14 rooms, 19 items, 3 NPCs, 259 tests)

All commands and links in the file were executed/resolved before committing.
This commit is contained in:
2026-07-14 12:55:19 +02:00
parent c205d1f49b
commit d7f91c2e47

View File

@@ -1,78 +1,89 @@
# Haunted Manor Textadventure # Haunted Manor Textadventure
Semesterprojekt im Modul Programmierung 2 (Algorithmen und Datenstrukturen, Semesterprojekt im Modul Algorithmen und Datenstrukturen (2. Semester).
2. Semester). Ein textbasiertes Adventure in Java: Der Spieler navigiert durch Ein textbasiertes Adventure in Java: Der Spieler navigiert durch die Räume eines
Räume eines Spukhauses, sammelt Gegenstände, interagiert mit NPCs und löst so Spukhauses, sammelt Gegenstände, spricht mit NPCs, löst Rätsel und findet einen
das Spiel. Weg hinaus.
Die Spielwelt ist vollständig datengetrieben Räume, Gegenstände und NPCs Die Spielwelt ist vollständig datengetrieben Räume, Gegenstände, NPCs, Quests
werden aus YAML-Dateien geladen, nicht im Code festverdrahtet. und Enden werden aus YAML-Dateien geladen, nicht im Code festverdrahtet.
## Features **Umfang:** 14 Räume, 19 Gegenstände, 3 NPCs, 259 Tests.
- **Navigation** durch beliebig viele Räume in den Himmelsrichtungen ---
- **Inventar** mit Aufnehmen (`take`) und Ablegen (`drop`) von Gegenständen
- **Item-Hierarchie**: einfache, lesbare (`read`) und schaltbare (`use`) Gegenstände
- **NPCs** mit Gesprächen (`talk`) und Geschenk-Reaktionen (`give`)
- **Zwei Frontends**: klassische Konsole und Swing-GUI (Bonus)
- **Datengetriebene Welt** aus YAML inkl. Referenzauflösung und Validierung beim Laden
## Voraussetzungen ## Schnellstart
- **Java 21+** (LTS). Gebaut wird gegen `maven.compiler.release=21`; das neueste Es gibt **zwei Wege**, das Spiel zu starten. Wenn Sie nur spielen wollen, nehmen
genutzte Sprachfeature ist `List.getFirst()` (SequencedCollection, Java 21). Sie Weg A dafür wird kein Maven benötigt.
Neuere JDKs (2225) funktionieren ebenfalls.
- Maven 3.9+ **oder** der mitgelieferte Maven Wrapper (`./mvnw`) damit ist
keine separate Maven-Installation nötig; der Wrapper lädt die passende
Maven-Version beim ersten Aufruf selbst herunter.
## Schnellstart ohne Maven ### Voraussetzung
Im Ordner [`release/`](release/) liegen zwei fertig gebaute, eigenständige **Java 21 oder neuer** (LTS). Gebaut wird gegen `maven.compiler.release=21`;
JARs mit allen Abhängigkeiten je eine pro Frontend. Sie brauchen nur Java 21+: das neueste genutzte Sprachfeature ist `List.getFirst()` (SequencedCollection,
Java 21). Neuere JDKs (2225) funktionieren ebenfalls.
Prüfen mit:
```bash ```bash
java -jar release/HauntedManor-Console.jar # Konsolen-Version (~5 MB, ohne Audio) java -version
java -jar release/HauntedManor-GUI.jar # Swing-GUI (~17 MB, mit Musik)
``` ```
Die Konsolen-JAR enthält weder die Musikdateien noch die OGG-Bibliotheken ### Weg A fertige JARs starten (ohne Maven)
der Textmodus spielt keine Musik, daher werden sie dort weggelassen.
## Bauen und Testen Im Ordner [`release/`](release/) liegen zwei eigenständige JARs, die alle
Abhängigkeiten bereits enthalten. Es wird **nur Java** gebraucht, kein Maven,
Mit dem Wrapper (empfohlen, keine Maven-Installation nötig) unter Windows keine Internetverbindung:
`mvnw.cmd` statt `./mvnw`:
```bash ```bash
./mvnw clean test # alle Tests ausführen (259 Tests) java -jar release/HauntedManor-Console.jar # Konsolen-Version (~5 MB)
./mvnw clean package # baut beide JARs nach release/ (Console + GUI) java -jar release/HauntedManor-GUI.jar # Swing-GUI (~17 MB)
``` ```
Ein systemweit installiertes `mvn` funktioniert identisch (`mvn clean test` …). - Die **Konsolen-Version** läuft in jedem Terminal, auch ohne grafische
Oberfläche.
- Die **GUI-Version** (Bonus) braucht einen Desktop und spielt zusätzlich
Hintergrundmusik. Die Konsolen-JAR enthält weder die Musikdateien noch die
OGG-Bibliotheken im Textmodus gibt es keine Musik, daher sind sie dort
weggelassen.
## Spielen ### Weg B mit Maven bauen und starten
Über die im `pom.xml` konfigurierten Run-Profile des `exec-maven-plugin`: Der mitgelieferte **Maven Wrapper** (`./mvnw`) lädt die passende Maven-Version
beim ersten Aufruf selbst herunter eine separate Maven-Installation ist nicht
nötig. Unter Windows `mvnw.cmd` statt `./mvnw` verwenden. Ein systemweit
installiertes `mvn` funktioniert identisch.
```bash ```bash
./mvnw exec:java@run # Konsolen-Version (thb.jeanluc.adventure.App) ./mvnw clean test # alle 259 Tests ausführen
./mvnw exec:java@gui # Swing-GUI (thb.jeanluc.adventure.AppGui) ./mvnw clean package # baut beide JARs neu nach release/
./mvnw exec:java@run # Konsolen-Version starten (thb.jeanluc.adventure.App)
./mvnw exec:java@gui # Swing-GUI starten (thb.jeanluc.adventure.AppGui)
``` ```
`./mvnw exec:java` ohne Profil startet ebenfalls die Konsolen-Version. `./mvnw exec:java` ohne Profil startet ebenfalls die Konsolen-Version.
Alternativ direkt aus der gebauten JAR (siehe [Schnellstart](#schnellstart-ohne-maven)).
### Befehle im Spiel > **Hinweis:** Der erste Maven-Aufruf lädt die Abhängigkeiten (Jackson, Lombok,
> JUnit, Logback) aus dem Internet. Wer offline ist, nimmt Weg A.
Befehle funktionieren auf **Englisch und Deutsch** die deutschen Beispiele der Speicherstände und Einstellungen legt das Spiel in einem Ordner `saves/` neben
Aufgabenstellung (`Gehe nach Norden`, `Nimm Brief`, `Lies Brief`, dem Arbeitsverzeichnis an, aus dem es gestartet wurde.
`Benutze Schaufel`) laufen also direkt. Item-IDs bleiben englisch (`letter`,
`shovel`), Himmelsrichtungen verstehen beide Sprachen (`north` / `norden`). ---
## Befehle im Spiel
Befehle funktionieren auf **Englisch und Deutsch** die deutschen Beispiele aus
der Aufgabenstellung (`Gehe nach Norden`, `Nimm Brief`, `Lies Brief`,
`Benutze Schaufel`) laufen also direkt. Gegenstands-IDs bleiben englisch
(`letter`, `shovel`), Himmelsrichtungen verstehen beide Sprachen
(`north` / `norden`).
| Befehl | Aliase | Deutsch | Wirkung | | Befehl | Aliase | Deutsch | Wirkung |
|---|---|---|---| |---|---|---|---|
| `go <richtung>` | `move`, `walk` | `gehe`, `geh` | In eine Himmelsrichtung gehen | | `go <richtung>` | `move`, `walk` | `gehe`, `geh` | In eine Himmelsrichtung gehen |
| `go to <raum>` | | | Automatisch zu einem bekannten Raum laufen |
| `look` | `l` | `schau`, `umsehen` | Aktuellen Raum beschreiben | | `look` | `l` | `schau`, `umsehen` | Aktuellen Raum beschreiben |
| `map` | `m` | `karte` | Karte der erkundeten Räume anzeigen | | `map` | `m` | `karte` | Karte der erkundeten Räume anzeigen |
| `quests` | `log`, `journal` | `aufgaben` | Aktive und erledigte Quests anzeigen | | `quests` | `log`, `journal` | `aufgaben` | Aktive und erledigte Quests anzeigen |
@@ -85,54 +96,63 @@ Aufgabenstellung (`Gehe nach Norden`, `Nimm Brief`, `Lies Brief`,
| `examine <item>` | `x`, `inspect` | `untersuche` | Gegenstand genauer ansehen | | `examine <item>` | `x`, `inspect` | `untersuche` | Gegenstand genauer ansehen |
| `talk <npc>` | `speak` | `rede`, `sprich` | Mit einem NPC sprechen | | `talk <npc>` | `speak` | `rede`, `sprich` | Mit einem NPC sprechen |
| `give <item> <npc>` | | `gib` | NPC einen Gegenstand geben | | `give <item> <npc>` | | `gib` | NPC einen Gegenstand geben |
| `save` | | `speichern` | Spielstand speichern |
| `help` | `?` | `hilfe` | Befehlsübersicht anzeigen | | `help` | `?` | `hilfe` | Befehlsübersicht anzeigen |
| `quit` | `exit` | `beenden` | Spiel beenden | | `quit` | `exit` | `beenden` | Speichern und zurück ins Hauptmenü |
Zu Beginn führt ein kurzes, überspringbares Tutorial (`skip`) durch die
wichtigsten Befehle.
---
## Wo die Aufgabenstellung umgesetzt ist ## Wo die Aufgabenstellung umgesetzt ist
Kurzwegweiser zu den geforderten Punkten: | Anforderung | Wo im Code | Umsetzung |
| Anforderung | Wo | Details |
|---|---|---| |---|---|---|
| **Raum-Klasse + Navigation**, mind. 4 Räume | [`model/Room.java`](src/main/java/thb/jeanluc/adventure/model/Room.java), [`model/Direction.java`](src/main/java/thb/jeanluc/adventure/model/Direction.java) | **14 Räume**. `Room` hält direkte Referenzen auf Nachbarräume in einer `EnumMap<Direction, Room>` ein echter Objektgraph, keine String-IDs. | | **Raum-Klasse + Navigation**, mind. 4 Räume | [`model/Room.java`](src/main/java/thb/jeanluc/adventure/model/Room.java), [`model/Direction.java`](src/main/java/thb/jeanluc/adventure/model/Direction.java) | **14 Räume.** `Room` hält direkte Referenzen auf seine Nachbarräume in einer `EnumMap<Direction, Room>` ein echter Objektgraph, keine String-IDs. |
| **`gehen`-Befehl** (Tipp: switch/if-else/HashMap) | [`command/CommandRegistry.java`](src/main/java/thb/jeanluc/adventure/command/CommandRegistry.java), [`command/impl/GoCommand.java`](src/main/java/thb/jeanluc/adventure/command/impl/GoCommand.java) | Dispatch über `HashMap<String, Command>` (O(1), Aliase = mehrere Keys auf dieselbe Instanz). Richtung dann O(1) per `EnumMap`-Lookup. | | **`gehen`-Befehl** (Tipp: switch / if-else / HashMap) | [`command/CommandRegistry.java`](src/main/java/thb/jeanluc/adventure/command/CommandRegistry.java), [`command/impl/GoCommand.java`](src/main/java/thb/jeanluc/adventure/command/impl/GoCommand.java) | Dispatch über `HashMap<String, Command>` (O(1); Aliase = mehrere Keys auf dieselbe Instanz, statt eines wachsenden `switch`). Die Richtung danach O(1) per `EnumMap`-Lookup. |
| **Gegenstände + Inventar**, mind. 3 Items | [`model/Player.java`](src/main/java/thb/jeanluc/adventure/model/Player.java), [`model/item/`](src/main/java/thb/jeanluc/adventure/model/item/) | **19 Items**. Inventar = `LinkedHashMap<String, Item>`. Item-Hierarchie: `PlainItem`, `ReadableItem`, `SwitchableItem`. | | **Gegenstände + Inventar**, mind. 3 Items | [`model/Player.java`](src/main/java/thb/jeanluc/adventure/model/Player.java), [`model/item/`](src/main/java/thb/jeanluc/adventure/model/item/) | **19 Gegenstände.** Inventar = `LinkedHashMap<String, Item>` (O(1)-Zugriff *und* stabile Anzeigereihenfolge). Hierarchie: `PlainItem`, `ReadableItem`, `SwitchableItem`. |
| **Aufnehmen / Ablegen / Benutzen / Lesen** | [`command/impl/`](src/main/java/thb/jeanluc/adventure/command/impl/) | `TakeCommand`, `DropCommand`, `UseCommand`, `ReadCommand` inkl. `letter` („Brief") und `shovel` („Schaufel") aus den Beispielen. | | **Aufnehmen / Ablegen / Benutzen / Lesen** | [`command/impl/`](src/main/java/thb/jeanluc/adventure/command/impl/) | `TakeCommand`, `DropCommand`, `UseCommand`, `ReadCommand` inklusive `letter` („Brief") und `shovel` („Schaufel") aus den Beispielen der Aufgabenstellung. |
| **Datenstrukturwahl** (bewertungsrelevant) | [`docs/data-structures.md`](docs/data-structures.md) | Begründung je Anwendungsfall + Tabelle „Bewusst NICHT gewählt". | | **Wahl der Datenstrukturen** | [`docs/data-structures.md`](docs/data-structures.md) | Begründung je Anwendungsfall, plus Tabelle „Bewusst NICHT gewählt". |
| **NPCs** (Bonus) | [`model/Npc.java`](src/main/java/thb/jeanluc/adventure/model/Npc.java) | 3 NPCs, die auf Spielerzustand **reagieren** (`talk`, `give`). | | **NPCs** (Bonus) | [`model/Npc.java`](src/main/java/thb/jeanluc/adventure/model/Npc.java) | 3 NPCs, die auf den Spielerzustand **reagieren** (`talk`, `give`). |
| **Swing-GUI** (Bonus) | [`io/SwingIO.java`](src/main/java/thb/jeanluc/adventure/io/SwingIO.java) | `JTextField` für die Eingabe (s. Hinweis unten). | | **Swing-GUI** (Bonus) | [`io/SwingIO.java`](src/main/java/thb/jeanluc/adventure/io/SwingIO.java) | `JTextField` für die Eingabe (siehe Hinweis unten). |
| **Javadoc** | überall | An Klassen, Methoden und Instanzvariablen. | | **Javadoc** | gesamtes `src/main/java` | An Klassen, Methoden und Instanzvariablen. |
**Hinweis zur GUI:** Die Aufgabe nennt `JTextField` + `JTextArea`. Die Eingabe ist ein **Hinweis zur GUI:** Die Aufgabe nennt `JTextField` **und** `JTextArea`. Die
`JTextField` wie gefordert; für die **Ausgabe** wird bewusst ein `JTextPane` statt einer Eingabe ist wie gefordert ein `JTextField`. Für die **Ausgabe** wird bewusst ein
`JTextArea` verwendet. Grund: `JTextArea` kann nur unformatierten Text darstellen, während `JTextPane` statt einer `JTextArea` verwendet: `JTextArea` kann ausschließlich
das Spiel farbige/gestylte Ausgabe (`StyledText``StyledDocument`) rendert Raumnamen, unformatierten Text darstellen, das Spiel rendert aber farbig gestylte Ausgabe
Items und Hinweise sind farblich unterschieden. `JTextPane` ist dafür die passende (`StyledText``StyledDocument`), damit Raumnamen, Gegenstände und Hinweise
Swing-Komponente und funktional eine Obermenge von `JTextArea`. optisch unterscheidbar sind. `JTextPane` ist dafür die passende Swing-Komponente
und funktional eine Obermenge von `JTextArea`.
---
## Projektstruktur ## Projektstruktur
``` ```
Semesterprojekt/ Semesterprojekt/
├── src/main/java/thb/jeanluc/adventure/ ├── src/main/java/thb/jeanluc/adventure/
│ ├── App.java - Einstiegspunkt Konsole │ ├── App.java Einstiegspunkt Konsole
│ ├── AppGui.java - Einstiegspunkt Swing-GUI │ ├── AppGui.java Einstiegspunkt Swing-GUI
│ ├── command/ - Befehlsparser, Registry + impl/ (15 Kommandos) │ ├── model/ Domänenmodell: Room, Player, Npc, item/ …
│ ├── game/ - Spielschleife, Engines, Light, Pathfinder, MapLayout │ ├── command/ Parser, Registry + impl/ (15 Kommandos)
│ ├── io/ - GameIO-Abstraktion: ConsoleIO, SwingIO + text/ (Styling) │ ├── game/ Spielschleife, Engines, Light, Pathfinder, MapLayout
│ ├── loader/ - YAML-Laden: dto/, Factories, Resolver, Validator │ ├── io/ GameIO-Abstraktion: ConsoleIO, SwingIO + text/
│ ├── menu/ - Hauptmenü und Einstellungen │ ├── loader/ YAML-Laden: dto/, Factories, Resolver, Validator
│ ├── model/ - Domänenmodell: Room, World, Player, Npc, item/ │ ├── save/ Speicherstände und Einstellungen (JSON)
│ └── save/ - Speicherstände und Settings (JSON) │ └── menu/ Hauptmenü und Einstellungen
├── src/main/resources/world/ ├── src/main/resources/world/
│ ├── game.yaml - Metadaten (Titel, Startraum, Begrüßung) │ ├── game.yaml Metadaten (Titel, Startraum, Begrüßung)
│ ├── rooms.yaml - Räume mit Ausgängen, Items, NPCs │ ├── rooms.yaml Räume mit Ausgängen, Gegenständen, NPCs
│ ├── items.yaml - Gegenstände │ ├── items.yaml Gegenstände
│ ├── npcs.yaml - NPCs und ihre Reaktionen │ ├── npcs.yaml NPCs und ihre Reaktionen
│ ├── quests.yaml - Quests und Stages │ ├── quests.yaml Quests und ihre Stufen
│ ├── endings.yaml - Spielenden │ ├── endings.yaml Spielenden
│ └── combinations.yaml - Item-Kombinationen (`use X on Y`) │ └── combinations.yaml Gegenstands-Kombinationen (`use X on Y`)
── docs/ - Design- und Architekturdokumentation ── src/test/java/ 259 Tests (JUnit 5 + AssertJ)
├── release/ fertig gebaute JARs (Console + GUI)
└── docs/ Design- und Architekturdokumentation
``` ```
## Technologie-Stack ## Technologie-Stack
@@ -140,12 +160,12 @@ Semesterprojekt/
- **Java 21 (LTS)** - **Java 21 (LTS)**
- **Jackson** (YAML) für das Laden der Spielwelt - **Jackson** (YAML) für das Laden der Spielwelt
- **Lombok** zur Reduktion von Boilerplate - **Lombok** zur Reduktion von Boilerplate
- **JUnit 5 + AssertJ + Mockito** für Tests - **JUnit 5 + AssertJ + Mockito** für die Tests
- **SLF4J + Logback** für Logging - **SLF4J + Logback** für Logging
- **Swing** für die GUI (Bonus) - **Swing** für die GUI (Bonus)
## Dokumentation ## Dokumentation
Ausführliche Design- und Architekturdokumentation liegt unter Ausführliche Design- und Architekturdokumentation liegt unter
[`docs/`](docs/README.md): Package-Struktur, Datenstrukturen, Item-Modell, [`docs/`](docs/README.md): Package-Struktur, Wahl der Datenstrukturen,
YAML-Schemas, Lade-Ablauf, Befehle und NPC-Modell. Item-Modell, Befehle, NPC-Modell, YAML-Schemas und Lade-Ablauf.