规则集人物卡

用本页设计规则集的人物卡:属性、技能、字段和列表、实时状态、伤势轨道、惩罚、休息和游戏主持人(GM)文本,以及队伍人物卡和进度时钟。本页面向规则集作者。开始之前,先读编写 Game Mode 规则集。

人物卡

凡是读取数字的地方都用值引用指定。它是恰好含一个键的对象,键为 const、field、derived、abilityScore、abilityMod、abilityModFromField、skillMod、saveMod、listSum、livePool、liveTrack 或 itemStat。例如,最大值来自派生值的池:"max": { "derived": "grit_max" }。

未受训练时的检定。 角色没有受过训练的技能或豁免(其档位为第一档)照常掷骰,除非规则集另有规定:在技能或豁免上,或在其所在的分区上写 untrained(技能或豁免自身的规则优先):

"sections": [{ "id": "labour", "label": "Labour", "untrained": { "by": -1 } }],
"skills": [
  { "id": "dig", "label": "Dig", "ability": "sinew", "section": "labour", "untrained": "refuse" },
  { "id": "listen", "label": "Listen", "ability": "nerve", "untrained": "harder" }
]

属性、技能和豁免上的分区,以及任何位置的 untrained,对打包规则集要求 Capability API 1.41。

技能或豁免可以带 cap,这是一个值引用,表示其检定最多能达到的数字:角色无法超越的评级,或压制它的轨道。Gravewatch 把 Soothe 限制为守望者剩余的 Resolve:"cap": { "livePool": "resolve" }。限制后的数字就是人物卡显示、每次检定掷出的数字;with= 替换先在限制前的数字上计算,再重新限制。上限不能读取技能或豁免调整值,它所读取的派生值,以及声明在该派生值之前的任何派生值也都不能,因此人物卡仍然自上而下只计算一遍。Capability API 1.39。

hideWhen 按另一字段的值隐藏字段、派生值、列表、池或普通轨道,方式恰好是以下三种之一:equals 某个值;notEquals 某个值(字段为其他任何值时都隐藏,“只适用于这类角色”就这样表达);或 in 一组值。指定的每个值都必须是该字段能取的值,否则规则永远不会匹配;这会逐个值检查,对 notEquals 和 in 来说还包括数字字段的范围和文本字段的长度。规则读取人物卡编辑器显示的值:未设置的字段取空白人物卡的初始值,规则集已不再提供的枚举值取字段默认值。5e 文件用 equals 为不施法的角色隐藏法术位;Gravewatch 用 "hideWhen": { "field": "watch", "notEquals": "night" },让所有不值夜班的守望者人物卡上都不出现灯油。层不能移除这些规则所比较的任何值。伤势轨道不能隐藏,因为无论人物卡显示什么,掷骰和战斗都会读取它。notEquals 和 in 要求 Capability API 1.39。

普通轨道的 max 可以是值引用而非数字,这样角色拥有的评级就决定了它的长度:"max": { "field": "willpower_rating" }。其 min 和 default 仍是数字。带命名等级的伤势轨道,max 等于等级数;编号格子的轨道长度等于自身的 max(见下文“编号格子代替命名等级”)。alwaysShow: true 让轨道即使处于默认值也会印在 Game Master 的人物卡块中,适合每回合都重要的评级;没有它时,处于默认值的轨道会被省略,因为三个为零的死亡豁免什么也没说明。这三项对打包规则集要求 Capability API 1.37。

实时状态:形态、姿态、灯光

枚举字段在创建角色时选定,之后保持不变;状态只有开和关。实时状态则是从封闭集合中取的一个值,会在游玩中变化:变形者的形态、战斗姿态、提灯的亮度。

"live": {
  "states": [
    {
      "id": "stance",
      "label": "Stance",
      "values": ["guarded", "steady", "reckless"],
      "valueLabels": { "guarded": "Guarded", "steady": "Steady", "reckless": "Reckless" },
      "default": "steady"
    }
  ]
}

随之变化的数字。 "op": "enumTable" 的派生值为枚举字段或实时状态的每个值给出一个数字:

{
  "id": "stance_brawn",
  "label": "Stance on Brawn",
  "op": "enumTable",
  "from": { "liveState": "stance" },
  "table": { "guarded": -1, "reckless": 2 },
  "default": 0
}

实时状态、enumTable 以及指定 state 的休息步骤,对打包规则集要求 Capability API 1.42。

伤势轨道:用轨道而非数字表示生命

许多系统根本不计生命点,而是有一列每格比前一格更严重的格子,受伤时标一格。给 live.tracks 条目添加 kinds,再加上命名的 levels 或编号的 boxes(见下文) 之一,它就从刻度数字变为这种轨道:

