跳到正文

共享 JSON 剧情节点图 ​

《粥还热》的手写内容源统一为源码仓库中的 packages/dayun-kicker-content/data/chapters/zhou-hai-re.json。Web 和 Godot 消费同一份配置,分别执行相同的节点、条件与状态转换。本文说明 JSON 编辑、可视化编辑器与剧情预览;修改后仍需构建发布,当前没有运行时热更新。

修改与构建 ​

编辑 JSON 后执行:

sh
pnpm story:build
pnpm story:check
pnpm godot:dev

story:build 先做 JSON Schema 和跨节点语义校验,再生成流程 JSON、兼容的章节数值 JSON,以及 ADV 对话程序。派生文件也保存在源码中供构建检查,不能单独手改;CI 的 story:check 会拒绝过期输出。

Web 的 ADV 运行时代码与对白程序继续按需加载。Godot 在构建时把流程与对话装入原生内容包;游戏运行不启动 Node 或浏览器。修改后重新构建 / 导出应用,当前没有对已安装客户端在线下发剧情。

可视化编辑器 ​

在游戏源码 monorepo 的根目录运行:

sh
pnpm story:editor

日常创作默认构建并打开 ADV.JS Electron,在主区域选择「GZA 剧情」。GZA 项目插件位于 packages/dayun-kicker-editor-plugin,共享同一首章 JSON;apps/dayun-kicker-story-editor 只保留启动与调试壳。需要独立页面排查时运行 pnpm story:editor:debug,打开 http://127.0.0.1:4317。工具不进入游戏发布产物,静态构建不带文件读写服务。

左侧切换陈叔对白、章节进程、街头异变、阿婆救援与解药路线,并按 ID、人物或内容搜索当前流程。画布展示节点、入口与连线,支持拖动平移、滚轮平移、缩放、总览和定位;虚线标记含条件的节点或分支。默认自动布局,位置只属于编辑器视图,不写入游戏配置。

右侧可修改人物、对白、事件标题与正文、选项、跳转目标、条件、计时、状态写入及反馈文字,也可添加或删除分支。条件仍使用共享的安全 JSON 表达式。章节配置页可修改接单条件、结章条件与派生事实;完整 JSON 入口用于其他字段和节点结构。未完成的条件输入切换节点后仍会保留,并阻止保存。支持撤销、重做、草稿下载、关闭页面提醒和 Ctrl / ⌘ + S 保存。

保存先经过与 story:build 相同的 JSON Schema、引用与节点语义校验、对白编译,再检查读取时的文件 SHA-256。通过后写入原共享 JSON,并生成 Web 对白程序、双端流程 JSON 和兼容规则 JSON。保存不会直接改写玩家存档;修改后需要重新运行 Web / Godot,原生发布还需重新准备与导出。

  • 若 JSON 被其他编辑器修改,保存返回冲突,保留浏览器草稿和磁盘文件。先下载草稿,再重新读取并合并;不会自动覆盖。
  • 每次保存前在 .cache/story-editor/ 留存源文档和派生产物的原文备份,使用同一目录内的写入锁串行处理本地编辑器。文件逐个原子替换,常规写入异常会回滚;进程或机器在中途退出后,先检查备份并执行 pnpm story:check / pnpm story:build 恢复派生产物。
  • 若异常退出遗留 write.lock,确认没有其他编辑器进程正在保存后再移除锁目录。备份、锁、截图和测试副本均不进入 Git。
  • 同版本删除持久变量、修改类型或移除已有枚举值会被拒绝,需提升内容版本并配置迁移。这个保护不能代替旧存档回归:改变持久状态含义、流程游标或复杂条件后仍应验证历史存档。
  • 含未完成 JSON 字段的“下载草稿”会生成带 chapter 和 invalidFields 的 .story-draft.json,保留原始输入供手工恢复;它不是可直接投入游戏的章节文件。

验证命令:

sh
pnpm story:editor:test
pnpm story:editor:e2e
pnpm story:editor:build
pnpm story:check

