Add story-graph narrative system (campaigns, editors, runtime)

Introduce the narrative layer as a directed story-flow graph: an authored
campaign a player walks node by node, replacing the interim slot/chapter model.

Schema (migrations 015-021):
- mysteries, global NPC templates + named poses, per-user playthroughs
- story_nodes, terminals, utterances (the flow graph and dialogue trees)
- clean cutover: retire slot cutscenes/chapters/seen_dialogue

Runtime:
- New Game creates a playthrough bound to the JWT identity (dev test-user fallback)
- advance() walks the graph cutscene -> dialogue -> level -> ..., auto-skipping gates
- branching dialogue: player choices route out through node terminals

Admin authoring:
- NPC editor: upload named poses to the gupi MinIO bucket
- mystery graph editor: vertical node canvas, wiring, entrypoint, delete-by-click
- dialogue crafter: utterance tree, Tab to add child, 1/2 speaker, undo

Content authored via the manifest importer / admin panel and seeded for Glass
Harbour. MinIO added to the dev stack; dev container runs in development mode.

Also includes a folder-widget simplification (removes open/close) and a
resolveUserId auth helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 15:37:46 +02:00
co-authored by Claude Opus 4.8
parent 0237da74cf
commit ddb3a386f0
30 changed files with 3016 additions and 55 deletions
+38
View File
@@ -123,6 +123,44 @@
"metadata": { "lot": "117", "seller_reference": "MV-3/771", "estimate": "45,00052,000 NOK" }
}
],
"narrative": {
"cast": [
{ "key": "professor", "name": "Prof. Almira Vetch", "role": "Glitch University · Investigative Method", "defaultPose": "neutral" }
],
"graph": {
"entry": "intro",
"nodes": [
{
"key": "intro", "type": "cutscene", "label": "The Glass Harbour Diversion",
"componentKey": "glass-harbour-diversion", "x": 200, "y": 60,
"terminals": [{ "key": "continue", "to": "briefing" }]
},
{
"key": "briefing", "type": "dialogue", "label": "Briefing", "x": 200, "y": 220,
"terminals": [{ "key": "continue", "label": "Begin", "to": "investigate" }],
"utterances": [
{ "npc": "professor", "pose": "neutral", "text": "Principal Investigator. Good — you're early. Sit. The Society has handed us a mess: a restored Fresnel lens, bought and paid for, that never reached the lighthouse it was meant for." },
{ "npc": "professor", "pose": "concerned", "text": "Greyhaven file 87-10. Between dispatch and installation the shipment simply changed course. Someone arranged that, and someone stood to profit. Both facts are in the documents — nowhere else." },
{ "npc": "professor", "pose": "wry", "text": "I won't tell you who did it. That is the whole exercise. Classify every name, tie each party to the evidence, and reconstruct the order of events until the account defends itself." },
{ "npc": "professor", "pose": "neutral", "text": "The terminal will not congratulate you. A solved board is one where your conclusion is the only one the paper trail still allows. Go." }
]
},
{
"key": "investigate", "type": "level", "label": "Investigate the board",
"templateSlug": "glass-harbor", "x": 200, "y": 380,
"terminals": [{ "key": "report_back", "label": "Report back", "to": "debrief" }]
},
{
"key": "debrief", "type": "dialogue", "label": "Debrief", "x": 200, "y": 540,
"terminals": [{ "key": "continue", "label": "End", "to": null }],
"utterances": [
{ "npc": "professor", "pose": "wry", "text": "There it is. The lens never sailed for the lighthouse — it sailed for a saleroom, and your thread shows exactly whose hand turned it." },
{ "npc": "professor", "pose": "neutral", "text": "A defensible account, Principal Investigator. Greyhaven file 87-10 is closed. Get some sleep — there will be another." }
]
}
]
}
},
"folders": [
{
"key": "procurement-file",