Semantic output model (Style/Span/StyledText) + RoomView/Hud views, GameIO extended additively (write(String) stays as default), ConsoleIO (ANSI + box-drawing, tiered glyph modes) and SwingIO (region layout, JTextPane, bundled font) renderers, command + TestIO migration. Map, menu, save, music explicitly deferred to later sub-projects. Also: capture "don't overwhelm / distribute info across regions" and "B baseline + ASCII for big moments" decisions in the backlog. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9.4 KiB
Spec: Präsentations-/Ausgabe-Schicht (Teilprojekt 1)
Stand: 2026-05-31. Erstes Teilprojekt aus enhancement-ideas.md.
Baut auf der v1.0-Baseline (Tag v1.0) auf.
1. Kontext & Ziel
GameIO bietet aktuell nur readLine() + write(String); beide Frontends
(ConsoleIO, SwingIO) schieben rohen Text durch. Ziel: reichere, strukturierte
Ausgabe in beiden Modi, ohne den für beide Frontends identischen Game-Loop
aufzugeben (Kernwert der Architektur).
Zielbild (bestätigt im Brainstorming):
- Konsole: Stufe B – Boxed Heading, gelabelte Sektionen, persistente HUD-Zeile; ASCII-Art nur für große Momente.
- GUI ist der Fokus (mehr Freiheit), Konsole wird weiter unterstützt.
- Semantische Farb-Rollen für Items / NPCs / Exits / Headings / Gefahr.
- Progressive Disclosure (nicht überladen) und Info auf Regionen verteilen (Karte ersetzt später die Exit-Zeile).
2. Scope
In Scope:
- Semantisches Ausgabe-Modell (
Style,Span,StyledText+ Builder). - High-Level-Views:
RoomView,Hud. - Erweiterung der
GameIO-Schnittstelle (additiv,write(String)bleibt als Default). ConsoleIOals Renderer: ANSI-Farben + Box-Drawing (Stufe B), HUD-Zeile, ASCII-Art-Banner-Mechanismus, gestufte Glyph-Fähigkeit.SwingIOals Renderer: Region-Layout (HUD oben, Output mittig, Seitenpanel rechts reserviert, Input unten),JTextPane/StyledDocument, gebündelter Font.- Migration der bestehenden Commands auf die neue API;
TestIOangepasst.
Out of Scope (jeweils eigene spätere Specs):
- Tatsächliches Zeichnen der Karte (Teilprojekt 2) – Seitenpanel ist nur Slot, zeigt bis dahin die Exit-Liste.
- Hauptmenü/Settings (6), Speichern/Laden (7), Musik (8).
- Neue Inhalte/Mechaniken (Quests, Licht, Item-Kombination).
Die Schnittstellen lassen bewusst Slots (Seitenpanel, HUD-Felder) für diese späteren Teile.
3. Architektur (Approach 2)
Commands erzeugen Bedeutung, kein Format. Ein Command baut ein semantisches
Objekt (StyledText oder eine View) und übergibt es an GameIO. Jedes Frontend
rendert es selbst: ConsoleIO → ANSI/Box-Drawing, SwingIO → Swing-Komponenten
und eigenes Layout. Der Game-Loop bleibt unverändert.
flowchart LR
CMD["Command<br/>(z.B. LookCommand)"] -- "baut" --> VIEW["RoomView / StyledText / Hud<br/>(semantisch, kein Format)"]
VIEW -- "GameIO.showRoom() / print() / setHud()" --> IO{{"GameIO"}}
IO --> CON["ConsoleIO<br/>ANSI + Box-Drawing"]
IO --> GUI["SwingIO<br/>JTextPane + Panels"]
4. Komponenten & API
Neues Subpackage io.text für das Modell und die Views (Style, Span,
StyledText, RoomView, Hud); die Renderer (ConsoleIO, SwingIO) bleiben in io.
4.1 Style (enum)
public enum Style { PLAIN, HEADING, ITEM, NPC, EXIT, DANGER, DIM }
Kleines, bewusst begrenztes Rollen-Set. Mapping auf konkrete Farben/Attribute liegt im jeweiligen Renderer, nicht am Style.
4.2 Span & StyledText
public record Span(String text, Style style) {}
public final class StyledText {
private final List<Span> spans; // immutable
public List<Span> spans() { ... }
public String plainText() { ... } // Spans konkateniert, ohne Format
public static StyledText of(String s) { ... } // ein PLAIN-Span
public static Builder builder() { ... }
public static final class Builder {
Builder plain(String s); Builder heading(String s);
Builder item(String s); Builder npc(String s);
Builder exit(String s); Builder danger(String s);
Builder dim(String s); StyledText build();
}
}
Beispiel:
io.print(StyledText.builder()
.plain("You pick up ").item("the brass lamp").plain(".").build());
Zeilenumbrüche stehen als \n im Text der Spans.
4.3 Views
public record RoomView(String name, String description,
List<String> items, List<String> npcs,
List<String> exits) {}
public record Hud(String location, int gold, int turn, boolean lightOn) {}
Views tragen bereits anzeigefertige Strings (Namen aufgelöst), aber kein
Layout. RoomView.exits versorgt vorerst das GUI-Seitenpanel; sobald Teilprojekt 2
die Karte zeichnet, blendet der GUI-Renderer die Exit-Sektion aus.
4.4 GameIO (erweitert, additiv)
public interface GameIO {
String readLine();
void print(StyledText text); // gestylter Ausgabe-Block
void showRoom(RoomView room); // High-Level-Raumansicht
void setHud(Hud hud); // HUD-Region aktualisieren
default void write(String s) { // Komfort + Rückwärtskompatibilität
print(StyledText.of(s));
}
}
write(String) bleibt als Default → bestehende Aufrufstellen kompilieren weiter;
Migration kann schrittweise erfolgen.
4.5 ConsoleIO (Renderer)
print: Spans → ANSI-Sequenzen (Rollen→Farbe), dannprintln.showRoom: Stufe-B-Layout mit Box-Drawing-Rahmen (Heading + Sektionen „You see / Here is / Exits"), Rollen-Farben.setHud: dim gefärbte Statuszeile ([ Kitchen · 0 gold · turn 7 · light: off ]).- ASCII-Art-Banner: Methode/Helfer für große Momente (Start, Finale); Kunst aus Ressourcen geladen. Mechanismus in Scope, konkrete Kunstwerke folgen mit Inhalten.
- Konfiguration (Defaults sicher, Verdrahtung mit Settings-Menü später):
ColorMode: an/aus; Auto-Erkennung (System.console() == null→ keine ANSI).GlyphMode:ASCII | UNICODE | GLYPH, DefaultUNICODE(Box-Drawing breit unterstützt; Nerd-Glyphen nur bei explizitem Opt-in). BeiASCIIdegradieren die Rahmen auf+,-,|statt der Unicode-Box-Drawing-Zeichen.
4.6 SwingIO (Renderer) + Layout
Region-Layout (bestätigt, Variante A):
- NORTH: HUD-Leiste (
JPanelmitJLabels) – Ort · Gold · Zug · Licht. - CENTER:
JTextPanemitStyledDocument(Farben/Stile viaAttributeSetproStyle), scrollbar. - EAST: Seitenpanel, reserviert für Karte; zeigt bis dahin
RoomView.exitsals Liste. - SOUTH: Eingabefeld (bestehende
LinkedBlockingQueue-Brücke bleibt).
Gebündelter Font: .ttf unter src/main/resources/fonts/, via
Font.createFont(Font.TRUETYPE_FONT, stream) geladen und registriert → Farben und
Glyphen funktionieren ohne Installation auf dem Spieler-System. Fallback auf
Font.MONOSPACED bei Ladefehler (geloggt).
print/showRoom schreiben gestylte Spans in das JTextPane; setHud aktualisiert
die Labels. Alle Swing-Mutationen via SwingUtilities.invokeLater (wie bisher).
5. Datenfluss
Command → baut RoomView/StyledText/Hud → GameIO.{showRoom|print|setHud}
→ Frontend-Renderer → Konsole (ANSI) bzw. GUI (Swing)
Game-Loop, Parser, Registry, Domain: unverändert.
6. Nerd-Font / Glyph-Strategie (zusammengefasst)
- GUI: Font mitliefern (bundled
.ttf) → Problem gelöst, keine Installation nötig. - Konsole: Font nicht kontrollierbar → gestufte Fallbacks (
ASCII→UNICODE-Box-Drawing →GLYPH). DefaultUNICODE. Keine harte Abhängigkeit von installierten Nerd Fonts.
7. Migration der Commands & Tests
LookCommand: stattStringBuildereineRoomViewbauen →io.showRoom(view).- Übrige Commands (
Take,Drop,Use,Read,Examine,Talk,Give,Inventory,Help):io.print(StyledText…)mit Rollen, wo es Mehrwert bringt; sonst genügt das Default-write(String). Schrittweise, nicht Big-Bang. TestIO: implementiertprint/showRoom/setHuddurch Flattening auf Klartext (StyledText.plainText()bzw. RoomView→Textblock), abgelegt inoutputs.lastOutput()/allOutput()liefern weiterhin Klartext → die bestehenden.contains(...)-Assertions der 67 Tests bleiben gültig.
8. Fehlerbehandlung
| Fall | Verhalten |
|---|---|
| Gebündelter Font nicht ladbar | Log-Warnung, Fallback Font.MONOSPACED |
| Terminal ohne ANSI / Umleitung | Auto-Erkennung → Farben aus, reiner Text |
GlyphMode=GLYPH ohne passende Terminal-Font |
Nutzer-Opt-in; kein Auto-Fallback nötig, Default bleibt UNICODE |
null-Text an StyledText.of |
IllegalArgumentException (wie bestehende Konventionen) |
9. Testing-Strategie
- Unit:
StyledText-Builder &plainText(),Span, Immutabilität vonRoomView/Hud. - ConsoleIO: Rendern einer bekannten
RoomView/Hudmit Farbe AUS gegen einenStringWriter/PrintStream(Test-Konstruktor existiert bereits) → strukturelle Assertions (Heading, Labels, Exit-Namen). - Command-Tests: laufen unverändert über das angepasste
TestIO(Klartext). - SwingIO: nicht unit-getestet (EDT/Swing); Logik dünn halten, manueller
Smoke-Test. Verifikation per
mvn exec:java@gui.
10. Handoffs an spätere Teilprojekte
- Karte (2): GUI-Seitenpanel + künftige
showMap(MapView)-Methode;RoomView.exitsist bis dahin Platzhalter-Quelle. - Menü/Settings (6): verdrahtet später
ColorMode/GlyphMode/Lautstärke. - Save (7), Musik (8): nutzen die HUD/Region-Slots, hier nicht implementiert.
11. Offene Detailfragen (in Implementierung entscheidbar)
- Konkrete Wahl des gebündelten Fonts (freier Monospace bzw. Nerd-Font-Variante, Lizenz prüfen).
- Exakte ANSI-Farbpalette pro Rolle.
- Ob
Hudschon einlightOnführt, obwohl die Licht-Mechanik erst mit den Inhalten kommt → Feld vorsehen, vorerst konstantfalse/ausgeblendet.