docs: record the Slice 0.9 quest system and its deviations

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Bastian Wagner
2026-08-22 23:36:42 +02:00
parent 3a84fd5371
commit 70ee207f1f
2 changed files with 172 additions and 12 deletions

View File

@@ -1354,13 +1354,22 @@ Erster NPC: **Borin, Quartermaster** an `south-gate` (Graufurt) mit
DIALOGUE + MERCHANT + RESOURCE_EXCHANGE gleichzeitig — der Kompositionsfall
aus §2/§26 in echt.
## Abweichung 1 — kein `NpcQuestAssignment`
## Abweichung 1 — kein `NpcQuestAssignment` — **aufgelöst in Slice 0.9**
§34 listet es im V1-Scope, aber es gibt noch kein Questsystem (Slice 0.9).
§34 listet es im V1-Scope, aber es gab in Slice 0.8 noch kein Questsystem.
Eine Tabelle mit Fremdschlüssel auf eine nicht existierende `quests`-Tabelle
ist nicht baubar, und ein `questKey`-String ohne Validierung wäre spekulative
Architektur (AGENTS §1.7). Nachzuholen mit Slice 0.9, zusammen mit den
Dialog-Actions `START_QUEST` / `COMPLETE_QUEST`.
Architektur (AGENTS §1.7).
Slice 0.9 hat das nachgeholt: `NpcQuestAssignment` liegt unter
`apps/api/src/quests/entities/npc-quest-assignment.entity.ts` und trägt die
Rollen `OFFER` / `TURN_IN` / `PROGRESS` aus §14. Der Unique-Index umfasst die
Rolle mit, damit derselbe NPC eine Quest sowohl vergeben als auch annehmen
kann — genau der Fall aus §14, den die erste Questkette benutzt: der South
Gate Warden vergibt und nimmt an, Borin steht als `PROGRESS` dazwischen.
Die Dialog-Actions `START_QUEST` / `COMPLETE_QUEST` bleiben offen, siehe
Abweichung 4.
## Abweichung 2 — kein `NpcDialogueProfile`
@@ -1372,14 +1381,52 @@ neu zu schreiben.
## Abweichung 3 — Conditions, die (noch) nichts beantworten kann
`GameConditionType` enthält die vollständige V1-Liste aus §19, aber nur
`REGION_REPUTATION`, `WORLD_RENOWN`, `FLAG_SET` und `HAS_ITEM` sind
auswertbar. `QUEST_ACTIVE`, `QUEST_COMPLETED`, `BOSS_DEFEATED` und
`LOCATION_DISCOVERED` haben noch kein System dahinter.
`GameConditionType` enthält die vollständige V1-Liste aus §19, aber nicht jeder
Typ hat ein System dahinter.
Sie werten **fail-closed** aus, also immer "nicht erfüllt". Für ein Gate ist
die sichere Richtung eines Fehlers zu, nicht offen — ein Quest-Gate darf
niemals aufgehen, nur weil es keine Quests gibt.
Seit Slice 0.9 auswertbar: `REGION_REPUTATION`, `WORLD_RENOWN`, `FLAG_SET`,
`HAS_ITEM`, `QUEST_ACTIVE`, `QUEST_COMPLETED`. Offen bleiben `BOSS_DEFEATED`
(Slice 0.11) und `LOCATION_DISCOVERED` (wird pro Charakter nicht getrackt).
Die beiden Quest-Typen werten boolesch aus wie `FLAG_SET`, nicht numerisch:
eine Quest läuft oder nicht, `actual` bleibt `null`. `value: false` ist die Art,
wie Content die Verneinung ausdrückt — die Angebotszeile des Wardens braucht
"noch nicht begonnen und noch nicht abgeschlossen", ohne dass die
Condition-Sprache ein NOT bekommt.
Die verbleibenden Typen werten **fail-closed** aus, also immer "nicht erfüllt".
Für ein Gate ist die sichere Richtung eines Fehlers zu, nicht offen.
## Abweichung 4 — Questschritte laufen nicht über Dialog-Actions
`DialogueActionType` enthält `START_QUEST` und `COMPLETE_QUEST`, aber beide
bleiben auch mit dem Questsystem aus Slice 0.9 wirkungslos.
Sie auszuführen bräuchte zwei Dinge, die es nicht gibt: einen Endpoint, über
den der Client eine Dialog-Response *auswählt*, und einen Action-Executor
dahinter. Playable Slice 0.9 §10 verlangt ausdrücklich einen
schrittbasierten Questautomaten und verbietet eine verzweigende
Narrative-Engine — und ein Response-Endpoint plus Executor wäre genau deren
Anfang, für eine Kette mit fünf Schritten.
Stattdessen:
```text
POST /api/npcs/:npcKey/quests/:questKey/accept
POST /api/npcs/:npcKey/quests/:questKey/advance
```
Keine der beiden Routen nimmt einen Body. Welcher Schritt gerade dran ist, was
er vergibt und was er verbraucht, entscheidet der Server (AGENTS §5). Der Satz,
den der NPC zu einem Schritt sagt, ist Content auf dem `QuestObjective`
(`npc_line`) und kommt mit der Antwort zurück.
Nachzuholen wäre das, sobald ein NPC wirklich verzweigende Antworten braucht:
dann ein Response-Auswahl-Endpoint, ein Executor über
`SUPPORTED_DIALOGUE_ACTIONS`, und die Questschritte können auf Dialog-Actions
umziehen, ohne dass die Questtabellen sich ändern.
---
## Offene Designfrage — World Renown im Tausch
@@ -1398,3 +1445,40 @@ anzufassen.
Falls Renown später doch als Punktwährung gedacht ist, muss das zuerst in
0.6.5 geändert werden — nicht hier.
---
# 41. Implementierungsstand (Slice 0.9)
```text
QuestDefinition / QuestObjective apps/api/src/quests/entities/
NpcQuestAssignment Rollen aus §14
CharacterQuest Player-State, ein Row pro Charakter/Quest
QuestProgressService leitet den aktiven Schritt ab (lesend)
QuestService accept / advance, Effekte, Belohnungen
QuestController / NpcQuestController apps/api/src/quests/quest.controller.ts
```
Erste Questkette: **Trouble Beyond the Gate**, vergeben und angenommen von
Halvik am Südtor, mit Borin als Zwischenschritt — der Mehr-NPC-Fall aus §14
in echt.
Zwei Module statt einem: `QuestProgressModule` enthält nur den lesenden Teil,
weil `NpcService` Questmarker braucht und `QuestService` umgekehrt `NpcService`
für die Erreichbarkeitsprüfung. Der Schnitt hält den Modulgraphen zyklenfrei,
ohne `forwardRef`.
Es gibt bewusst **keine** Tabelle für Objective-Fortschritt. Slice 0.9 §11
verlangt, dass Sammelschritte ihren Stand aus dem aktuellen Besitz ableiten;
genau das macht `QuestProgressService`, und dadurch fallen die Softlock-Fälle
aus §11 von selbst weg, statt einzeln behandelt zu werden.
Was ein Schritt *tut*, steht als Content auf dem `QuestObjective`
(`sets_flag_key` / `sets_flag_npc_key`, `grants_loot_bag_key`,
`consume_on_complete`, `advance_when_blocked`). `QuestService` wendet an, was
es vorfindet, und verzweigt nirgends auf einen Questkey (AGENTS §9).
Markerregeln für die Ortsansicht (§24, Slice 0.9 §12): höchstens ein
Questmarker pro NPC, Reihenfolge `QUEST_TURN_IN` > `QUEST_AVAILABLE` >
`QUEST_IN_PROGRESS`. Halvik hält für dieselbe Quest zwei Rollen; ohne
Vorrangregel trüge er zwei Symbole und keines würde etwas aussagen.