Walking skeleton through Swing GUI: YAML-driven world (4 rooms, 4 items, 1 NPC), HashMap command dispatch with parser, three-tier item hierarchy (readable / switchable / plain), and end-to-end NPC give/receive flow. 67 tests green.
136 lines
5.1 KiB
Markdown
136 lines
5.1 KiB
Markdown
# 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<br/>Domain-Fundament"]
|
||
P2["Phase 2<br/>Command-Schicht"]
|
||
P3["Phase 3<br/>Walking Skeleton<br/>(Konsole + handgebaute Welt)"]
|
||
P4["Phase 4<br/>YAML-Loading + Validator"]
|
||
P5["Phase 5<br/>Restliche Commands"]
|
||
P6["Phase 6<br/>NPCs end-to-end"]
|
||
P7["Phase 7<br/>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.
|