story:editor:e2e 运行桌面集成;story:editor:advjs:e2e 运行 Editor 浏览器集成;story:editor:debug:e2e 运行独立调试页面,后者通过独立测试仓库副本验证保存及派生产物、对白和分支条件、事件编辑、外部修改冲突、草稿下载、非法请求与手机布局,不会改动实际章节或玩家存档。

剧情预览模式 ​

点击编辑器顶部的「剧情预览」,直接运行当前内存中的章节草稿,无需先保存。对白由当前草稿编译成 ADV 程序,再交给与 Web 相同的 ADV.JS 运行时;事件、条件、计时和状态写入使用共享内容包中的 ChapterRuntime,读取与 Web / Godot 相同的节点图。预览进度只留在内存中,不写章节、派生产物或玩家存档,也不在浏览器存储中保存测试进度。返回编辑后再进入预览会保留本次进度;关闭 / 刷新页面会清空。

预览界面分三部分,桌面各栏可独立滚动,手机优先展示对白和操作:

  • 中间显示当前章节目标、对白、可选分支和世界事件。使用「联系陈叔」「继续对白」和选项推进,因条件不满足而隐藏的分支会提示数量。
  • 变量面板可调整登记、模拟速度、配送用时、JSON 声明的剧情变量、解药路线模拟和感染者清场状态;可选变量可恢复为未设置。派生事实和接单门槛只读,始终依据当前草稿计算。调试订单完成标记可用于构造测试场景,正式通关验证仍应走接单、取件、交付流程。手动调整流程游标变量时会清空临时计时器,从新游标重新计时。违反剧情状态约束时显示警示。
  • 右侧显示各条流程的激活状态、当前节点,以及最近 200 步的对白、反馈和变量前后变化。点击流程游标或「定位对白节点」可返回编辑器定位对应节点。

「回退一步」会重放此前输入,恢复对白、宿主接单效果和计时进度;「从当前草稿重跑」清空本次测试状态并载入最新配置。一次预览最多 2000 步。修改剧情后,旧预览会提示配置已变化并暂停操作;先通过校验,再点击重跑,避免混用新旧条件。

完整首章的手动验证路径:

  1. 联系陈叔,登记并接园区午餐。每单使用「前往取件点 → 模拟取件 → 前往送达点 → 模拟交付」。
  2. 完成阿婆第一碗热粥,接滨江咖啡。到送达点后先「推进时间」,满足异变条件,再交付咖啡。
  3. 接阿婆第二单,到门口「轻敲两下」,推进到回应;将 granny · 感染者已清场 打开,再连续推进到清场成立,接阿婆出门后交付。
  4. 接解药、取件,点「前往导航点」接阿杰来电;选择主路或绕行,再点「前往导航点」通过所选路口,最后去送达点交付。
  5. 在陈家祠南门联系陈叔,先后完成粮米、工具,再选择喝粥的结尾,读完街坊对白。

预览中的地点是固定的抽象绑定,取送按钮表示完成一次停稳交接;世界时间以可控步长推进,交谈期间暂停。它用于验证剧情文本、分支条件、任务门槛与章节逻辑,不模拟真实骑行、路线寻路、碰撞、感染者 AI、库存、评分或表现动画。完整游戏与 Windows 实机验收仍需另行进行。

预览测试覆盖主路 / 绕行两条首章完整路径、修改后的对白和计时规则、变量变化后的分支刷新、接单及计时回退、非法输入 / 循环保护;浏览器还覆盖整章交互、草稿变更重跑、手机布局、键盘操作,以及预览过程没有发起写入或触碰玩家存储。

与 ADV.JS 的边界 ​

通用剧情创作能力逐步归入 ADV.JS,GZA 作为实际验证项目。第一批已接入 @advjs/core/authoring:现有 Flow 编辑命令、连线投影与布局用于当前编辑器;预览操作历史、最近 200 步轨迹、失败恢复及回退重放使用通用内存会话。配送、地点、清场和计时规则仍由 GZA 适配层提供。Web 正式游戏与编辑器预览共用 courier/service 插件声明,实际请求均由各自宿主校验后返回结果。

