51 lines
5.5 KiB
TypeScript
51 lines
5.5 KiB
TypeScript
import type { Character, WorldStarter } from '@dng/shared'
|
||
|
||
const defaultWorld: WorldStarter = {
|
||
title: 'Signal at Black Meridian',
|
||
genre: 'Solar gothic science fiction',
|
||
tone: 'Tense, mysterious, hopeful under pressure',
|
||
premise: 'At the edge of charted space, a dead relay begins broadcasting tomorrow’s distress calls. The crew of the courier Orison is the only ship close enough to answer.',
|
||
contentBoundaries: ['13+ adventure', 'No explicit sexual content', 'No graphic gore'],
|
||
startingLocation: { id: 'loc_meridian', kind: 'location', name: 'Black Meridian Relay', summary: 'A lightless communications cathedral orbiting a cold blue star.', tags: ['station', 'derelict'], secrets: ['The distress calls are being sent by the crew themselves, three days in the future.'] },
|
||
npcs: [
|
||
{ id: 'npc_lyra', kind: 'npc', name: 'Lyra Venn', summary: 'A calm signal analyst who hears patterns as music.', tags: ['analyst'], secrets: ['She recognizes the voice in the transmission.'] },
|
||
{ id: 'npc_cassian', kind: 'npc', name: 'Cassian Holt', summary: 'A company marshal with a sealed retrieval order.', tags: ['marshal'], secrets: ['His orders prioritize the relay core over the crew.'] },
|
||
{ id: 'npc_moth', kind: 'npc', name: 'Moth', summary: 'A maintenance intelligence speaking through antique service drones.', tags: ['ai'], secrets: ['Moth has been awake for eighty-seven years.'] },
|
||
],
|
||
factions: [
|
||
{ id: 'fac_heliograph', kind: 'faction', name: 'Heliograph Compact', summary: 'A mercantile coalition that owns the relay network.', tags: ['corporate'], secrets: [] },
|
||
{ id: 'fac_quiet', kind: 'faction', name: 'The Quiet Choir', summary: 'Pilgrims who believe signals can remember the dead.', tags: ['mystic'], secrets: [] },
|
||
],
|
||
companions: [
|
||
{ name: 'Rook-7', concept: 'A security intelligence in a weathered rescue frame.', abilities: { str: 16, dex: 11, con: 15, int: 10, wis: 12, cha: 8 }, hp: 15, maxHp: 15, defense: 15, proficiency: 2, inventory: ['Arc baton', 'Emergency beacon'], persona: { voice: 'Direct and protective.', motivation: 'Keep the crew alive long enough to learn the truth.', flaw: 'Treats uncertainty as a threat.', bond: 'The Orison crew gave Rook a second purpose.' } },
|
||
{ name: 'Sable Thread', concept: 'A probabilistic navigator who reads possible futures as tangled routes.', abilities: { str: 8, dex: 14, con: 10, int: 16, wis: 13, cha: 11 }, hp: 10, maxHp: 10, defense: 13, proficiency: 2, inventory: ['Route prism', 'Vacuum line'], persona: { voice: 'Careful, elliptical, and precise.', motivation: 'Find the future in which everyone returns.', flaw: 'Can hesitate when too many paths look viable.', bond: 'Trusts the party to choose what prediction cannot.' } },
|
||
{ name: 'Morrow Coil', concept: 'A former relay technician rebuilt around experimental signal hardware.', abilities: { str: 11, dex: 12, con: 13, int: 14, wis: 10, cha: 13 }, hp: 12, maxHp: 12, defense: 12, proficiency: 2, inventory: ['Signal probe', 'Insulated field coat'], persona: { voice: 'Warm humor under technical jargon.', motivation: 'Prove the relay can be understood instead of destroyed.', flaw: 'Cannot leave a broken machine alone.', bond: 'Believes this party is the last honest crew in the sector.' } },
|
||
],
|
||
hook: 'Dock with the silent relay, locate the source of the impossible distress call, and decide whether its warning should be believed.',
|
||
hiddenThreat: 'A causality fracture is teaching the relay to choose which future becomes real.',
|
||
openingScene: 'The Orison drifts beneath the relay’s vast black vanes. Every console shows the same countdown: 00:17:42. Then your own voice breaks through the static: “Do not open the central archive.”',
|
||
}
|
||
|
||
const defaultCharacters: Character[] = [
|
||
{ id: 'char_mara', name: 'Mara Vale', concept: 'Human salvage pilot with an instinct for impossible routes', controller: 'human', userId: 'demo', abilities: { str: 10, dex: 16, con: 13, int: 12, wis: 14, cha: 11 }, hp: 11, maxHp: 11, defense: 14, proficiency: 2, inventory: ['Pulse cutter', 'Vacuum cloak'], statuses: [] },
|
||
{ id: 'char_rook', name: 'Rook-7', concept: 'AI companion inhabiting a weathered security frame', controller: 'ai', userId: null, abilities: { str: 16, dex: 11, con: 15, int: 10, wis: 12, cha: 8 }, hp: 15, maxHp: 15, defense: 15, proficiency: 2, inventory: ['Arc baton', 'Emergency beacon'], statuses: [] },
|
||
]
|
||
|
||
export function useDemo() {
|
||
const signedIn = useState('signed-in', () => false)
|
||
const worlds = useState<WorldStarter[]>('worlds', () => [defaultWorld])
|
||
const activeWorld = useState<WorldStarter>('active-world', () => defaultWorld)
|
||
const characters = useState<Character[]>('characters', () => defaultCharacters)
|
||
const roundNumber = useState('round-number', () => 3)
|
||
const scene = useState('scene', () => defaultWorld.openingScene)
|
||
const history = useState<Array<{ type: 'narration' | 'action' | 'roll'; author: string; body: string; meta?: string }>>('history', () => [
|
||
{ type: 'narration', author: 'GROUNDKEEPER', body: defaultWorld.openingScene, meta: 'ROUND 03 · NOW' },
|
||
{ type: 'action', author: 'MARA VALE', body: 'I ask the Orison to isolate the transmission and compare the voiceprint to mine.', meta: 'READY' },
|
||
{ type: 'roll', author: 'SYSTEM CHECK', body: 'Intelligence check · 1d20 + 3 = 17', meta: 'SUCCESS · DC 14' },
|
||
])
|
||
const currentAction = useState('current-action', () => '')
|
||
const ready = useState('ready', () => false)
|
||
|
||
return { signedIn, worlds, activeWorld, characters, roundNumber, scene, history, currentAction, ready }
|
||
}
|