# Item-Modell

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

## Hierarchie

```mermaid
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.

```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.