现有首章 JSON 仍是唯一手写内容源。适配层将节点临时投影为 ADV.JS Flow 创作文档,修改后还原原来的 dialogue / tracks 结构;世界状态值、分支条件和其他游戏字段保留。投影的 gza 元数据不进入源文件或发布产物。JSON 条件不会隐式改成 ADV 表达式字符串;同章引用也不会隐式改成跨章跳转。已有终止节点携带的历史 next 元数据仍保留,执行时继续遵循原终止语义。

Flow 创作文档 schema 1 与 RuntimeProgram 的版本是两个协议。当前 ADV.JS 上游运行程序使用 schema 2,GZA / Godot 继续固定在现有 schema 1。此次只引入可移植创作模块,未更改运行程序、章节版本、存档 envelope 或存储位置;未经编辑的章节经过投影往返后,生成程序及 hash 相同,现有对白快照可继续恢复。快照随源码固定,独立 checkout 不依赖开发者机器上的 ADV.JS 目录。

第二批已接入 @advjs/gui/authoring 的通用画布、条件检查器和诊断面板。GZA 用轻量适配层提供节点标签、中文提示、JSON 条件与章节校验,保留游戏 token 和 GameButton;共享组件负责平移、缩放、选择、草稿保留、字段错误、诊断分页及定位事件。全部诊断均可分页查看,不再只显示前 100 条。条件语法未完成时保留原文;语义错误继续沿用构建 / 保存校验器,阻止保存和预览。适配器按稳定的内容路径复用,输入完整 JSON 的未完成草稿不会反复重建其他条件校验器。

第三阶段已把预览会话控制、对白播放器、变量检查器与执行轨迹迁到 ADV.JS @advjs/gui/authoring。useAuthoringPreview 管理创建、忙碌、过期草稿、错误和回退;AdvPreviewPlayer、AdvVariableInspector、AdvPreviewTrace 通过受控数据与事件复用。GZA 的 preview-adapter.ts 映射剧情变量、宿主状态、只读事实、流程游标和业务命令;ChapterPreview 继续负责订单、地点、清场、世界计时与章节完成。通用包不读取 GZA 的 JSON 或玩家存档,也不加载 Three.js / Godot。

共享控件保留原本中文标签、GameButton / GameSwitch / EditorSelect 和 UI token。可选值取消与 false、0、空字符串区分;空或越界数值只显示字段错误,不执行宿主命令。轨迹按最新在前显示最近 200 项,回退仍使用完整输入历史;定位保持原节点 ID。固定快照更新为 0.1.4-dayun.9,源清单和对应源码同步更新,运行时 lib、章节 JSON、编译程序及现有存档格式保持原样。Windows 实机继续后置。

第四阶段已通过 Editor SDK 注册 gza.story/workbench 主区域面板。工作台复用同一画布、检查器、预览与轨迹,并通过显式展示控件接口使用 AGUI Button / Select / Switch / Panel 和 Editor 的明暗主题。GZA 业务源码与原运行时在 GZA 仓库构建,通用 Editor 只增加显式可信插件构建入口,不包含 GZA 专用分支。

在 GZA 源码仓库运行:

sh
# 默认查找同级 YunYouJun/advjs/editor/core;其他位置使用 --editor-root
pnpm story:editor:advjs --editor-root /absolute/path/to/advjs/editor/core
# 构建后重复启动可跳过编译
pnpm story:editor:advjs:serve

打开 http://127.0.0.1:4320,在主区域选择「GZA 剧情」;插件列表可启停「GZA 剧情工作台」。首次集成构建需要准备好 ADV.JS Editor 的依赖与现有包产物,详见 Editor SDK 构建说明。本地启动会预编译 GZA 插件,再将其显式加入 Editor catalog;插件产物留在 packages/dayun-kicker-editor-plugin/dist/,Editor 中间产物留在 GZA .cache/story-editor-advjs/,不覆盖上游默认构建目录。它不是远程插件市场,也不改变已安装的旧 Editor 二进制。

