跳到主要内容

阶段式建卡

用字段输入、引用、随机表、授予和采购任务生成角色,设置秘密结果与属性交换。

本页目录

自建角色使用角色类型中的 creationFlow.workbench。建卡按明确配置的任务生成角色,不依赖模型,也不从规则正文或 Markdown 表格推断建卡步骤。角色类型必须声明 designation = "character",对应开局方案须允许自建。

先确定生成规则

先列出确认角色时必须存在的原始值,再决定哪些由玩家输入、选择、随机或固定授予。任务只生成普通输入和初始事实;派生上限、阈值与汇总留给计算字段。mutable 约束运行中更新,不阻止建卡填写只读初值。

身份和名字用输入,固定选项用 choice,已有职业或背景资料用 reference;独立骰式用 roll,成组随机条目用 table;需要展开所选资料中的任务时用 document,固定初值或复制已生成值用 grant,预算与数量选择用 purchase。规则正文里的骰表和物品清单不会自动成为任务。

任务按依赖排序:先选择来源,再读取来源;先产生基础值,再复制初值。平台按稳定任务 ID 保存答案和随机历史,作者修改标题时应保留 ID,不通过改 ID 强制重掷。建卡已经授予的初始资源,不应在开场初始化指令中再次授予。

逐条检查分支:是否补齐必填字段、是否存在写入冲突,列表授予应使用 append 还是整体 set,以及数字累加是否重复。秘密任务的私密配置属于建卡协议,不能据此推断整个冒险具有按玩家分别揭露的资料权限。

一份可用的工作台

在最小模组的副本中试做:将 .types/character.fields.toml 换成下面这份完整的 .types/character.fields.json,同名 TOML 文件不要继续留在模组内。若已经配置角色布局,先移除这份练习副本中的 character.layout.*,避免它引用本例没有定义的职业等字段。其他字段规则见文档类型与字段。

{
"designation": "character",
"fields": {
"age": { "type": "number", "label": "年龄", "required": true },
"background": { "type": "text", "label": "背景" },
"prepared": { "type": "boolean", "label": "已做好准备" },
"hp": {
"type": "number",
"label": "生命值",
"required": true,
"mutable": true
}
},
"creationFlow": {
"workbench": {
"stages": [
{
"id": "identity",
"title": "身份",
"tasks": [
{
"id": "name",
"title": "你的名字",
"kind": "input",
"bind": "name"
},
{ "id": "age", "title": "年龄", "kind": "input", "bind": "age" },
{
"id": "background",
"title": "背景",
"kind": "input",
"bind": "background",
"required": false
},
{
"id": "prepared",
"title": "已做好准备",
"kind": "input",
"bind": "prepared"
}
]
},
{
"id": "resources",
"title": "初始资源",
"tasks": [
{
"id": "hp",
"title": "初始生命",
"kind": "roll",
"bind": "hp",
"dice": { "count": 1, "sides": 6, "modifier": 2 }
}
]
}
]
}
}
}

保留阿青这名预设角色时,需要在 character/阿青.toml 的顶层补上 age = 20,以满足新定义的必填字段;其他可选字段可以省略。然后把现有开局的 allowCreate 改为 true,保留 "characterTypeIds": ["character"]。

开发者手动验收:新建冒险,在选角处进入自建,应出现“身份”和“初始资源”两个阶段。填写姓名、年龄和准备状态,生成生命值后核对角色卡;生命值应为 3–8,确认后新角色可以进入冒险。

至少配置一个阶段。阶段有唯一且稳定的 id、非空中文 title 和任务列表 tasks;同一任务列表里的任务 ID 不能重复。调整标题无需改变 ID,随机记录和答案仍按任务 ID 保存。

八类任务

每个任务需要 id、title 和 kind。字段路径用 attributes.strength 这样的点路径。

kind 配置与行为
input 必须有 bind;控件类型、枚举选项、对象和列表结构来自目标字段定义
choice 必须有 bind,目标为文本或枚举;用非空无重复 options,或用 source 读取已选文档的 creation.options
reference 用 optionTypeId 选择已有文档;bind 必须是允许该类型的引用字段
roll 必须绑定数字字段,配置 dice.count、dice.sides 和可选 dice.modifier
table table 引用随机表文档;均匀掷一枚与条目数相同面数的骰子,选中 creation.entries 条目,再执行其任务;可选 bind 保存条目的 text
document source 指向角色上已选引用字段,展开该文档的 creation.tasks
grant 必须有 bind;用 value 指定值,或用 source 复制当前角色字段值
purchase 用 table 引用采购表、budget 给出预算,根据数量重复执行购买条目的任务

mode 默认 set,直接赋值;add 只用于数字累加,缺省初值按 0 处理;append 只用于向列表追加一个元素,任务值须符合列表元素类型。需要写入结果的任务应明确配置 bind。姓名、简介可用 name、description;其余字段必须声明,计算字段及计算父字段不能作为写入目标。

required 默认 true,非必填任务可以跳过。输入按原生值保存,数字不会变成文本,布尔 false 和空列表都是有效输入;对象只接受已声明的可输入字段。mutable 管游戏中的修改权限,不阻止建卡填写初始值。

