跳到主要内容

Lattice 语法参考 · 第 1 章,共 4 章

源码文件与声明

本章定义线性故事CYOA 都能使用的公共语法,包括项目如何展开,以及建立故事身份和叙事语境的全部声明。

文档由什么组成

一个 .la 文件包含零个或多个顶层条目。完整的顶层词汇如下:

text
use "./relative-file.la";

story main (...);
entity character ch_mara (...);
predicate knows (...);
state group location (...);
state st_knows_signal (...): event { ... };
pool investigation (...);
module md_decode (...) { ... }
scene garden (...) { ... }

predicatestatepoolmodule 只属于 CYOA。线性项目只能使用导入、唯一 Story、实体、顶层 Scene,以及嵌套在 Scene 里的 Block 和内容。

文件与导入

项目配置指定一个入口文件,通常是 main.la。TypeApe 从这里开始,递归展开每一条 use

text
# main.la
use "./story/entities.la";
use "./story/opening.la";

story main (
  title: "信号花园",
  language: "zh-CN",
  format: novel,
  entry: opening
);

导入路径必须:

  • 是带引号的路径,并相对于写下 use 的文件解析;
  • 指向当前项目内的文件;
  • 不能通过 .. 或指向外部的符号链接逃离项目;
  • 不能形成导入循环;
  • 不能通过不同相对路径重复载入同一个文件。

被导入文件的声明会排在导入者自身声明之前。这不会创建 Block 流转,但会让声明顺序保持确定,并决定符合条件的 CYOA Module 在界面中的展示顺序。

全部展开后,整个项目必须恰好包含一个 story 声明。被导入文件应该提供实体、Scene、State 或 Module,而不是再次声明 Story。

注释、逗号与分号

只有 # 能开始行注释,// 不是 Lattice 注释。

text
# 这是一条 Lattice 注释。
entity item it_key (name: "信号钥匙"); # 行尾注释
结构结束方式
usestoryentitypredicatestatestate grouppool;
带主体的 scenemodule} 结束,不加 ;
textdialogueforeshadowingreveals;
nextendentercomplete、每个 option;
外层 choice { ... }} 结束,不加 ;

元数据属性和事件字段使用逗号分隔。文档语法没有定义尾随逗号,因此最后一个属性之后不要加逗号。

标识符、字符串与正文

Lattice 有意区分身份、精确文本和叙事正文。

形式示例用途
标识符openingch_mararesolution声明 ID、引用和枚举值
字符串"zh-CN""黎明之前"明确要求带引号的精确文本
正文{ 一束光苏醒。 }叙事内容或文本元数据
数字31.5被允许的事件或扩展字面量
布尔值truefalse被允许的事件或扩展字面量
列表[opening, resolution]有序元数据值,也可以是空列表 []

身份不是标题

在下面的声明中,ch_mara 是稳定身份,"玛拉" 是展示文本:

text
entity character ch_mara (name: "玛拉");

读者看到的名字变化时修改 name。只有实体身份本身发生变化时才修改 ch_mara,并同步更新所有引用,再做全局校验。不要给 ID 引用加引号:

text
# 正确
pov: ch_mara

# 错误:这是字符串,不是角色引用
pov: "ch_mara"

正文花括号属于语法

正文以 { 开始、以 } 结束,内部换行和普通标点都会保留。

text
logline: {
  一位耐心的信号猎人发现,明天正在
  从她的花园地下传来回应。
}

叙事段落和文本元数据使用正文。像 language 这样明确要求字符串的字段则使用带引号字符串。

元数据

元数据写在声明 ID 后面的圆括号里:

text
scene opening_scene (
  title: "开篇",
  beat: opening,
  pov: ch_mara
) {
  # Blocks
}

同一个声明里,每个键最多出现一次。字段会根据所有者检查类型:某个字段即使对 Scene 有效,也可能不能用于 Story 或 Block。

未知键会产生错误,除非它以 x_ 开始:

text
scene opening_scene (
  title: "开篇",
  x_editor_color: "blue"
) { ... }

x_ 元数据会被保留,但没有内置语义。除非某个具名产品能力明确支持,否则它不能创建路径、改变预览、修改 State、绕过诊断或影响导出。

Story 声明

