# NPCs

Nicht-Spieler-Figuren — optionaler Bonusteil laut Aufgabenstellung. Hier mit Talk- und Give-Interaktion modelliert.

## Domain-Modell

```mermaid
classDiagram
    class Npc {
        -String id
        -String name
        -String description
        -String greeting
        -Map~String, NpcReaction~ reactions
        +talk(GameContext) void
        +receive(Item, GameContext) boolean
    }

    class NpcReaction {
        -Item consumes
        -Item gives
        -String response
        +apply(Player) void
    }

    Npc "1" --> "*" NpcReaction : reactions
```

- `reactions` ist `HashMap<String, NpcReaction>` mit dem Trigger-Item-id als Key. O(1) Nachschlagen beim `gib X an Y`.

## Interaktionen

### `talk <npc>`

- Sucht NPC im aktuellen Raum.
- Wirft `greeting`-Text aus.
- Mutiert nichts.

### `gib <item> an <npc>`

```mermaid
sequenceDiagram
    actor Spieler
    participant Cmd as GiveCommand
    participant P as Player
    participant N as Npc
    participant IO

    Spieler->>Cmd: give lamp old_man
    Cmd->>P: hasItem("lamp")?
    P-->>Cmd: yes
    Cmd->>N: receive(lamp)
    N->>N: reactions.get("lamp")
    alt Reaktion existiert
        N->>P: inventory.remove(lamp)
        N->>P: inventory.add(key)
        N-->>Cmd: response text
        Cmd->>IO: write(response)
    else keine Reaktion
        N-->>Cmd: false
        Cmd->>IO: write("The NPC does not react.")
    end
```

## YAML-Beispiel

```yaml
- id: old_man
  name: Old Man
  description: A stooped old man with a grey beard.
  greeting: |
    "Hello traveller. If you bring me the lamp,
    I will show you the way to the cellar."
  reactions:
    - onReceive: lamp
      response: |
        "Thank you! Here, take this key."
      gives: key
      consumes: lamp
```

Felder einer Reaktion:

| Feld | Pflicht | Bedeutung |
|---|---|---|
| `onReceive` | ja | Item-id, das ausgelöst werden muss |
| `response` | ja | Antworttext nach erfolgreicher Übergabe |
| `gives` | nein | Item-id, das der NPC zurückgibt |
| `consumes` | nein | Item-id, das aus Spielerinventar entfernt wird (oft = `onReceive`) |

## Speichern in Räumen

Räume halten ihre NPCs analog zu Items:

```java
private final LinkedHashMap<String, Npc> npcs = new LinkedHashMap<>();
```

- `LinkedHashMap` weil O(1)-Lookup beim `talk <npc>` *und* stabile Reihenfolge in der Raumbeschreibung („Here is: Old Man, Innkeeper").

## Erweiterungen (bewusst nicht im MVP)

- Bedingte Dialoge („wenn Quest X erledigt, sag Y")
- Kauf/Verkauf mit Gold
- NPC bewegt sich zwischen Räumen
- Mehrfache Reaktionsketten (NPC-Memory)

Diese würden ein eigenes Quest-/Event-System rechtfertigen. Für 3 Bonuspunkte überdimensioniert.

## Validierungsregeln

Wiederholung aus [yaml-schemas.md](yaml-schemas.md), hier explizit pro NPC:

1. `id` eindeutig in `npcs.yaml`
2. `greeting` nicht leer (sonst sinnloses NPC)
3. Jede `onReceive`-id existiert in `items.yaml`
4. Jede `gives`-id existiert in `items.yaml`
5. Jede `consumes`-id existiert in `items.yaml`
6. Innerhalb eines NPCs ist `onReceive` eindeutig (keine zwei Reaktionen auf dasselbe Item)