系统需要哪种形式? 池记录承受多少伤害;轨道记录每份伤害的量与种类。如果伤势分为冲击、致命或恶化,并且因为恶化伤害恢复更慢、无法吸收或最终致死,种类在打击之后仍有意义,就必须在掷骰后保留它,而只有标记能携带种类。点数池做不到:扣伤后只剩更小数字,人物卡不记得哪些点属于哪类。因此,生命为池时 combat.damageKinds 会被拒绝,而非静默忽略。池仍可有 damageTypes,对手仍能抵抗或免疫,因为那关乎一击实际造成多少伤害,而非事后伤势是什么。

{
  "id": "harm",
  "label": "Harm",
  "min": 0,
  "max": 4,
  "levels": [
    { "label": "Scuffed", "penalty": 0 },
    { "label": "Winded", "penalty": -1 },
    { "label": "Bleeding", "penalty": -3 },
    { "label": "Down", "penalty": -99 }
  ],
  "kinds": [
    { "id": "knock", "label": "K", "severity": 0 },
    { "id": "tear", "label": "T", "severity": 1 }
  ]
}

模糊理解会得到错误轨道,因此以下是精确规则:

游玩时标记。 Game Master 写 [sheet: op="damage" track="harm" kind="knock" amount="1"],用负 amount 治疗。在按格填充的轨道上,它还会加上 box="3",指明这次打击落在哪一格。原先指定 pool= 的 damage 形式不变。伤势轨道拒绝普通 track 命令,因为单一数字无法说明新增标记是什么。玩家也可在人物卡手动标记和清除格子,符合这些系统的预期。

战斗也能标记。 将 combat.health 指向轨道而非池,命中一击就按 combat.damageKinds.marks 指定格数,以及该块将伤害类型映射到的种类进行标记。轨道填满的角色倒下,濒死规则读取这一状态。在按格填充的轨道上,per-point 伤害会指定落在哪一格,per-blow 则瞄准第一格;没有任何格子能承受的打击会让角色倒下,在采用这类轨道的系统中,这就是被击倒出局的含义。治疗清一个标记,即最轻的那个,不论种类。轨道没有临时点数缓冲,因此拒绝临时点数。Engine 把轨道视为剩余级数,所以战斗的其他部分、倒下、复活、日志和战后摘要都不变。

休息可以治疗伤势轨道。 用 "to" 指定轨道的恢复步骤会连同溢出一起清到指定标记数;用负数 "by" 则先清溢出,清除指定数量,因此 "by": { "const": -2 } 清除两个。同时指定 kind 的步骤只清该种类的标记:Gravewatch 的 “Catch your breath” 写作 { "track": "harm", "kind": "knock", "to": "min" },治好冲击伤而留下撕裂伤。会增加标记的步骤不做任何事,因为休息不造成伤害,正数 "by" 也是如此:它会被一声不响地跳过,所以请写上负号。

编号格子代替命名等级

如果轨道的长度取决于角色有多坚韧,就用 boxes 代替 levels。格子数等于自身的 max,它可以是人物卡计算出的值(但绝不能读取实时状态),最多 64:

{
  "id": "strain",
  "label": "Strain",
  "min": 0,
  "max": { "derived": "strain_boxes" },
  "boxes": {
    "penalty": {
      "by": "remaining",
      "table": [
        [0, -1],
        [1, 0]
      ]
    }
  },
  "kinds": [{ "id": "strain", "label": "S", "severity": 0 }],
  "fill": "indexed",
  "onFull": "refuse"
}

按格填充,以及拒绝标记的轨道

由列表增加的等级

在带命名等级的轨道上写 "extra": { "list": "scars", "countColumn": "levels", "penaltyColumn": "penalty" },人物卡上的列表就能为每个角色单独延长它。每一行按顺序以其惩罚增加其计数的等级(最多 16),放在惩罚至少同样好的最后一级之后;惩罚相同时以该级命名,否则以惩罚命名。Gravewatch 的伤疤就是这样运作的。被人物卡隐藏的列表不增加任何等级;如果某行在其等级带有标记时被删除,放不下的标记会作为溢出保留:编辑人物卡不能抹消伤害。

boxes、fill、onFull、extra 以及休息步骤的 kind,对打包规则集要求 Capability API 1.40。

掷骰惩罚

resolution.penaltyFrom 指定伤势轨道,其惩罚应用于规则集掷出的每次检定。它需要声明而非推定,因此省略时与伤势轨道出现之前完全一样。

惩罚如何作用,由结算类型决定,与人物卡自身数字相同:

指定的必须是伤势轨道。普通轨道没有可应用的惩罚,导入时会拒绝。结果说明用了哪个惩罚,让玩家理解为何少掷骰;Game Master 的人物卡块也显示档位及代价。

