ctx.story 为扩展提供只读剧本快照。章节正文包含片段和 Studio 保存的原始 Block 数据,适合制作章节选择、台词检索、流程统计或辅助功能。

API

方法返回值说明
listChapters()StoryChapterMeta[]同步读取章节目录,不加载正文
getChapter(id)Promise<StoryChapter | null>按章节 id 读取完整章节;不存在时返回 null
getAllChapters()Promise<StoryChapter[]>按目录顺序读取当前宿主中的完整剧本
StoryChapter 的结构如下:
interface StoryChapter {
  id: string;
  name: string;
  disabled?: boolean;
  fragments: Array<{
    id: string;
    name: string;
    blocks: StoryBlock[];
  }>;
}
StoryBlock 保留原始 Block 的 idtypecontentpropschildren 和扩展自定义字段。返回结果是深拷贝;修改对象只会改变扩展自己的副本,不会修改 Studio 工程或正在运行的剧本。

案例:读取完整剧本中的所有对话块

输入: 项目包含多个章节,需要按章节统计 dialogue Block 数量。
const chapters = await ctx.story.getAllChapters();

const result = chapters.map((chapter) => ({
  id: chapter.id,
  name: chapter.name,
  dialogueCount: chapter.fragments.reduce(
    (count, fragment) =>
      count + fragment.blocks.filter((block) => block.type === "dialogue").length,
    0,
  ),
}));

console.log(result);
输出:
[
  { "id": "chapter-1", "name": "序章", "dialogueCount": 18 },
  { "id": "chapter-2", "name": "第一章", "dialogueCount": 42 }
]

加载与宿主差异

  • listChapters() 只返回轻量目录,适合先展示列表,再按需调用 getChapter()
  • Player 可能需要读取章节文件,因此 getChapter()getAllChapters() 必须使用 awaitgetAllChapters() 的开销会随剧本大小增加,不建议在每次 React render 中调用。
  • Studio 中可以读取工程里的禁用章节,此时目录和章节对象带有 disabled: true
  • 构建后的 Player 只能读取实际进入成品的章节;禁用章节不会被打包,因此不在目录和完整剧本中。
  • 任一章节加载失败时,getAllChapters() 会 reject。需要自行降级时,可以先用 listChapters(),再逐章调用 getChapter() 并捕获错误。
ctx.history.entries() 只包含玩家已经播放过的历史对话,不是完整剧本。需要读取未播放内容时请使用 ctx.story