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>
6.7 KiB
Spec: Karte / Mini-Map (Teilprojekt 2)
Stand: 2026-05-31. Zweites Teilprojekt aus 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 ruftsetMappro Zug (nebensetHud).- GUI:
MapPanel(Graphics2D) im Seitenpanel, scrollbar, auf aktuellen Raum zentriert. - Konsole: ASCII-Karte (
AsciiMap), respektiertGlyphMode.
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.
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.
// 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);paintComponentzeichnet.command.impl.MapCommand– bautMapView, ruftio.showMap(view).model.Player–Set<String> visitedRoomIds(LinkedHashSet, Reihenfolge stabil).
GameIO-Erweiterung (additiv)
/** 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überschreibtshowMap→print(AsciiMap.render(view, this.glyphs))(nutzt also seinen konfigurierten Glyph-Modus).setMapbleibt Default (no-op) → kein Karten-Spam pro Zug in der Konsole.SwingIOüberschreibtsetMap→MapPanelneu zeichnen;showMap→ no-op (das Panel ist ohnehin sichtbar).TestIOnutzt beide Defaults:setMap= no-op,showMapdruckt UNICODE-ASCII inoutputs→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 bekanntemMapView(Namen vorhanden, aktueller markiert,?fürKNOWN), Farbe aus.MapCommand: überTestIO– 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).