浏览器工作台的文档端口固定绑定启动命令所在 GZA 仓库;切换 Editor 标准 ADV 项目不会切换这个文件。Electron 工作台通过下述原生打开流程选择绑定仓库。保存经过原章节 Schema、语义 / 编译、存档迁移兼容检查、SHA-256 冲突检查、写锁与备份,再更新共享 JSON 和 Web / Godot 派生产物。插件视图不接收任意路径或文件句柄,服务仅监听 loopback,同源会话 token 留在端口实现中。静态 Editor 产物自身没有写文件能力。

切换标签保留画布、未保存草稿和预览进度;隐藏时不响应保存快捷键。停用插件会取消请求并移除监听,未保存章节与未完成 JSON 输入保留在本次页面会话内存,重新启用后恢复;预览重新开始。刷新或关闭页面会清除这份内存草稿,应先保存或下载。取消请求不会撤销服务端已完成的写入,再次保存仍检查旧 revision 防止覆盖。

独立页面只保留 pnpm story:editor:debug 调试入口,各入口共享同一插件实现、保存服务和锁。原运行时 schema、章节版本、玩家存档地址与业务规则均不变。Electron 原生菜单与关闭保护现已接入;Studio 嵌入仍属后续集成,Windows 实机继续后置。

Electron 桌面工作台 ​

在 GZA 仓库运行 pnpm story:editor:desktop,先更新 Editor SDK 与桌面 Node bridge,再构建本地插件、Editor 和 Electron 主进程并启动;构建完成后用 pnpm story:editor:desktop:serve 再次启动。可用 --editor-root 指定 ADV.JS 的 editor/core 路径。独立开发配置保存在 .cache/story-editor-advjs/desktop-profile/,不替换日常 Editor 的用户数据或已安装应用。

主区域的「GZA 剧情」向 Editor SDK 登记草稿与保存回调。系统「文件 → 保存」和 ⌘ / Ctrl + S 通过原保存服务更新共享 JSON、Web 对白和 Godot 流程;隐藏标签也可原生保存。未保存修改会显示标题标记与状态栏提示。关闭、退出、重载或返回欢迎页时使用原生「保存并继续 / 取消」;校验失败、JSON 尚未完成、外部版本冲突或保存未完成时保留窗口及草稿,成功落盘后才继续。存在未保存内容时不能停用插件,需先保存或明确重新读取以放弃草稿。

桌面开发宿主只通过固定 loopback 文档端口访问 GZA 服务;插件不接收 Electron 文件句柄或项目凭据。浏览器入口、章节 Schema、备份、写锁、业务条件与旧存档迁移保持同一实现。当前验收是 macOS 开发 Electron 客户端;未制作或替换已安装的签名发行包,Windows 实机仍后置。

桌面「文件 → 打开工作台仓库 / 切换工作台仓库」使用系统目录选择器,选择包含项目标识、共享 JSON 与已有剧情产物的完整 GZA 源码目录。JSON、语义、编译与固定输出路径先通过检查,再进行切换;不执行所选仓库的脚本或项目配置。普通打开与最近记录默认在当前窗口替换绑定;如该真实路径已在另一窗口打开,则聚焦已有窗口,其草稿继续保留。新建窗口可独立打开另一仓库。

切换前复用「保存并继续 / 取消」。保存失败、外部冲突或未完成 JSON 时保持当前仓库与草稿;等待新服务期间发生的新修改也会再次检查。取消目录选择或选择无效 / 已移动目录不会改绑,也不新增最近记录。窗口标题、状态栏和工作台显示当前仓库;每个窗口拥有独立读写会话,旧端口令牌在切换后退役,旧草稿不会套用到新仓库。工作台布局按仓库保存在用户目录,预览在新绑定中重新开始。

「最近仓库」与项目切换器共享最多 12 条真实路径记录,符号链接别名会合并;重启恢复上次成功打开的仓库,路径失效时提示并回退到启动仓库。可从欢迎页移除记录,源文件不会删除。记录保存在独立 profile 的 workbench-gza.story-recent.json,与普通 ADV 项目的 recent-projects.json 分开。浏览器与独立编辑器仍绑定启动仓库;玩家存档和游戏运行时格式不随工作台切换改变。

