Files
ashen-realms/docs/Ashen_Realms_NPC_System_Specification_V1.md
Bastian Wagner 8a5f57fa1c specs
2026-08-20 16:17:19 +02:00

1334 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Ashen Realms NPC System Specification V1
## Zweck dieser Dokumentation
Dieses Dokument definiert das NPC-System von **Ashen Realms** für den ersten erweiterten Vertical Slice und die darauf aufbauende Spielarchitektur.
Es beschreibt:
- das gemeinsame NPC-Grundmodell
- die Trennung zwischen NPC-Definition und spielerspezifischem Zustand
- NPC-Fähigkeiten statt Klassenvererbung
- Händler, Questgeber und gemischte Rollen
- Dialoge und Dialogbedingungen
- Ruf- und Freischaltbedingungen
- Materialtausch und Verkaufslogik
- Anbindung des Taschensystems
- wiederverwendbare Conditions
- technische Datenmodelle und Services
- den empfohlenen V1-Scope
Das System folgt den bestehenden Grundsätzen von Ashen Realms:
- Content ist datengetrieben.
- Spiellogik ist serverautoritativ.
- Content-Definitionen und Player-State bleiben getrennt.
- NPCs sollen Teil der Welt sein und nicht nur UI-Schaltflächen mit Portrait.
- Neue Spezialfälle sollen möglichst über wiederverwendbare Bausteine modelliert werden.
---
# 1. Designziel
NPCs sind zentrale Interaktionspunkte der Welt.
Sie können unter anderem:
- Quests anbieten
- Quests abschließen
- Gegenstände verkaufen
- Materialien ankaufen oder eintauschen
- Ruf prüfen
- besondere Waren freischalten
- Taschen verkaufen oder vergeben
- Informationen geben
- Story und Lore vermitteln
- Reisen oder andere Dienste anbieten
Ein NPC darf mehrere dieser Funktionen gleichzeitig besitzen.
Beispiel:
**Elyra, die verbannte Jägerin** kann gleichzeitig:
- Story-NPC sein
- Jagdquests anbieten
- Materialien annehmen
- Dämmerwald-Ruf berücksichtigen
- Gebietsausrüstung verkaufen
- besondere Angebote nach Quests freischalten
Grundsatz:
> **NPCs sind Personen mit mehreren Fähigkeiten keine voneinander getrennten NPC-Klassen.**
---
# 2. Keine klassische NPC-Vererbung
Für das NPC-System wird ausdrücklich keine tiefe Klassenhierarchie verwendet.
Nicht vorgesehen:
```ts
class Npc {}
class MerchantNpc extends Npc {}
class QuestNpc extends Npc {}
class QuestMerchantNpc extends MerchantNpc {}
```
Dieses Modell skaliert schlecht, sobald ein NPC mehrere Rollen kombiniert.
Stattdessen verwendet Ashen Realms:
> **Composition / Capability-based NPC design**
Ein NPC besitzt eine gemeinsame Identität und kann über verknüpfte Daten verschiedene Fähigkeiten erhalten.
Beispiel:
```text
NPC
├── Dialogue
├── Quest Assignments
├── Shop
├── Resource Exchange
├── Reputation Requirements
└── Player-specific State
```
---
# 3. NPC Definition
Jeder NPC besitzt eine persistierte Content-Definition.
Vorgeschlagenes Modell:
```ts
NpcDefinition {
id: string;
key: string;
name: string;
title?: string;
description?: string;
locationId: string;
factionKey?: string;
portraitPath: string;
artworkPath?: string;
dialogueProfileId?: string;
enabled: boolean;
createdAt: Date;
updatedAt: Date;
}
```
## Pflichtfelder
| Feld | Bedeutung |
|---|---|
| `id` | interne UUID |
| `key` | stabiler fachlicher Key |
| `name` | sichtbarer NPC-Name |
| `locationId` | aktueller Standort |
| `portraitPath` | Portrait für UI und Dialog |
| `enabled` | NPC aktiv/inaktiv |
## Optionale Felder
| Feld | Bedeutung |
|---|---|
| `title` | z. B. „Händler der Grenzwacht“ |
| `description` | kurze Beschreibung |
| `factionKey` | Fraktionszugehörigkeit |
| `artworkPath` | größeres Artwork für lokale Ansicht |
| `dialogueProfileId` | Dialogdefinition |
---
# 4. Stabiler NPC-Key
Jeder NPC benötigt einen stabilen fachlichen Key.
Beispiele:
```text
captain-garrick
merchant-borin
elyra-exiled-huntress
brother-caelan
```
Dieser Key darf in:
- Seeds
- Quests
- Dialogbedingungen
- Shopbedingungen
- Flags
- Tests
verwendet werden.
Interne UUIDs sollen nicht zur fachlichen Referenzierung von Content benutzt werden.
---
# 5. NPC Capabilities
NPC-Fähigkeiten werden nicht über Vererbung bestimmt.
Für V1 und spätere Erweiterungen sind folgende Capabilities vorgesehen:
| Capability | Funktion |
|---|---|
| `DIALOGUE` | NPC kann angesprochen werden |
| `QUEST_GIVER` | bietet Quests an |
| `QUEST_TURN_IN` | nimmt Quests entgegen |
| `MERCHANT` | besitzt normalen Shop |
| `REPUTATION_MERCHANT` | Angebote hängen von Ruf ab |
| `RESOURCE_EXCHANGE` | tauscht Materialien gegen Belohnungen |
| `BAG_MERCHANT` | verkauft oder vergibt Taschen |
| `LORE` | bietet Story-/Weltinformationen |
| `TRAINER` | spätere Skills/Fähigkeiten |
| `TRAVEL` | spätere Reiseleistung |
| `SERVICE` | Heilung, Bank, Reparatur etc. |
| `EVENT` | zeit- oder weltzustandsabhängige Funktion |
Capabilities sind primär beschreibend.
Die tatsächliche Funktion entsteht durch verknüpfte Daten wie:
- `NpcShop`
- `NpcQuestAssignment`
- `NpcDialogueProfile`
- `NpcExchangeProfile`
---
# 6. NPC und Standort
Ein NPC befindet sich grundsätzlich an einem Ort.
V1:
```text
NpcDefinition.locationId -> LocationDefinition.id
```
Später möglich:
- NPC wechselt abhängig von Weltzustand den Ort
- NPC erscheint nur während einer Quest
- NPC wird nach Storyfortschritt ersetzt
- NPC verschwindet temporär
Für V1 wird der Standort statisch gespeichert.
---
# 7. Trennung von Content und Player-State
Ein NPC ist globaler Content.
Der individuelle Zustand eines Spielers gegenüber diesem NPC ist separater Player-State.
Nicht in `NpcDefinition` speichern:
- ob ein Spieler den NPC bereits getroffen hat
- persönliche Beziehung
- individuelle Dialogflags
- persönliche Freischaltungen
Dafür existiert optional:
```ts
CharacterNpcState {
id: string;
characterId: string;
npcId: string;
relationValue?: number;
firstMetAt?: Date;
lastInteractionAt?: Date;
flags: Record<string, boolean | string | number>;
}
```
---
# 8. Ruf ist nicht automatisch NPC-Beziehung
Ashen Realms unterscheidet ausdrücklich zwischen:
## Welt-/Gebiet-/Fraktionsruf
Beispiele:
```text
World Renown
Graufurt Reputation
Ashen Fields Reputation
Duskwood Reputation
Faction Reputation
```
und
## Persönlicher NPC-Beziehung
Beispiele:
```text
Borin kennt den Spieler
Elyra vertraut dem Spieler
Caelan reagiert auf eine Storyentscheidung
```
Ruf ist ein übergeordnetes Progressionssystem.
NPC-Beziehung ist optionaler individueller Zustand.
Ein Händler kann daher verlangen:
```text
Graufurt Reputation >= 3
```
ohne dass der Spieler automatisch „3 Ruf bei Borin“ besitzt.
---
# 9. Character Reputation
Ruf wird unabhängig von NPCs gespeichert.
Beispielmodell:
```ts
CharacterReputation {
id: string;
characterId: string;
type: ReputationType;
targetKey: string;
value: number;
}
```
Mögliche Typen:
```ts
enum ReputationType {
WORLD = 'WORLD',
REGION = 'REGION',
FACTION = 'FACTION',
}
```
Beispiele:
```text
WORLD / global / 4
REGION / graufurt / 3
REGION / duskwood / 2
FACTION / border-watch / 1
```
---
# 10. NPC Dialogue System
NPCs sollen nicht nur statische Texte besitzen.
Dialoge reagieren auf:
- Quests
- Ruf
- Items
- Flags
- besiegte Gegner oder Bosse
- Storyfortschritt
- erste Begegnung
Vorgeschlagenes Modell:
```ts
NpcDialogueProfile {
id: string;
key: string;
npcId: string;
}
```
```ts
DialogueNode {
id: string;
profileId: string;
key: string;
text: string;
priority: number;
conditions: GameCondition[];
actions: DialogueAction[];
responses: DialogueResponse[];
}
```
---
# 11. Dialogpriorität
Mehrere Dialoge können gleichzeitig gültig sein.
Daher besitzt jeder Node eine Priorität.
Beispiel:
```text
1000 Questabschluss verfügbar
900 Quest aktiv
800 neue wichtige Quest
500 Ruf-basierter Dialog
100 Standarddialog
```
Der Server ermittelt den höchstpriorisierten gültigen Dialog.
Dadurch kann ein NPC situationsabhängig reagieren, ohne große If-/Else-Blöcke im Code.
---
# 12. Dialogue Responses
Ein Dialog kann mehrere auswählbare Antworten besitzen.
```ts
DialogueResponse {
id: string;
nodeId: string;
text: string;
targetNodeKey?: string;
conditions?: GameCondition[];
actions?: DialogueAction[];
}
```
Beispiele:
```text
„Zeig mir deine Waren.“
„Was weißt du über die Aschengrube?“
„Ich habe die Felle gebracht.“
„Leb wohl.“
```
---
# 13. Dialogue Actions
Dialoge dürfen Aktionen auslösen.
V1-relevante Actions:
```text
START_QUEST
COMPLETE_QUEST
OPEN_SHOP
OPEN_EXCHANGE
GRANT_ITEM
SET_FLAG
```
Später möglich:
```text
START_TRAVEL
OPEN_TRAINER
OPEN_SERVICE
CHANGE_RELATION
DISCOVER_LOCATION
START_EVENT
```
Beispiel:
```json
{
"type": "OPEN_SHOP",
"shopKey": "borin-general-store"
}
```
---
# 14. Quest-Zuordnung zu NPCs
Questgeber und Questabgabe werden getrennt modelliert.
Nicht vorgesehen:
```ts
npc.isQuestGiver = true;
```
Stattdessen:
```ts
NpcQuestAssignment {
id: string;
npcId: string;
questId: string;
role: NpcQuestRole;
}
```
```ts
enum NpcQuestRole {
OFFER = 'OFFER',
TURN_IN = 'TURN_IN',
PROGRESS = 'PROGRESS',
}
```
Dadurch kann eine Quest mehrere NPCs verwenden.
Beispiel:
```text
Garrick -> OFFER
Borin -> PROGRESS
Garrick -> TURN_IN
```
---
# 15. Händler-System
Ein Shop ist kein Spezialtyp von NPC.
Ein NPC kann null, einen oder später mehrere Shops besitzen.
```ts
NpcShop {
id: string;
key: string;
npcId: string;
name: string;
enabled: boolean;
}
```
```ts
ShopOffer {
id: string;
shopId: string;
itemDefinitionId: string;
currencyType: string;
price: number;
quantity?: number;
repeatable: boolean;
conditions: GameCondition[];
}
```
---
# 16. Rufbasierte Shop-Angebote
Nicht mehrere Shops pro Rufstufe anlegen.
Stattdessen erhält jedes Angebot eigene Conditions.
Beispiel:
```text
Großer Fellbeutel
Preis: 140 Silber
Bedingung: Graufurt Ruf >= 3
```
Oder:
```text
Dämmerjäger-Kapuze
Preis: 28 Dämmermarken
Bedingungen:
- Dämmerwald Ruf >= 4
- Quest „Graufangs Spur“ abgeschlossen
```
Dadurch bleiben Shops vollständig datengetrieben.
---
# 17. Resource Exchange
Mit dem neuen Rufsystem können bestimmte NPCs Materialien annehmen.
Dafür wird kein normaler Item-Shop missbraucht.
Vorgesehen:
```ts
NpcExchangeProfile {
id: string;
key: string;
npcId: string;
}
```
```ts
ExchangeRule {
id: string;
profileId: string;
inputItemId: string;
inputQuantity: number;
silverReward?: number;
worldRenownReward?: number;
regionReputationReward?: number;
factionReputationReward?: number;
conditions: GameCondition[];
}
```
Beispiel:
```text
5 × Aschenfell
→ 18 Silber
→ 10 Graufurt-Ruf
→ 2 Weltruhm
```
Die genauen Werte werden separat gebalanced.
---
# 18. Verbindung zum Taschensystem
Das NPC-System muss das Taschensystem unterstützen.
NPCs können:
- Taschen verkaufen
- Taschen einmalig vergeben
- Taschen über Quests freischalten
- größere Taschen erst ab Rufstufen anbieten
Beispiel:
```text
Borin
├── Einfacher Fellbeutel
│ └── über Intro-Quest verfügbar
├── Verstärkter Fellbeutel
│ └── Graufurt Ruf >= 2
└── Großer Jägerbeutel
└── Graufurt Ruf >= 4
```
Taschen selbst bleiben Teil des Item-/Bag-Systems.
Der NPC kontrolliert nur:
- Angebot
- Freischaltung
- Preis
- Questbezug
---
# 19. Game Conditions
NPC-Systeme verwenden eine gemeinsame Condition Engine.
V1 Condition Types:
```ts
enum GameConditionType {
QUEST_ACTIVE = 'QUEST_ACTIVE',
QUEST_COMPLETED = 'QUEST_COMPLETED',
HAS_ITEM = 'HAS_ITEM',
REGION_REPUTATION = 'REGION_REPUTATION',
WORLD_RENOWN = 'WORLD_RENOWN',
FLAG_SET = 'FLAG_SET',
BOSS_DEFEATED = 'BOSS_DEFEATED',
LOCATION_DISCOVERED = 'LOCATION_DISCOVERED',
}
```
Später möglich:
```text
FACTION_REPUTATION
NPC_RELATION
HAS_EQUIPMENT
BAG_OWNED
MONSTER_KILL_COUNT
ACHIEVEMENT_COMPLETED
EVENT_ACTIVE
TIME_WINDOW
```
---
# 20. Condition Struktur
Conditions sollten möglichst generisch gespeichert werden.
Beispiel:
```ts
GameCondition {
type: GameConditionType;
key?: string;
operator?: ComparisonOperator;
value?: string | number | boolean;
}
```
Beispiel:
```json
{
"type": "REGION_REPUTATION",
"key": "graufurt",
"operator": "GTE",
"value": 3
}
```
---
# 21. Condition Evaluation
Eine zentrale serverseitige Komponente wertet Conditions aus.
```ts
GameConditionService {
evaluate(
characterId: string,
conditions: GameCondition[],
): Promise<boolean>;
}
```
Sie kann wiederverwendet werden für:
- Dialoge
- Shop-Angebote
- Quests
- Materialtausch
- Reiseverbindungen
- Ortsinteraktionen
- Bosszugänge
- spätere Events
Grundsatz:
> **Keine separaten Condition-Systeme für jedes Feature bauen.**
---
# 22. NPC Interaction API
Der Client soll NPC-Logik nicht selbst auswerten.
Mögliche API-Endpunkte:
```text
GET /api/locations/:locationId/npcs
GET /api/npcs/:npcId
GET /api/npcs/:npcId/interaction
POST /api/npcs/:npcId/dialogue/:nodeId/respond
```
Zusätzlich über separate Fachmodule:
```text
GET /api/npcs/:npcId/shop
POST /api/npcs/:npcId/shop/purchase
GET /api/npcs/:npcId/exchange
POST /api/npcs/:npcId/exchange
```
Questaktionen können über das Questmodul laufen.
---
# 23. NPC Interaction Response
Ein NPC-Interaction-Response kann enthalten:
```ts
interface NpcInteractionDto {
npc: {
id: string;
key: string;
name: string;
title?: string;
portraitPath: string;
};
dialogue?: DialogueNodeDto;
availableActions: NpcActionDto[];
}
```
Beispiel Actions:
```text
TALK
OPEN_SHOP
OPEN_EXCHANGE
VIEW_QUESTS
```
Der Server entscheidet, welche Actions aktuell verfügbar sind.
---
# 24. NPCs in der lokalen Ortsansicht
NPCs werden in der lokalen Ortsansicht sichtbar dargestellt.
Ein NPC-Eintrag zeigt mindestens:
- Portrait oder Artwork
- Name
- Titel/Rolle
- Interaktionsstatus
Mögliche Marker:
```text
! neue Quest
? Quest abgabebereit
¤ Händler
↔ Materialtausch
● neue Dialoginformation
```
Die UI soll nicht jeden Capability-Typ als eigenes dauerhaftes Symbol erzwingen.
Nur relevante Aktionen werden gezeigt.
---
# 25. NPC-Verfügbarkeit
V1 sind NPCs grundsätzlich dauerhaft verfügbar, solange:
```text
enabled = true
```
Später können Conditions für NPC-Sichtbarkeit ergänzt werden.
Beispiele:
- NPC erscheint erst nach Quest
- NPC stirbt oder verschwindet
- anderer NPC ersetzt ihn
- NPC wechselt Ort
- Event-NPC erscheint temporär
Dafür kann später ergänzt werden:
```ts
NpcDefinition.visibilityConditions
```
Für V1 nicht erforderlich.
---
# 26. Beispiel Borin
```text
Borin
Händler der Grenzwacht
Ort: Graufurt
```
Capabilities:
```text
DIALOGUE
MERCHANT
RESOURCE_EXCHANGE
BAG_MERCHANT
```
Funktionen:
```text
Shop
├── Heiltrank
├── einfache Ausrüstung
├── Fellbeutel
└── größere Taschen über Ruf
Exchange
├── Aschenfell
├── zähes Fell
└── weitere Tiermaterialien
```
Dialogzustände:
```text
Erste Begegnung
→ reservierter Standarddialog
Quest-Einführung aktiv
→ „Garrick schickt dich also.“
Graufurt-Ruf >= 2
→ freundlicherer Dialog
Graufurt-Ruf >= 4
→ Spezialangebot verfügbar
```
---
# 27. Beispiel Elyra
```text
Elyra, die verbannte Jägerin
Ort: Verfallener Jägerschrein
```
Capabilities:
```text
DIALOGUE
QUEST_GIVER
QUEST_TURN_IN
RESOURCE_EXCHANGE
REPUTATION_MERCHANT
LORE
```
Mögliche Funktionen:
```text
Quests
├── Jagd auf Dämmerwölfe
├── Schwarzmähnen-Spur
└── Graufang
Exchange
├── Dämmerfell
├── Giftdrüse
└── Elite-Trophäen
Shop
├── Heiltrank
├── Gegengift
├── Bestienbeutel
└── Dämmerjäger-Items
```
---
# 28. Beispiel Bruder Caelan
```text
Bruder Caelan
Letzter Hüter
Ort: Kapelle der letzten Wacht
```
Capabilities:
```text
DIALOGUE
QUEST_GIVER
QUEST_TURN_IN
REPUTATION_MERCHANT
LORE
```
Mögliche Besonderheiten:
- reagiert auf entdeckte Ruinen
- erklärt Untote und Siegelbruchstücke
- bietet spezielle Waren nach Sir Varos
- besitzt Storydialoge zum Knochenfürsten
---
# 29. Technische Modulstruktur
Empfohlene Backend-Struktur:
```text
apps/api/src/
├── npcs/
│ ├── entities/
│ │ ├── npc-definition.entity.ts
│ │ ├── npc-dialogue-profile.entity.ts
│ │ ├── dialogue-node.entity.ts
│ │ └── character-npc-state.entity.ts
│ │
│ ├── npc.service.ts
│ ├── npc.controller.ts
│ └── npc.module.ts
├── shops/
│ ├── entities/
│ │ ├── npc-shop.entity.ts
│ │ └── shop-offer.entity.ts
│ └── ...
├── reputation/
│ ├── entities/
│ │ └── character-reputation.entity.ts
│ └── ...
├── exchanges/
│ ├── entities/
│ │ ├── npc-exchange-profile.entity.ts
│ │ └── exchange-rule.entity.ts
│ └── ...
├── quests/
│ └── npc-quest-assignment.entity.ts
└── conditions/
├── game-condition.types.ts
├── game-condition.service.ts
└── game-condition.service.spec.ts
```
---
# 30. Verantwortlichkeiten der Services
## `NpcService`
Verantwortlich für:
- NPCs eines Ortes laden
- NPC-Definition laden
- aktuell verfügbare Interaktionen bestimmen
- relevanten Dialog ermitteln
Nicht verantwortlich für:
- Kauftransaktionen
- Rufberechnung
- Questfortschritt
- Itemtransfer
---
## `GameConditionService`
Verantwortlich für:
- Conditions serverseitig auswerten
- wiederverwendbare Freischaltlogik
---
## `ShopService`
Verantwortlich für:
- verfügbare Angebote bestimmen
- Conditions prüfen
- Preise validieren
- Kauf atomar durchführen
---
## `ExchangeService`
Verantwortlich für:
- Materialbesitz prüfen
- Taschen-/Inventarbezug beachten
- Items entfernen
- Silber/Ruf gewähren
- Transaktion atomar durchführen
---
# 31. Transaktionsregeln
Folgende Aktionen müssen serverseitig und atomar ausgeführt werden:
```text
Shop Purchase
Material Exchange
Quest Reward
Item Grant
Reputation Grant
```
Beispiel Materialtausch:
```text
Material prüfen
→ Material entfernen
→ Silber gewähren
→ Regionalruf gewähren
→ Weltruhm gewähren
→ Commit
```
Ein Teilzustand darf nicht persistiert werden.
---
# 32. Seeds
NPC-Content wird über reproduzierbare Seeds angelegt.
Empfohlene Dateien:
```text
apps/api/src/database/seeds/
├── npcs.seed.ts
├── npc-dialogues.seed.ts
├── npc-shops.seed.ts
├── npc-exchanges.seed.ts
└── npc-quest-assignments.seed.ts
```
Seeds müssen idempotent sein.
Fachliche Verknüpfungen verwenden stabile Keys.
---
# 33. Tests
Mindestens folgende Fälle sollten getestet werden:
## NPC
- NPCs eines Ortes werden korrekt geladen
- deaktivierte NPCs erscheinen nicht
- NPC besitzt mehrere Capabilities gleichzeitig
## Dialog
- Standarddialog wird angezeigt
- höherpriorisierter Questdialog überschreibt Standarddialog
- Rufdialog erscheint erst bei erfüllter Condition
## Shop
- Rufanforderung wird serverseitig geprüft
- Questanforderung wird geprüft
- gesperrtes Item kann nicht direkt über API gekauft werden
## Exchange
- benötigte Materialien werden geprüft
- Materialien werden entfernt
- Silber wird vergeben
- Ruf wird vergeben
- Transaktion bleibt atomar
## Quest
- ein NPC kann Questgeber sein
- anderer NPC kann Questfortschritt auslösen
- dritter oder erster NPC kann Questabschluss übernehmen
---
# 34. V1 Scope
Für die erste Implementierung wird bewusst nur ein kleiner Teil benötigt.
## Implementieren
```text
NpcDefinition
NpcService
NpcController
NpcQuestAssignment
NpcShop
ShopOffer
NpcExchangeProfile
ExchangeRule
GameConditionService
Conditions:
- QUEST_ACTIVE
- QUEST_COMPLETED
- REGION_REPUTATION
- WORLD_RENOWN
- FLAG_SET
```
Dialogsystem zunächst einfach:
```text
priorisierte DialogNodes
+ Conditions
+ OPEN_SHOP
+ OPEN_EXCHANGE
+ START_QUEST
+ COMPLETE_QUEST
```
---
# 35. Noch nicht für V1 implementieren
Noch nicht notwendig:
- komplexe persönliche NPC-Beziehungen
- Romance
- NPC-Tagesabläufe
- freie NPC-Bewegung
- KI-gesteuerte NPCs
- prozedurale Dialoge
- Sprachausgabe
- NPC-Inventare als physische Simulation
- dynamische Preise
- Verhandlungssystem
- Diebstahl
- Begleiter
- NPC-Tod mit komplexen Weltfolgen
- Echtzeit-Weltzustände
`CharacterNpcState` kann vorbereitet, aber erst später vollständig genutzt werden.
---
# 36. UI-Prinzip
Ein NPC-Screen oder NPC-Panel soll weiterhin den visuellen Grundsätzen von Ashen Realms folgen.
NPCs werden über:
- großes Portrait oder Artwork
- Namen
- Titel
- Dialogtext
- klar sichtbare aktuelle Aktionen
präsentiert.
Nicht geeignet:
- reine Tabellenansicht
- generische Admin-Card
- zehn gleichgewichtete Buttons
Der NPC soll wie ein Teil der Welt wirken.
---
# 37. Architekturregeln
1. **NPC-Funktionen werden über Composition modelliert, nicht über Klassenvererbung.**
2. **NPC-Definition und Player-State bleiben getrennt.**
3. **Ruf ist nicht automatisch persönliche NPC-Beziehung.**
4. **Ein NPC darf mehrere Rollen gleichzeitig besitzen.**
5. **Questgeber und Questabgabe sind getrennte Zuordnungen.**
6. **Shopangebote besitzen eigene Conditions.**
7. **Materialtausch ist ein eigenes System und kein normaler Shopverkauf.**
8. **Conditions werden zentral wiederverwendbar ausgewertet.**
9. **Der Client entscheidet nicht über Freischaltungen.**
10. **Alle relevanten Transaktionen sind serverautoritativ und atomar.**
11. **Content verwendet stabile fachliche Keys.**
12. **NPC-Spezialverhalten soll möglichst datengetrieben entstehen.**
---
# 38. Zielbild
Das NPC-System soll langfristig folgende Interaktionen ermöglichen, ohne für jeden NPC neuen Spezialcode zu schreiben:
```text
Spieler spricht mit NPC
Server prüft Quest-, Ruf- und Weltzustand
passender Dialog wird gewählt
verfügbare Aktionen werden angezeigt
Spieler kann z. B.
├── Quest annehmen
├── Quest abgeben
├── Materialien eintauschen
├── Shop öffnen
├── Tasche kaufen
└── Lore lesen
```
Der gleiche technische Unterbau kann für Borin, Elyra, Bruder Caelan und zukünftige NPCs wiederverwendet werden.
---
# 39. Kurzfassung
Ashen Realms verwendet ein gemeinsames NPC-Grundmodell.
NPC-Typen wie Händler oder Questgeber werden **nicht** über Vererbung umgesetzt.
Stattdessen besitzt ein NPC kombinierbare Funktionen:
```text
NPC
+ Dialogue
+ Quests
+ Shop
+ Resource Exchange
+ Reputation Conditions
+ Bag Offers
+ Player State
```
Ruf bleibt ein separates Charakter-Progressionssystem.
Persönliche NPC-Beziehung kann später zusätzlich ergänzt werden.
Eine zentrale Condition Engine verbindet:
- NPCs
- Dialoge
- Quests
- Shops
- Ruf
- Materialtausch
- Taschen
- Weltfortschritt
Der zentrale Grundsatz lautet:
> **NPCs sind datengetriebene Charaktere mit kombinierbaren Interaktionen keine voneinander getrennten Spezialklassen.**