diff --git a/Semesterprojekt/docs/superpowers/specs/2026-05-31-map-design.md b/Semesterprojekt/docs/superpowers/specs/2026-05-31-map-design.md new file mode 100644 index 0000000..d40c5e7 --- /dev/null +++ b/Semesterprojekt/docs/superpowers/specs/2026-05-31-map-design.md @@ -0,0 +1,151 @@ +# Spec: Karte / Mini-Map (Teilprojekt 2) + +Stand: 2026-05-31. Zweites Teilprojekt aus [enhancement-ideas.md](../../enhancement-ideas.md). +Baut auf der Präsentations-Schicht (Teilprojekt 1) auf, insbesondere dem +reservierten GUI-Seitenpanel und dem semantischen Modell-+-Renderer-Muster. + +## 1. Kontext & Ziel + +Räume bilden bereits einen gerichteten N/S/O/W-Graphen über `Room.exits` +(`EnumMap`). Ziel: eine **Karte** in beiden Frontends, die diesen +Graphen sichtbar macht – **Fog of War** (nur Entdecktes), Stil **„umrandete Räume + +Korridore"** (im Brainstorming bestätigt). Die GUI zeichnet grafisch (Graphics2D), +die Konsole spiegelt es als ASCII. + +Bestätigte Entscheidungen: +- **Fog of War**: nur besuchte Räume + deren direkte Nachbarn als `?`-Stub. +- **Stil**: umrandete Räume, durch Korridore verbunden; aktueller Raum hervorgehoben. +- **Layout aus dem Graphen abgeleitet** (keine Koordinaten in YAML). +- GUI: persistentes Panel ersetzt die Exit-Zeile. Konsole: `map`-Befehl on demand, + Exit-Zeile im Raumtext bleibt. + +## 2. Scope + +**In Scope:** +- `MapView`-Modell (platzierte Zellen + Verbindungen, Zustände). +- `MapLayout`-Service: BFS-Layout aus dem Exit-Graphen + Fog-of-War-Filter. +- **Besuchte-Räume-Tracking** auf `Player`. +- `GameIO`: `setMap` (Push pro Zug, GUI) + `showMap` (on demand, Konsole). +- `MapCommand` (`map`, `m`). +- `Game`-Loop ruft `setMap` pro Zug (neben `setHud`). +- GUI: `MapPanel` (Graphics2D) im Seitenpanel, scrollbar, auf aktuellen Raum zentriert. +- Konsole: ASCII-Karte (`AsciiMap`), respektiert `GlyphMode`. + +**Out of Scope (spätere Teilprojekte):** +- Marker für Items/NPCs auf der Karte. +- Styling für verschlossene/blockierte Exits (Quest-Teilprojekt; Karte bekommt + später einen Hook dafür). +- Zoom über das Scrollen hinaus. + +## 3. Architektur (Modell + Renderer pro Frontend) + +`MapLayout` erzeugt aus `World` (Exit-Graph), der Menge besuchter Räume und dem +aktuellen Raum eine frontend-agnostische `MapView`. Layout und Fog-of-War-Logik +liegen ausschließlich hier; die Renderer kennen nur das fertige `MapView`. + +```mermaid +flowchart LR + W["World (Exit-Graph)"] --> L["MapLayout.compute()"] + V["Player.visitedRoomIds"] --> L + C["aktueller Raum"] --> L + L --> MV["MapView (Zellen + Verbindungen)"] + MV -- "setMap()" --> GUI["SwingIO → MapPanel (Graphics2D)"] + MV -- "showMap()" --> CON["ConsoleIO → AsciiMap (StyledText)"] +``` + +## 4. Komponenten & Datenmodell + +Views in `io.text`, Layout-Logik in neuem Package `map`, GUI-Panel in `io`. + +```java +// io.text +public enum CellState { CURRENT, VISITED, KNOWN } + +public record RoomCell(String id, String name, int x, int y, CellState state) {} + +public record Connection(RoomCell from, RoomCell to) {} // Stil via Zellzustand + +public record MapView(java.util.List cells, + java.util.List connections) { + public MapView { cells = List.copyOf(cells); connections = List.copyOf(connections); } +} +``` + +- `map.MapLayout` – `static MapView compute(World world, String startRoomId, Set visited, Room current)`. +- `io.text.AsciiMap` – `static StyledText render(MapView, GlyphMode)`. +- `io.MapPanel` – `JComponent`, `void show(MapView)`; `paintComponent` zeichnet. +- `command.impl.MapCommand` – baut `MapView`, ruft `io.showMap(view)`. +- `model.Player` – `Set visitedRoomIds` (LinkedHashSet, Reihenfolge stabil). + +### GameIO-Erweiterung (additiv) + +```java +/** Push pro Zug: GUI aktualisiert ihr Panel; Konsole ignoriert (kein Spam). */ +default void setMap(MapView view) { /* no-op */ } + +/** On demand (map-Befehl): rendert die Karte. Default = ASCII in UNICODE. */ +default void showMap(MapView view) { print(AsciiMap.render(view, GlyphMode.UNICODE)); } +``` + +- **`ConsoleIO`** überschreibt `showMap` → `print(AsciiMap.render(view, this.glyphs))` + (nutzt also seinen konfigurierten Glyph-Modus). `setMap` bleibt Default (no-op) → + kein Karten-Spam pro Zug in der Konsole. +- **`SwingIO`** überschreibt `setMap` → `MapPanel` neu zeichnen; `showMap` → no-op + (das Panel ist ohnehin sichtbar). +- **`TestIO`** nutzt beide Defaults: `setMap` = no-op, `showMap` druckt UNICODE-ASCII + in `outputs` → `MapCommand`-Test kann darauf assertieren. Kein Override nötig. + +So liegt der Glyph-Modus dort, wo er hingehört (am Konsolen-Renderer), ohne die +`GameIO`-Schnittstelle um eine Glyph-Methode aufzublähen. + +## 5. Fog-of-War-Regeln + +| Zustand | Bedeutung | Darstellung | +|---|---|---| +| `CURRENT` | besuchter Raum, in dem der Spieler steht | Goldener Rahmen | +| `VISITED` | bereits betretener Raum | heller Rahmen + Name | +| `KNOWN` | direkter Exit-Nachbar eines besuchten Raums, noch nicht betreten | gestrichelter `?`-Stub | +| (hidden) | alles andere | nicht platziert | + +Beim Betreten eines `?`-Raums wird er `VISITED`; seine Nachbarn werden zu neuen +`KNOWN`-Stubs. Die Karte wächst eine Ringschicht voraus. + +## 6. Layout-Algorithmus & Konfliktbehandlung + +BFS vom Startraum über den **vollständigen** Exit-Graphen; jede Richtung verschiebt +die Gitterkoordinate (N→y−1, S→y+1, O→x+1, W→x−1). Hand-gebaute Welten sind +geometrisch konsistent; falls BFS zwei Räume auf dieselbe Zelle legt (nicht-planarer +Graph), **gewinnt der erste**, die Kollision wird geloggt, das Rendern läuft weiter +(kein Crash). Die Fog-of-War-Filterung läuft nach dem Layout; danach werden die +Koordinaten normalisiert (Verschiebung, sodass min = 0,0). + +## 7. Konsolen-Verhalten + +`map` / `m` druckt die ASCII-Karte on demand (keine persistente Region in der +Konsole). Die Box-Raumansicht behält ihre Inline-`Exits:`-Zeile – Konsolenspieler +müssen also nicht `map` tippen, um zu navigieren; die Karte ist Überblick-Bonus. In +der GUI ersetzt das Dauerpanel die Exit-Zeile. + +## 8. Fehlerbehandlung + +| Fall | Verhalten | +|---|---| +| Koordinaten-Kollision (nicht-planar) | erste Zuordnung gewinnt, Warnung geloggt, Rendern fährt fort | +| Startraum hat keine Exits | Karte zeigt nur die aktuelle Zelle | +| Sehr große Karte (viele Räume) | GUI-Panel scrollbar, auf aktuelle Zelle zentriert; ASCII so groß wie nötig | + +## 9. Testing + +- `MapLayout` (reine Logik): Koordinaten aus bekanntem Graphen; Fog-Zustände für eine + gegebene Visited-Menge; Kollisionsbehandlung; Normalisierung. +- `MapView`/Records: Immutabilität. +- `AsciiMap`: strukturelle Assertions auf bekanntem `MapView` (Namen vorhanden, + aktueller markiert, `?` für `KNOWN`), Farbe aus. +- `MapCommand`: über `TestIO` – erzeugt Karten-Ausgabe. +- `Player`: Besuchte-Räume-Tracking (Start + nach Bewegung). +- `SwingIO`/`MapPanel`: nicht unit-getestet (Graphics2D/EDT); manueller Smoke-Test. + +## 10. Offene Detailfragen (in Implementierung entscheidbar) + +- ASCII-Zellbreite/-höhe und Verbindungslänge (Lesbarkeit vs. Kompaktheit). +- Genaue Farben/Strichstärken im `MapPanel` (an Präsentations-Palette angelehnt).