导入展开后,项目中只能有一个 Story。

text
story main (
  title: "信号花园",
  language: "zh-CN",
  format: novel,
  entry: opening,
  logline: { 一个埋藏的信号把猎人召回家。 },
  audience: "青少年",
  content_rating: "Teen",
  genres: [mystery, science_fiction],
  themes: [memory, belonging],
  tags: [draft],
  prose_unit: non_whitespace_graphemes,
  prose_scope: authored_body,
  prose_minimum: 4500,
  prose_target: 5000,
  prose_maximum: 5500,
  beat_order: [opening, midpoint, resolution]
);
字段必填接受的值含义
title字符串或正文面向读者的故事标题
language带引号的 BCP 47 字符串故事正文语言
formatnovelscreenplay展示/导出格式
entryBlock ID阅读开始的主故事 Block
loglineaudiencecontent_rating文本描述性元数据
genresthemestags文本或列表分类元数据
prose_unit有目标时计数单位标识符word_likenon_whitespace_graphemes
prose_scope有目标时authored_body面向读者的已创作正文
prose_target有目标时正整数预期正文总长度
prose_minimumprose_maximum正整数可选的交付边界
beat_order标识符列表Scene 节拍应遵循的全局顺序
initial_state仅 CYOAState ID 列表运行开始时激活的 State

入口必须存在于主故事中。属于 Module 的 Block 不能成为 Story 入口。线性故事不能声明 initial_state,即使它只是空列表。

prose_unitprose_scopeprose_target 组成一个可选目标,必须一起声明。存在边界时须满足 minimum <= target <= maximumword_like 统计类单词单元;中文“字数”请求使用 non_whitespace_graphemes。只有 TypeApe 内置字段会进入创作契约,x_* 元数据保持惰性。

实体声明

实体声明的形态是 entity <kind> <id> (...)

text
entity character ch_mara (
  name: "玛拉",
  role: protagonist,
  description: { 一位耐心的信号猎人。 },
  aliases: ["玛拉·文", "倾听者"],
  traits: [observant, stubborn],
  tags: [cast, point_of_view]
);

entity location loc_garden (
  name: "信号花园",
  description: { 湿润土壤下埋藏着天线阵列。 },
  tags: [exterior]
);

entity item it_key (
  name: "信号钥匙",
  description: { 一枚会在午夜搏动的温热碎片。 }
);

支持的 kind 是 characterlocationitem。所有实体都必须有文本 name

Kind可选字段
characterrole 标识符;description 文本;aliasestraitstags 文本或列表
locationdescription 文本;tags 文本或列表
itemdescription 文本;tags 文本或列表

引用会检查实体类型。Dialogue 说话者和 Scene pov 必须是角色;Scene location 必须是地点。CYOA Predicate 元组也会确认每个实体符合声明的字段类型。

Scene 声明

Scene 把多个 Block 组织在共同叙事语境下。

text
scene garden (
  title: "花园",
  summary: { 玛拉听见了埋藏的信号。 },
  beat: opening,
  pov: ch_mara,
  location: loc_garden,
  time: "黎明之前",
  tone: [quiet, uneasy],
  prose_target: 1200,
  tags: [chapter_one]
) {
  block opening (...) { ... }
}
字段必填接受的值
title文本
summarytime文本
beatpovlocation标识符/引用
tonetags文本或列表
prose_target正整数

顶层 Scene 属于主故事。嵌套在 Module 里的 Scene 只属于该 Module。每个 Block 都继承 Scene 的所有者,流转目标不能从主故事跳入 Module、从一个 Module 跳到另一个 Module,也不能从 Module 跳回主故事。

Scene 的 prose_target 是可以重新分配的创作预算,不是独立的有效性阈值。它要求 Story 已经声明完整的正文目标。

安全的多文件布局

text
project/
├── main.la
└── story/
    ├── entities.la
    ├── opening.la
    └── ending.la
text
# main.la
use "./story/entities.la";
use "./story/opening.la";
use "./story/ending.la";

story main (
  title: "信号花园",
  language: "zh-CN",
  format: novel,
  entry: opening
);

导入只负责组织声明,绝不会暗示叙事顺序。下一章介绍 Block、内容、批注与线性流转