Files
Jander_Semester2/Semesterprojekt/docs/item-model.md
Jean-Luc Makiola 83643a192f semesterprojekt: implement full text adventure (phases 1-7)
Walking skeleton through Swing GUI: YAML-driven world (4 rooms,
4 items, 1 NPC), HashMap command dispatch with parser, three-tier
item hierarchy (readable / switchable / plain), and end-to-end
NPC give/receive flow. 67 tests green.
2026-05-25 21:37:59 +02:00

3.7 KiB

Item-Modell

Hierarchie aus abstraktem Item und drei konkreten Subtypen. Liegt im Subpackage thb.jeanluc.adventure.model.item.

Hierarchie

classDiagram
    class Item {
        <<abstract>>
        #String id
        #String name
        #String description
        +use(GameContext)* void
    }

    class ReadableItem {
        -String readText
        +use(GameContext) void
    }

    class SwitchableItem {
        -boolean state
        -boolean initialState
        -String onText
        -String offText
        +use(GameContext) void
        +isOn() boolean
    }

    class PlainItem {
        +use(GameContext) void
    }

    Item <|-- ReadableItem
    Item <|-- SwitchableItem
    Item <|-- PlainItem

Felder pro Klasse

Item (abstract)

Feld Typ Hinweis
id String unique slug, Lookup-Key
name String Anzeigename
description String examine-Output

Abstrakte Methode: public abstract void use(GameContext ctx)

ReadableItem

Feld Typ Hinweis
readText String wird beim read/use ausgegeben

use() schreibt readText über ctx.io().

SwitchableItem

Feld Typ Hinweis
state boolean aktueller Zustand (mutable)
initialState boolean aus YAML, im Konstruktor an state durchgereicht
onText String Nachricht beim Einschalten
offText String Nachricht beim Ausschalten

use() toggelt state und schreibt den entsprechenden Text.

Bewusste Entscheidung: boolean statt enum SwitchState. Wenn später BROKEN o.ä. nötig wird, refactoren.

PlainItem

Keine eigenen Felder. use() schreibt eine generische Nachricht („You can't use the X by itself."). Existiert, damit alle Items polymorph use() haben und das YAML einen konsistenten type:-Discriminator hat.

Lombok-Setup

@SuperBuilder ist Pflicht, weil normales @Builder Vererbung nicht beherrscht.

@Getter
@SuperBuilder
@RequiredArgsConstructor
public abstract class Item {
    protected final String id;
    protected final String name;
    protected final String description;

    public abstract void use(GameContext ctx);
}

@Getter
@SuperBuilder
public class ReadableItem extends Item {
    private final String readText;

    @Override
    public void use(GameContext ctx) {
        ctx.getIo().write(readText);
    }
}

Konstruktion:

ReadableItem.builder()
    .id("letter").name("Letter").description("A crumpled paper.")
    .readText("Meet me at midnight.")
    .build();

Jackson Polymorphism

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = ReadableItem.class, name = "readable"),
    @JsonSubTypes.Type(value = SwitchableItem.class, name = "switchable"),
    @JsonSubTypes.Type(value = PlainItem.class, name = "plain")
})
public abstract class Item { ... }

YAML-Pflichtfeld pro Item: type: readable|switchable|plain.

Verworfen / bewusst nicht im MVP

  • use X on Y (targeted use) — Items kennen ihren Kontext über GameContext, kein zweites Item als Parameter.
  • Hidden Items (sichtbar erst nach Aktion) — keine visible-Flag, alle Items sofort sichtbar.
  • Item kennt seinen Standort — Items sind „dumm", nur Room/Player wissen wer sie hält.
  • Item-State beeinflusst Raumbeschreibung (z.B. „Cellar dark unless lamp on") — wenn nötig, später per Conditions-System.
  • hasBeenRead-Tracking — Lesen bleibt idempotent.

Player-Input-Matching

Player tippt take lamp → Match gegen id. Multi-Word-Items haben snake_case ids, also take oil_lamp.

Alias-Feld (aliases: [lamp, oil]) ist YAGNI bis ein konkreter Bedarf entsteht.