掷骰数量为 1–20,面数为 2–1000,修正值为整数,默认 0。引用选择类型须有有效文档;字符串选项写入枚举时必须属于目标枚举。

文档里的建卡资料

普通内容文档可声明顶层 creation 对象,只包含以下资料:

属性 用途
tasks document 任务展开的显式任务列表
options choice 任务读取的非空字符串选项
entries 随机表或采购表;每项含 text、可选非负 price 和 tasks

creation 及其结构仍须在该文档类型的 fields 中声明。任务列表定义了应用生成角色的步骤,应设置 visibility = "gm":分别设置 creation.tasks 和 creation.entries 条目中的 tasks。也可以将父字段整体设为主持可见。公开任务列表会使建卡配置被拒绝;options、条目 text 和 price 可以公开。工作台仍会让所属玩家看到对应任务与结果。

例如,一份只授予银币的背景资料类型 .types/background.fields.json:

{
"fields": {
"creation": {
"type": "object",
"fields": {
"tasks": {
"type": "list",
"visibility": "gm",
"item": {
"type": "object",
"fields": {
"id": { "type": "text", "required": true },
"title": { "type": "text", "required": true },
"kind": {
"type": "enum",
"options": ["grant"],
"required": true
},
"bind": { "type": "text", "required": true },
"mode": {
"type": "enum",
"options": ["set", "add"],
"required": true
},
"value": { "type": "number", "required": true }
}
}
}
}
}
}
}

对应 background/旅人.json:

{
"name": "旅人",
"creation": {
"tasks": [
{
"id": "coins",
"title": "初始银币",
"kind": "grant",
"bind": "silver",
"mode": "add",
"value": 10
}
]
}
}

接下来扩展本页的角色定义。在 .types/character.fields.json 的 fields 对象中合并以下两个字段,保留原有字段;origin 专门记录背景资料引用,原来的 background 文本仍用于自由描述:

{
"silver": { "type": "number", "label": "银币" },
"origin": {
"type": "reference",
"label": "背景资料",
"targets": ["background"]
}
}

将下面两个任务追加到“身份”阶段的 tasks 数组末尾,放在姓名等原任务之后。这些任务先让玩家选择资料,再读取资料并授予资源;这是一组追加任务,不要替换整个阶段或角色文件:

[
{
"id": "origin",
"title": "选择背景",
"kind": "reference",
"bind": "origin",
"optionTypeId": "background"
},
{
"id": "origin-tasks",
"title": "背景给予的资源",
"kind": "document",
"source": "origin"
}
]

开发者手动验收:保存并新建冒险,自建角色选中“旅人”后,核对页应显示 10 枚银币;返回浏览或重复生成不应再次加到 20。

任务列表结构按你的模组声明:上例只接受数值授予任务。若添加骰子、物品对象或输入任务,也要在来源类型中补全对应字段。物品本身的资料通过引用关联,数量和使用次数放在角色持有状态中,见展示布局。

随机表每次选择一个条目,先将 text 写入可选的 bind(文本或枚举),再执行条目任务。嵌套表会继续生成,玩家需要填写或选择的部分留待玩家完成。循环引用会被拒绝。

采购与余款

下面是单个采购任务的片段,可加入阶段的 tasks 数组。使用前须创建 random_table/市场用品 文档,并按上一节为其 creation.entries 声明字段;它不是可独立运行的完整示例。

{
"id": "supplies",
"title": "采购用品",
"kind": "purchase",
"table": "random_table/市场用品",
"budget": 20,
"currency": "银币"
}

采购表须有非空 creation.entries,每项提供非负 price 和授予物品的 tasks。玩家每项选择 0–100 件,平台检查总价不超过预算;默认可以保留余款,要求正好花完时设置 spendAll: true。currency 只显示单位,不参与换算。采购不会自动将余款写入某个货币字段,角色货币和授予规则须由模组显式定义。

秘密结果、交换与确认

读取非公开来源或写入非公开目标的任务必须设置 private: true。秘密结果不能写入公开字段;公开对象里的私密子字段应通过独立任务绑定子路径。秘密任务的属性会传给嵌套任务,所属玩家可以在自己的工作台查看,其他玩家不会收到秘密值或进度。

workbench.swap 可以列出允许互换的数字字段,例如 ["attributes.strength", "attributes.dexterity"]。路径须指向已声明的非计算数字字段,不能重复;确认前最多选择一对互换,也可以恢复原顺序。

点击生成时,只会补齐当前阶段缺失的随机结果;重复生成、返回浏览和重新打开不会重掷已有骰值。更改文档选择会重新结算当前分支,原分支随机历史仍保留。确认时重新按任务计算角色原始值,计算字段由公式生成,派生结果不写回角色。

建卡预览和核对共用详情布局。修改模组后先做CLI 校验,再由开发者手动新建冒险,检查每个选择分支、生成、返回、采购与确认。条件能力、战斗结算和运行中的状态不要在建卡时提前结算。