docs: spec for map / mini-map (sub-project 2)
Fog-of-war map from the directional exit graph. MapLayout (BFS) produces a frontend-agnostic MapView; SwingIO draws it (Graphics2D MapPanel, replaces exits text), ConsoleIO mirrors it as ASCII via the map command. Visited tracking on Player. Item/NPC markers and locked-door styling deferred. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
151
Semesterprojekt/docs/superpowers/specs/2026-05-31-map-design.md
Normal file
151
Semesterprojekt/docs/superpowers/specs/2026-05-31-map-design.md
Normal file
@@ -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<Direction, Room>`). 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<RoomCell> cells,
|
||||
java.util.List<Connection> connections) {
|
||||
public MapView { cells = List.copyOf(cells); connections = List.copyOf(connections); }
|
||||
}
|
||||
```
|
||||
|
||||
- `map.MapLayout` – `static MapView compute(World world, String startRoomId, Set<String> 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<String> 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).
|
||||
Reference in New Issue
Block a user