剧本框架约束 V4
任何剧本须符合本框架才能录入。下面是要点;完整规范、字典规范与两份可直接照抄的最小示例都在剧本站(免登录可看可下载)。校验规则见 script_validator.py,与本文冲突时以校验器为准。
顶层结构
{ meta, scenes, map, starter?, events?, battles?, npcs?, items?, shop?, quota? }
必填:meta / scenes / map。其余可选。
meta
| 字段 | 必填 | 说明 |
| id | 必 | 全局唯一,拉丁/数字/下划线。入库后不可改(它是主键) |
| title | 必 | 显示名,建议 12 字以内 |
| initialScene | 建议 | 起始场景,须存在于 scenes |
| completionScene | 建议 | 通关场景。字符串或数组(数组 = 到达任一即通关),须存在于 scenes |
| version / description | 可选 | 你的版本号 / 一句话简介 |
| maxPlayers | 可选 | 建议人数:整数或区间(如 "2-4"),缺省按 4 |
| chapters | 可选 | 章节分组 [{id,name,cell_ids|scene_ids}];只写 scene_ids 也能用(引擎按场景反推格子) |
| xpFromOptions | 可选 | 打开(true)后,选项/事件里写的 effect.xp 才真发经验;不写则选项经验一律不发 |
场景 scene
- 必填:
id / name / desc(desc 是进场正文,写具体、写感官)。
- 可选增强:onEnter(每次进入追加)/ initial / ambience(氛围)/ details(细节)/ hints(提示)/ npcs(站谁)/ interactables(可交互物件)/ triggers。
- 选项的
goto / onPass.goto / onFail.goto 目标必须在 scenes 里。
- triggers 里 when.battleId 对应的战斗必须存在。
选项 option
{ "id","text",
"check":{ "kind":"skill|save|flat", "ability":"cha", "skill":"游说", "dc":11 },
"onPass":{ "goto":"...", "note":"...", "effect":{...} }, "onFail":{...},
"require":{...}, "effect":{...}, "note":"...", "once":true, "hidden":false }
- 无检定的选项直接用
goto + note + effect;写了 check 就按成功/失败分开写。
- onPass / onFail 可以直接写成一整句字符串,那句话就是结果文本。
skill 必须是 18 个规范技能名之一(体操/驯兽/奥秘/运动/欺瞒/历史/洞察/威吓/调查/医药/自然/察觉/表演/游说/宗教/手上功夫/隐匿/生存),写错会静默退化成按属性掷。
once = 这个按钮只能点一次;hidden = 按钮不给玩家看,只进 AI 上下文(暗门用)。
- require:item / not_item(道具,数组=任一/全不能有)、spell / class(全队任一人满足即可,职业中英文都认)、flag / not_flag(数组=全都要/全都不能有)、or_flag / not_or_flag(数组=任一满足/任一即不行)。
效果 effect(写了才真生效)
set_flag · give_item / grant_item · consume_item · gold · xp
heal · full_rest · trigger_battle · trigger_event
引擎只读这几个键:set_flag(字符串/数组/字典都行)、给道具、消耗道具、赏金(写负数就是扣钱)、经验(要先开 meta.xpFromOptions,同一个选项只发一次)、回血(整数或 "2d4+2")、长休、开战、触发事件。写别的键不会报错、也不会生效 —— 描写请放进 note。
战斗 battle
{ "id", "enemies":[{"monsterRef":"wolf","count":2}], "terrain",
"reward":{ "gold":50, "xp":100, "items":["potion"] }, "escapeDc":12 }
monsterRef 必须在怪物图鉴里真实存在(校验器会查);reward.gold=队伍局内金币(队级共用);reward.items 进整局共享背包。局内 progress.gold(战斗得、商店花)与跨剧本 characters.gold(通关钱袋)是两套,剧本只写 reward.gold。
战斗胜负必须写清"打完之后去哪":在任意场景放一条 battle_win 触发器(见下),否则玩家打赢只有系统那句「战斗胜利!」,剧情推不动。
触发器 trigger
{ "id":"trg_boss_win", "when":{ "kind":"battle_win", "battleId":"fight1" },
"then":{ "set_flag":"cleared", "show_text":"……" } }
when.kind 只有两种:battle_win(指定 battleId 打赢时,引擎全剧本找、写在哪个场景都行;when.sceneId 只影响"自动去哪"的路由,不写就留在原地)、flag_set(指定 flag 被置位时)。then 支持 set_flag 与 show_text。
商店 shop(顶层可选)
"shop":{ "goods":[ { "id":"potion","price":50,"chapter":"phand" } ] }
goods[].id 须在 items;price 缺省用 items 的 value;chapter 可选(缺省全章可购,指定则仅该章节开放)。购买=团长操作,给指定成员,扣队级金币。NPC 自己的货架写在他身上(shop.sells)。
地图 map + 探索点
{ "gridW","gridH","cells":[{id,x,y,label,sceneId}],"paths":[{from,to}],"playerStart","exploreNodes":[] }
cells 非空;playerStart 在 cells;paths 两端在 cells。探索点 exploreNodes:id 唯一、type∈npc/chest/landmark/env、cellId 填该格绑定的场景 id(不是 cell_xxx)、label/desc/info 必填、once 布尔、px/py 是 0~1 小数、effect 里给的道具须在 items。
三个最容易踩的坑
- 要得而给不出:某个道具被
require.item 要走了,剧本里却没有任何地方给出它(选项的 give_item、探索点的 effect、或商店能买)—— 那个按钮就永久点不动。
- 出口被堵死:一个场景的出口全被 require 卡住 = 玩家卡死。至少留一条不需要任何条件的出口。
- 打赢了不推进:battle_win 触发器的 battleId 写错(或漏写),打赢也不会有下文。
AI 任意行动
局内左下可自由行动(走 AI)。引擎自动从「当前场景可直达 goto + 当前章场景」生成白名单,AI 只能跳白名单内场景,不能现造。作者只需保证选项 goto 指向真实场景。
边界也是机械的(2026-09-23 起):玩家在描述里自己写的结论不算判定(命中带 check 的选项照样掷你写的 DC);被 require 或字典 rules 引用过的 flag,AI 一律置不上;开战选项不会被 AI 照点结算,还没打赢那场仗的战后落点也不在它的可跳转白名单里。详见字典规范「哪些 flag 允许 AI 自由置位」。
分段式录入
可先建骨架(meta/map/主场景),再按章节分批上传 scenes/events/battles 等段,或直接走剧本站的投稿(校验通过后进你的私人剧本,只有你能开房)。段内 + 段间引用须完整可解析。