来自人物卡的调整值

人物卡上有些数字会伴随它所涉及的每一次掷骰:每次攀爬都背着的沉重行囊、每次潜行都有的护甲惩罚、对一切都有效的祝福。resolution.adjust 就用来表达这一点,两种结算类型都适用:

"adjust": [{ "value": { "derived": "burdened" }, "abilities": ["brawn"] }]

结果会说明人物卡加了多少(记录上的 adjust="-2",以及骰子卡上的一行),让玩家明白掷骰为何是这个结果。它对打包规则集要求 Capability API 1.38。

休息

休息是恢复步骤和清除内容的列表。每步指定一个目标(pool、poolGroup、listPools、track 或 state),并设置为某值("to": "max"、"to": "min" 或数字),或改变它("by": { "const": 1 } 或 "by": { "fractionOfMax": 0.5 })。指定伤势轨道的步骤只能治疗,因此其 by 为负数,见上文。指定实时状态的步骤只能设置它,取 "default" 或其某个值。带 fractionOfMax 的 poolGroup 步骤按各池自己的比例改变组内每个池;加上 "shared": true(Capability API 1.77) 则改为按整组的最大值计算这个比例,再按人物卡列出池的顺序分给组内各池,每个池最多补到其所缺的量。5e 的长休息恢复“角色总生命骰数的一半”,生命骰按骰子种类各存一个池,写作 { "poolGroup": "hit_dice", "by": { "fractionOfMax": 0.5, "round": "down", "min": 1 }, "shared": true }。

Game Master 文本

完整的 GM 规则参考

可选的顶层 gmReference 保存规则集给 Game Master 的完整规则参考,最多 16,000 字符。把详细的裁定规则放在这里,而把简短的检定和资源说明留在 gm.checkGuidance 和 gm.sheetGuidance 中。这些字段以及 gm.worldGuidance 仍各有 1,500 字符的上限;层的指引也保持其现有上限。

"gmReference": "When a journey crosses hostile country, judge supplies, shelter and exposure before the next leg. A failed navigation check costs time rather than silently moving the party to its destination."

参考内容出现在稳定系统提示词的 Game Master rules reference(Game Master 规则参考) 之下,每次请求一次。它从不复制到每回合的提醒中。与其他提示词安全文本一样,它必须是一行,不含控制字符、方括号或宏花括号({{ 和 }})。如果在通常的历史和回复预算调整之后,请求仍超出上下文上限,就会先以带标记的截断缩短参考内容,再截断核心指令。不设置它,就保持现有提示词不变。使用它的包必须声明清单 schemaVersion: 2 和 capabilityApi: { "major": 1, "minor": 145 } 或更新版本。

队伍与场景的进度钟(clocks)

Capability API 1.143 新增可选的顶层 clocks 对象。省略它,进度钟命令就保持禁用。这个对象是严格的:只接受下面这三个键。

"clocks": {
  "sizes": [4, 6, 8],
  "max": 8,
  "text": "Create clocks for looming threats and long projects. Tick them when the fiction advances."
}

sizes 允许一到六个互不相同的整数分段数,每个取 2 到 12;默认为 [4, 6, 8]。max 是同时保留的进度钟数量上限,取 1 到 12,默认为 8。可选的 text 是一行提示词安全文本,最多 400 字符,说明 GM 何时应创建和推进进度钟。空对象表示启用默认值。

进度钟属于队伍或场景,而不属于某个角色。GM 提醒每回合列出它们的名称和填充程度,并教授以下命令:

名称是 1–100 个字符的单行文字,不含双引号、方括号、控制字符或宏花括号。每个标签只请求一个操作。命令按在回复中出现的顺序应用,因此一个进度钟可以在同一条回复中创建并推进。Engine 会把每个标签改写为带 result="ok" now="4/6"(或 now="cleared") 的形式,或带 result="refused" reason="..." 的形式。模型自行填写的结果属性会被丢弃。未知的进度钟和格式错误的请求会被拒绝;每条回复最多应用 40 条命令。

Game Mode 在队伍状态旁显示分段圆环、名称和填充程度。标签作为凭据留在保存的回复中,并从叙述中隐藏。进度钟以 rulesetClocks 的形式保存在每份游戏状态快照中;没有该数据就表示没有进度钟。重新生成从前一份快照开始,继续则从当前快照开始;选择或移除备选回复和消息时,进度钟会随该快照的其余部分一起恢复。

队伍人物卡:团伙、飞船或共享的 Story Points

Capability API 1.144 在 sheet 旁新增可选的顶层 party。它为每局游戏保存一张共享人物卡,例如 Blades 的团伙、Lasers & Feelings 的飞船,或 Genesys 的 Story Points:

"party": {
  "label": "Crew",
  "fields": [
    { "id": "heat", "label": "Heat", "type": "number", "min": 0, "max": 9, "default": 0 },
    { "id": "vault", "label": "Vault capacity", "type": "number", "min": 0, "max": 16, "default": 8 },
    { "id": "upgrades", "label": "Upgrades", "type": "text", "maxLength": 400 }
  ],
  "pools": [{ "id": "coin", "label": "Coin", "max": { "field": "vault" }, "start": "empty" }],
  "advancement": { "fields": ["heat", "vault", "upgrades"], "text": "Record heat after a score." },
  "text": "Coin belongs to the crew."
}

fields 使用与 sheet.fields 相同的字段结构;pools 使用 sheet.live.pools 的结构,包括 start、group、hideWhen 和 allowTemp。至少需要一个字段或池,其 ID 在队伍人物卡内必须唯一。角色人物卡和队伍人物卡的 ID 可以重合。在这一小版本中,队伍人物卡没有分区、列表、属性、派生值或角色人物卡迁移。池的 max 读取常量或队伍的数字字段。角色公式不能读取队伍的值,队伍公式也不能读取角色的值。hideWhen 也只能指定队伍字段。

advancement.fields 指定 GM 可以永久修改的 number、text 或 longtext 字段。advancement.text 和 text 各接受一行提示词安全文本,最多 400 字符。可选的 summary 复用 gm.sheetSummary:其 fields 选择队伍字段;derived 和 lists 必须为空。没有摘要时,显示所有可见的队伍字段。池总是会显示。

[sheet: sheet="party" op="restore" pool="coin" amount="3"]
[sheet: sheet="party" op="spend" pool="coin" amount="2"]
[sheet: sheet="party" op="advance" field="heat" by="2"]
[sheet: sheet="party" op="advance" field="upgrades" to="Hidden vault"]

sheet="party" 选择共享人物卡;不要写 who。现有的 who="party" 仍作用于每个角色,从不改变共享人物卡。这一小版本允许在队伍人物卡上使用 spend、restore 和字段的 advance。未知的人物卡、不支持的操作、池不足、未声明的成长和无效的值都会被拒绝,并改写为 Engine 的结果。队伍池遵循与角色池相同的当前值/最大值算法和默认值。

GM 每回合会在角色人物卡块旁收到队伍的标签、字段和池。Game Mode 在角色头像旁提供一张可折叠的只读队伍人物卡,使用相同的人物卡呈现方式。永久值在聊天元数据中只保存一份(gamePartySheet),与 gameCharacterCards 并列,并共用成长凭据。实时池使用 rulesetLive 中保留的 $party 条目;规范化后的角色名不会与之冲突。重新生成从回复之前开始,选择或删除备选回复会通过现有历史同时恢复永久值和实时值。没有 party 的规则集保持原有行为。

共享的队伍创建(Capability API 1.163)。 队伍人物卡可以带 lists(最多 12 个,写法与角色人物卡的列表相同)、自己的引导式 builder,以及 fromMembers:最多 8 个从队伍角色读取的队伍数字字段,每个写作 { "field", "count": true }(有几个角色) 或 { "field", "sum": "<character number field>" }(角色们该字段的总和,例如 Totem 点数)。每次读取共享人物卡时,成员字段都会按角色当下的状态计算,因此角色加入或离开会立即改变它们。有列表时,队伍的 summary.lists 可以指定它们。

构建器的写法与角色的完全相同(池、步骤、定价行、消费选项、方案),并针对队伍自己的字段和列表遵循同样的规则。它的目录步骤和复制行步骤可以读取为队伍列表供给内容的规则集目录。在 Game Mode 中,任何玩家都可以打开共享人物卡的 Edit,其中提供引导式设置,保存后对整个队伍生效。每次保存都会注明打开时所基于的修订版本(聊天元数据中的 gamePartySheetRevision,每次保存和故事自身的改动都会使其递增):如果打开之后别人的保存已经落地,这次保存会被拒绝,并显示较新的人物卡,玩家的草稿保持打开,是否再次保存以替换它由玩家决定。

"party": {
  "label": "The Crew",
  "fields": [
    { "id": "tier", "label": "Tier", "type": "number", "min": 0, "max": 4, "default": 0 },
    { "id": "size", "label": "Crew size", "type": "number", "min": 0, "max": 12, "default": 0 }
  ],
  "lists": [{ "id": "upgrades", "label": "Upgrades", "columns": [{ "id": "name", "label": "Name", "type": "text" }] }],
  "builder": { "steps": [{ "id": "upgrades", "label": "Upgrades", "kind": "rows", "list": "upgrades" }] },
  "fromMembers": [{ "field": "size", "count": true }]
}