# Item-Modell Hierarchie aus abstraktem `Item` und drei konkreten Subtypen. Liegt im Subpackage `thb.jeanluc.adventure.model.item`. ## Hierarchie ```mermaid classDiagram class Item { <> #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. ```java @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: ```java ReadableItem.builder() .id("letter").name("Letter").description("A crumpled paper.") .readText("Meet me at midnight.") .build(); ``` ## Jackson Polymorphism ```java @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.