diff --git a/src/world/loader.test.ts b/src/world/loader.test.ts index 11b882e..2254c85 100644 --- a/src/world/loader.test.ts +++ b/src/world/loader.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { parseRoom, parseItem, parseEnding } from './loader' +import { parseRoom, parseItem, parseEnding, parseEncounterNarration } from './loader' const FOYER_MD = `--- id: foyer @@ -246,3 +246,43 @@ whenFlags: {} expect(result.ending.narration).toBe('') }) }) + +const RAT_MD = `--- +id: rat +startsIn: "[[cellar-stair]]" +initialPhase: lurking +--- + +## lurking +A heavy rat watches you from the third step. Its eyes catch the light. + +## attack-resolved +You stamp. The rat squeals and is gone into the dark. + +## wait-stays +The rat does not move. Neither do you. +` + +describe('parseEncounterNarration', () => { + it('parses frontmatter and narration sections', () => { + const doc = parseEncounterNarration(RAT_MD, 'encounters/rat.md') + expect(doc.id).toBe('rat') + expect(doc.startsIn).toBe('cellar-stair') + expect(doc.initialPhase).toBe('lurking') + expect(doc.narrations).toEqual({ + lurking: 'A heavy rat watches you from the third step. Its eyes catch the light.', + 'attack-resolved': 'You stamp. The rat squeals and is gone into the dark.', + 'wait-stays': 'The rat does not move. Neither do you.', + }) + }) + + it('throws when no sections are present', () => { + const md = `--- +id: x +startsIn: room +initialPhase: p +--- +` + expect(() => parseEncounterNarration(md, 'encounters/x.md')).toThrow(/no narration sections/i) + }) +}) diff --git a/src/world/loader.ts b/src/world/loader.ts index 6e80081..d90589e 100644 --- a/src/world/loader.ts +++ b/src/world/loader.ts @@ -1,7 +1,7 @@ import matter from 'gray-matter' import type { Room, RoomDescriptions, Item } from './types' import type { Direction } from '../engine/types' -import { roomFrontmatterSchema, itemFrontmatterSchema, endingFrontmatterSchema } from './schema' +import { roomFrontmatterSchema, itemFrontmatterSchema, endingFrontmatterSchema, encounterFrontmatterSchema } from './schema' const WIKILINK = /^\[\[([^\]|]+)(?:\|[^\]]*)?\]\]$/ @@ -131,3 +131,26 @@ export function parseEnding(raw: string, _sourcePath: string): ParsedEnding { ending: { whenFlags: fm.whenFlags, narration: parsed.content.trim() }, } } + +export interface ParsedEncounterNarration { + id: string + startsIn: string + initialPhase: string + narrations: Record +} + +export function parseEncounterNarration(raw: string, sourcePath: string): ParsedEncounterNarration { + const parsed = matter(raw) + const frontmatter = stripWikilink(parsed.data) as Record + const fm = encounterFrontmatterSchema.parse(frontmatter) + const narrations = splitSections(parsed.content) + if (Object.keys(narrations).length === 0) { + throw new Error(`${sourcePath}: no narration sections found`) + } + return { + id: fm.id, + startsIn: fm.startsIn, + initialPhase: fm.initialPhase, + narrations, + } +}