2026-10-16 仓库集成已通过 7 条 macOS 开发 Electron E2E,覆盖此前保存 / 关闭保护、原生目录选择、保存与取消切换、校验 / 冲突阻止切换、最近记录与重启、真实路径去重、失效路径以及双窗口仓库隔离。测试使用临时内容副本与独立 profile,不修改真实章节或玩家存档。Windows 实机继续后置。

标准插件包与宿主适配 ​

GZA 项目插件是私有 workspace 包 @yunlefun/advjs-editor-plugin-gza,插件 ID 仍为 gza.story,面板 ID 仍为 gza.story/workbench。包清单 advjs.editorPlugin 使用版本 1,声明构建入口、样式与 Node 宿主入口;Editor 通过 ADVJS_EDITOR_PLUGIN_PACKAGES 接收显式可信包目录。通用 SDK 不含 GZA 专用代码,清单加载不执行宿主入口,也不读取所选仓库的可执行配置。当前采用可信构建集成,未实现已安装应用的在线插件安装。

插件 ./editor 的 createGzaEditorPlugin(host) 通过 GzaEditorHostAdapter.createDocumentPort(context) 接收有限读写接口。展示控件通过 GzaEditorControls 注入:Editor 使用 AGUI,独立调试壳使用游戏基础控件;不再通过 Vite 改写组件路径。画布、检查器、预览、草稿与业务命令只有一份实现。

Node 宿主通过 SDK EditorProjectHostAdapter 的 inspect / open / validateAccess 适配。SDK 统一管理控制鉴权、窗口会话、退役与销毁;GZA 插件继续负责目录识别、共享章节校验、输出编译、存档兼容检查、冲突、锁及备份。提交前必须重新验证会话和路径,切换后旧请求不能继续落盘。CLI 和插件共用内容包的 chapter-outputs 输出函数。

该重构保持共享 JSON、生成结果、旧存档、Web / Godot 运行协议及固定运行时 lib 不变,插件 SDK 源快照为 0.1.4-dayun.9。Windows 实机验收仍后置。

2026-10-16 插件包收敛回归通过:GZA 编辑 / 兼容测试 49 项、旧存档测试 31 项、独立调试页面 17 条、Editor 浏览器集成 5 条、macOS 开发 Electron 7 条,以及普通 ADV.JS 桌面回归 3 条。SDK 另通过 40 项注册、清单、宿主与文档生命周期测试;Godot 通过原有基础 / 章节 / 双端对照、跨进程恢复与两条首章路线检查。运行时 lib 的 16 项 SHA-256 保持不变,真实共享 JSON 未修改。

内容放在哪里 ​

配置可修改内容
dialogue.nodes角色、对白、选项、显示条件、跳转和登记 / 接单 / 收尾请求
acceptance / finishWhen各订单的前置门槛与首章完成条件;对话中的任务选项通过 fact 引用同一门槛
tracks章节进度、街角异变、阿婆救援、解药路口的节点与边
rules等待秒数、停靠 / 清场半径、遭遇数量、速度与接触参数
points / navigation订单取送点、来电 / 主路 / 绕行点引用及导航目标
encounters / refugeStages遭遇的生成 / 模拟条件、救护棚 / 厨房 / 修车点阶段
objectives / presentation目标提示、世界交互文本、护送表现与结束卡
variables / invariants / migrations可保存状态、状态约束与节点改名映射

对话节点支持 dialog、choice、command、anchor、end。世界轨道支持 wait、timer、interaction、end。只有执行器已经实现的能力可通过 JSON 编排;新增潜入、驾驶新载具等玩法时,仍需实现对应的宿主能力。

例子:调整阿婆回应时间 ​

修改 rules.granny.replySeconds 即可:

json
{
  "replySeconds": 3
}

