Files
Jander_Semester2/Semesterprojekt/docs/superpowers/specs/2026-05-31-presentation-layer-design.md
Jean-Luc Makiola 705d5f9db1 docs: spec for presentation/output layer (sub-project 1)
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>
2026-05-31 20:06:36 +02:00

9.4 KiB
Raw Blame History

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).
  • ConsoleIO als Renderer: ANSI-Farben + Box-Drawing (Stufe B), HUD-Zeile, ASCII-Art-Banner-Mechanismus, gestufte Glyph-Fähigkeit.
  • SwingIO als 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; TestIO angepasst.

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), dann println.
  • 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, Default UNICODE (Box-Drawing breit unterstützt; Nerd-Glyphen nur bei explizitem Opt-in). Bei ASCII degradieren die Rahmen auf +, -, | statt der Unicode-Box-Drawing-Zeichen.

4.6 SwingIO (Renderer) + Layout

Region-Layout (bestätigt, Variante A):

  • NORTH: HUD-Leiste (JPanel mit JLabels) Ort · Gold · Zug · Licht.
  • CENTER: JTextPane mit StyledDocument (Farben/Stile via AttributeSet pro Style), scrollbar.
  • EAST: Seitenpanel, reserviert für Karte; zeigt bis dahin RoomView.exits als 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 (ASCIIUNICODE-Box-Drawing → GLYPH). Default UNICODE. Keine harte Abhängigkeit von installierten Nerd Fonts.

7. Migration der Commands & Tests

  • LookCommand: statt StringBuilder eine RoomView bauen → 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: implementiert print/showRoom/setHud durch Flattening auf Klartext (StyledText.plainText() bzw. RoomView→Textblock), abgelegt in outputs. 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 von RoomView/Hud.
  • ConsoleIO: Rendern einer bekannten RoomView/Hud mit Farbe AUS gegen einen StringWriter/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.exits ist 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 Hud schon ein lightOn führt, obwohl die Licht-Mechanik erst mit den Inhalten kommt → Feld vorsehen, vorerst konstant false/ausgeblendet.