Add merit node type (Scene 8 ceremony + achievement award)

A merit node awards a configured achievement (awards_flag) to the player
the moment the playthrough arrives on it, with node provenance, then
presents a ceremony. Migration 028 extends node_type + the component_key
CHECK and adds awards_flag; the runtime grants the flag in advance/goto/
new-game write paths; MeritHost renders the ceremony (a component_key can
supply a bespoke one, e.g. a 3D model). Verified: teleport to a merit node
auto-grants its achievement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 16:11:53 +02:00
co-authored by Claude Opus 4.8
parent 5c514562a2
commit 778c6d972f
7 changed files with 70 additions and 13 deletions
+2 -1
View File
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from 'react'
import { CutsceneHost, DialoguePlayer, type PlaythroughState } from './narrative'
import { CutsceneHost, DialoguePlayer, MeritHost, type PlaythroughState } from './narrative'
type MysterySummary = { slug: string; title: string }
@@ -93,6 +93,7 @@ export function Play() {
const node = state?.node
if (!node) return <div className="boot"><div className="seal">GU</div><p>GLITCH UNIVERSITY NETWORK TERMINAL</p><small>{status || 'OPENING CASE FILE…'}</small></div>
if (node.kind === 'cutscene') return <CutsceneHost componentKey={node.componentKey} label={node.label} onComplete={() => { void advance() }} />
if (node.kind === 'merit') return <MeritHost componentKey={node.componentKey} label={node.label} awardsFlag={node.awardsFlag} onComplete={() => { void advance() }} />
if (node.kind === 'dialogue' && node.utterances) return <DialoguePlayer node={{ utterances: node.utterances, rootId: node.rootId ?? null }} onExit={terminalKey => { void advance(terminalKey) }} />
return <div className="boot"><div className="seal">GU</div><small>{node.label}</small></div>
}