敲门节点沿 next 进入 knocked;该计时节点读取上述规则,到时进入 answered,再等待门口连续安全,最后开放主动接人选项。两端使用相同配置;暂停、地图、对白和未加载的遭遇不会推进清场。

mermaid
flowchart LR
  home[门口停稳] -->|主动敲门| knocked[等待回应]
  knocked -->|运行达到设定秒数| answered[门口清场]
  answered -->|连续安全| clear[返回接人]
  clear -->|主动护送| escorted[救援完成]

轨道的 state 指向一个持久化状态字段。每个节点的 value 是该节点对应的存档值,执行 next 时写入目标值;因此没有另一份可能漂移的游标。新增阶段需要同时声明变量允许值和对应节点。

条件使用结构化表达式:all / any / not、比较、已完成订单、变量 / fact / 门槛引用、靠近地点、已加载且安全的遭遇。配置不执行 JavaScript 或 GDScript。状态效果只允许写入已声明的剧情变量;积分、订单结算和通关请求由宿主重新校验,不允许 JSON 直接加钱。

现有存档兼容 ​

保留原有 dayun-kicker 标识、地图原点、订单 ID、积分 / 职业、外观和世界位置。Web 存档仍使用原有 envelope,原生仍使用 schema 2 与独立 M2 路径,本次没有改写旧客户端的存储位置。

旧档里的 outbreakSeen、grannyRescue、antidoteRoute、antidoteCheckpoint 等字段直接对应新节点;缺少图版本标记的历史档按图版本 1 处理。进行新节点转换后记录 graph.id 和 graph.version。临时计时器在重启时重新计时,已完成的敲门、清场、护送和路口步骤保留,不倒退积分或重复发奖。

对白更新使程序 hash 改变时,本次交谈从入口重新开始,世界与配送进度保留。接单和收尾依然由宿主校验,恢复对白不会重复结算。

一般修改对白、数值和条件无需提升图版本。删除或改名已保存的节点值时,提升 version,为旧版本提供映射,例如:

json
{
  "from": 1,
  "to": 2,
  "rename": {
    "grannyRescue": { "clear": "safe" }
  }
}

这个示例还需要同步修改对应节点、允许值、边和引用条件。迁移会在内存中校验,再按既有存档流程写入;不发放奖励。未知的未来版本、不同章节或缺少版本迁移链时保留原文件,并阻止自动覆盖。原生主档即使有可读的旧备份,也不能覆盖未来剧情格式。

订单 ID、城市 / 世界 ID 保持稳定。删除订单、改地图原点或迁入未支持的玩法状态,需要单独的数据迁移,系统不会猜测。旧 Web / Electron 与原生之间的预览导入仍属于 M3,本轮兼容的是各自已有的存档。

校验与验证入口 ​

sh
pnpm story:check
pnpm typecheck:content
pnpm godot:test
pnpm godot:test:chapter:rendered
pnpm godot:test:ui

JSON Schema 提供编辑器补全与结构检查;构建另外验证节点 / 地点 / 订单引用、变量、条件、宿主命令、轨道可达性和版本映射。双端测试用相同观察序列比较状态、选项与交付门槛,包含仅修改 JSON 等待时间 / 清场半径的变体、取消输入、读档与节点改名。

本轮已完成 88 个 Web / 共享内容测试、18 个浏览器 E2E(全章、对白按需加载、存档、窄屏 / 触屏与虚拟手柄)、原生 74 项基础与 2,355 项首章检查、45 段双端行为 / 版本对照,以及 23 项原生 UI / 输入检查。原生渲染窗口通过约 20 公里连续章节和两条解药路线;迁移前真实 M2 的通关档(3,056 分)和解药来电档(815 分)在独立进程恢复,积分、位置、订单和阶段保持。实际 macOS 导出包也在源码目录外启动并读取旧通关档。原始日志、截图和样本留在私有忽略目录。

Windows 实机按维护者安排继续后置,实体手柄仍待验收;渲染自动驾驶用于功能验证,不替代同画质性能或人工全章验收。M2 街区与平台范围见 M2 记录。

文档文字采用 CC BY 4.0;素材与品牌另行授权