storyline/escape/simon 首批之后的推广收官:15 款全部按 26 号模式运行 (发言权/回执/三档提示/迷茫接管/手势阶梯/断点复述),旧「双击重听/格挡/ 菜单确认」等反模式口径全线废除,开场零规则念白(首句即动手)。 游戏层(12 款):rpg(格挡=前摇窗口内再单击)/ rhythm / space / radio / dungeon / abyss / detective / cipher / teahouse / keeper / bridge / hunt(联机切片): - 每款 game.js 接引擎六模块 + makeInputPipeline(doubleTapWindow:0); 四向玩法类 swipeCore + 长按帮助;双击是玩法的(space 护盾 / hunt 旋风斩)按手势阶梯阶段 3 分档恢复双击窗口(doubletapCore) - 每款 playtest 重写:玩法断言 + §6.2 乱点机器人(无声操作 / 被动 原句重复 / 打断失败 = 0);15 款 3900+ 断言全绿 - 12 份 docs/design/26-apply-*.md 施工单 + tools/voice_lines_26/ 台词清单 + voice-ids 文档同步 + 转写会话剧本按新口径重写 引擎层(web/engine/,6 处修复):pipeline 增 doubletapCore;novice teachLine 带 voice/ 前缀(此前教学句被静默丢弃);confusion.record 前置到新手门(锁定手势的乱点也计信号);InvalidHints 播完恢复解释前 状态(根治孤儿 BUSY);WaitPoint takeover(2) 台账去重;台账只在 speak 真正播出时登记。遗留引擎债登记 roadmap §3.1。 语音资产:台词表合并(新增 ~250 系统句 / 改 ~50 旧口径 / 删 ~110 废止句;合并器 tools/merge_voice_lines_26.py + 引用校验器 tools/validate_voice_refs.py),GPU 重录 1536 wav → 4204 ogg/m4a 落地,check_all_assets + validate_voice_refs 双绿;键盘变体同步扩表。 大厅/文档:15 款统一新交互口径(「新交互」徽标退役);README / HANDOVER / rpg README 旧口径修正;roadmap P0-P3 置完成。
158 lines
13 KiB
Markdown
158 lines
13 KiB
Markdown
# 26-apply · 《守塔人》重构施工单(26 号文档落地)
|
||
|
||
> **设计单正文见 [21-keeper.md](21-keeper.md) 附录 C**(一句话核心动作、状态×手势
|
||
> 矩阵、等待点配置表、首 3 分钟逐秒脚本、失败表——不在本文重复)。本文件是瘦版
|
||
> 施工单:只写实现接线、偏差记录、台词类别映射、测试记录。台词登记见
|
||
> [21-keeper-voice-ids.md](21-keeper-voice-ids.md) R2 增量节;清单见
|
||
> `tools/voice_lines_26/keeper.md`。交互权威:
|
||
> [26-audio-interaction-playbook.md](26-audio-interaction-playbook.md)。
|
||
> 范式对齐 [26-apply-storyline.md](26-apply-storyline.md)(纯叙事主参考)、
|
||
> [26-apply-radio.md](26-apply-radio.md) 与 [26-apply-abyss.md](26-apply-abyss.md)
|
||
> (叙事型 + 双音色组同型——KEEPER/SHORE 与 HOST/PROD 同构;pendingChoice 缓存、
|
||
> 打断快照 `_chainSpeaking`、sessions 范式重点参考 abyss)。
|
||
|
||
## 1. 一句话核心动作
|
||
|
||
**听一夜值守,单击继续;听到选项时左右滑:守灯还是救人、说出还是守住。**
|
||
|
||
## 2. 实现接线(web/keeper/game.js + index.html)
|
||
|
||
六模块(§4.1/§4.2/§4.3/§4.4/§4.5/§4.12)全部接通,玩法核心(风暴夜时间轴
|
||
INTRO→C1→CALM→C2→CALM→C3→p9→ENDING、回忆插叙三段、trust×truth 双标量、
|
||
9 抉择 4 结局、KEEPER/SHORE 双音色 + NPC 三代声、800ms 级结算保护、底噪互斥
|
||
vinyl/storm、雾角四处兑现、存档 summary)不动,交互层整体换成 26 号模式:
|
||
|
||
- **接线**:`this.receipts = new Receipts(audio)`、`this.floor = new Floor(audio)`、
|
||
`this.confusion = new Confusion({ onConfused: (lvl) => waitPoint?.takeover(lvl) })`、
|
||
`this.invalidHints = new InvalidHints().bind(this.floor)`、`this.ladder = new
|
||
GestureLadder()`(叙事类长按不占用:`setRateBoostEnabled(false)`,新手期
|
||
longpress 由管道判 invalid)。`index.html` 照 storyline 同构:
|
||
`makeInputPipeline({ floor, confusion, receipts, invalidHints, game, ladder,
|
||
opts: game.PIPELINE_OPTS })` + `attachGestures(root, pipe, { doubleTapWindow: 0 })`,
|
||
启动遮罩保留。doubleTapWindow: 0 → 未解锁双击由管道折算成 tap。
|
||
- **`onGesture` 返回值收口**(§4.2):`'ok'`(继续/抉择/菜单/状态)|`'hold'`
|
||
(重听、菜单换项——等待点保留)|`'silent-ok'` 本作无(无即时内容音手势)|
|
||
`'invalid'`(pipeline 给回执 + explainOnce + 迷茫信号 + settleWaitPoint(false))。
|
||
- **语音链 = floor.speak(critical/kind)**:节点链全部改走 floor;拆链实现句级
|
||
critical(abyss `_speakNodeVoices` parts 模式)。跳句/打断由 floor 收口:
|
||
tap=consumed 跳本句、滑动=打断并执行、critical=busy——旧 PROMPT_RE 门 / 锚句
|
||
空点 beat 震 / F4 恢复锁 / F5 交接尾句保护 / G5 首播锁被结构性消解。
|
||
- **等待点(§4.4 附录 A)**:intro(6/15s)|continue(13/30s)|choice(25/50s,
|
||
full=当前 pN 且链播即登台账)|settle(20/45s)|reopen(12/30s)|menu(20/45s)。
|
||
三档提示 + 30s 台账 + 主动重听「再说一遍:」全在 engine/hints.js。
|
||
- **开场 §4.10**:冷启动第一句 = `sys_first_tap`(critical 指令句,0~3s 动手);
|
||
第一下单击 → `[b_open, b_rain]`(雾角挂 b_rain 播完)→ `b_goal` 极短目标 →
|
||
continue 等待点;`sys_turn_teach` 挂 amo_a 链尾、「轮到你」状态声首现教学,
|
||
`sys_skip` 挂 amo_b 链尾(radio z1_a/z1_b 先例)。**b_rule 开场规矩念白与
|
||
gd_brief 操作念白废除**;重开新夜链 = sys_new_night → b_open → b_rain(雾角)
|
||
→ b_goal,教学句不再播(原则 11)。
|
||
- **手势阶梯(§4.3)**:第一次抉择(choose 首次)→ `ladder.onGoal()` 解锁上滑,
|
||
「对。」(sys_praise)挂下一段链头、教学句挂链尾;第一结局(enterEnding)→
|
||
`ladder.onGameDone()` 解锁下滑/第 3 局双击,教学句挂结局链尾。`teachLine()`
|
||
已带 `voice/` 前缀,游戏侧直接引用不拼前缀。
|
||
- **上滑「我在哪」(21 附录 C §3:时位句先行 → 信任档 → 一个动作)**:
|
||
now 线 = `prog_m{act}` → `tr_{tier}` → `tru_{truth}` → 动作尾巴(choice 等待=
|
||
status_choice / 其余= sys_hint_continue_full);MEMORY = `prog_mem_*` → `tr_*`
|
||
→ `tru_*` → `sys_mem_back`;ENDING = 结局主句 → `tr_*` → settle 动作句。
|
||
状态句全量 markHintSaid(原则 9);播完回原等待点(radio 口径 resumeTurn,
|
||
档位计数保留),等待点不在(旁白被打断/自动流)则按来源重建,不重播节点。
|
||
G6 顺序(prog → tr → tru)由 playtest 断言锁定;G2 的 tr_thin 附 warning 震
|
||
保留首夜首幕豁免(自然起点不是警告)。
|
||
- **断点复述(§4.12)**:每抉择结算、CALM/ENDING 进入、菜单「保存并回大厅」写
|
||
`soundgame.progress.keeper`(含 timeline 时空位 + goalMet/turnTaught/skipHintShown
|
||
教学标志;**summary 必带**;MENU 落盘记回原状态)。回来:sys_continue_ask 询问
|
||
(tap=继续上次 / right=新风暴夜);MEMORY 存档恢复链 = prog_m → prog_mem →
|
||
重播当前回忆句起链(首句 critical,F4 等价),≤5s 回到可玩。
|
||
- **迷茫检测(§4.5)**:confusion 信号(含管道在 invalid 时 recordInvalid、等待点
|
||
静默阈值喂 recordSilence)→ 当前等待点三级接管(L1 短句 → L2 只留期待手势 →
|
||
L3 演示:intro/continue 替玩家走一步,choice 只复述方向不替选)。
|
||
|
||
## 3. 偏差记录(与 26 号手册 / 21 号附录 C 的实现偏差)
|
||
|
||
1. **800ms 结算死窗升级为整句 critical**(abyss 先例):结局链首句(e1~e4_a)、
|
||
c3_dark(灯灭差分插播,auto+@x 尾句锁)、rev_teach(时空教学)、插叙锚句首播
|
||
(rev_g1/g4、rev_f1/f5、rev_c1/c5)、恢复链首句(_resumeGuard)整句 critical
|
||
——tap/滑动给 busy 回执不截链,覆盖旧 `_settleGraceUntil` 800ms 定时窗且更强。
|
||
插叙入口 600ms 静默拍期间 floor=BUSY,手势同样 busy 回执(死窗语义等价)。
|
||
2. **p* 选项句整句 critical**(radio 口径):p1~p7、p8_boat/p8_reef、p9a/p9b 首播
|
||
不可跳(tap=busy),链播即 markHintSaid——被动提示永不逐字复述选项。21 附录 C
|
||
§8 台词表把 p* 归 content 指内容属性;矩阵 §4「p* 首播不可跳」以 critical 落地。
|
||
3. **旁白链滑动 = 缓存抉择 + 跳选项句**(abyss 模式,兑现矩阵 §4「缓存抉择,不
|
||
盲选」):旁白(含被打断快照 `_chainSpeaking`)中 left/right 若落在抉择点
|
||
(本节点 next 直入 p*),缓存 pendingChoice 并 playNode(p*);选项句整句 critical
|
||
播完当刻缓存生效(ui_confirm → 结果节点,W2 契约 + 盲玩公平)。非抉择点滑动
|
||
→ invalid + `_recoverInterrupted` 交回 continue 等待点(零无声死锁)。
|
||
4. **MEMORY 打断恢复 = 重播当前回忆节点**:插叙是 auto 链不设等待点;滑动打断/
|
||
双击重听/菜单关闭/上滑播完都从「当前回忆句」(memIdx,由 audio.play 钩子实时
|
||
维护)重播起链,复播不锁锚句(G5 复播可掐);链末照常自动回返现在线。
|
||
MEMORY 中 left/right = invalid(插叙无选择,21 附录 C §4)。
|
||
5. **CALM 幕间为自动流(BUSY)**:链播中 tap=跳句(consumed)、空档手势=busy;
|
||
滑动打断后不重交话轮——`_ensureWait` CALM 分支直接挂喘息定时器推进下一幕
|
||
(radio BRIDGE 同款)。跨档 tr_* 账目在链构造时结清(radio enterBridge 口径;
|
||
不再逐句起播钩子)。
|
||
6. **等待声**:六等待点统一引擎合成「嘟—嘟」bed;插叙/幕间/危机拍为 BUSY 演出
|
||
态无等待声(原「统一 8 音词表纪律、时空不换 earcon」继续成立——本作不新增
|
||
任何 earcon,时空签名全靠内容层底噪/BGM/锚句)。
|
||
7. **过渡音 idle 门简化**:memory_in/out、static_burst 只在语音空闲时播出(原
|
||
「挂队句间补发」依赖游戏层句级钩子,floor 时代无此钩子;调用点全在链入口前,
|
||
在播即丢弃,playtest 断言成对性不受影响)。
|
||
8. **`_chainSpeaking` 打断快照为游戏层补丁**(abyss 同款):pipeline 的
|
||
floor.handleInput 对滑动「打断并执行」先杀链置 BUSY,游戏侧 floor.speaking
|
||
已 false——快照在 speak 置位、onDone/interrupt 清零,让被掐断的旁白链按矩阵
|
||
落「旁白中」分支(上游修复后可撤)。
|
||
9. **旧交互债务清除**:双击=重听节点旧口令废止(阶段 3 起=重听当前等待点完整句
|
||
/当前回忆节点);菜单改 00 号 §5.4(左右滑换项 + 单击确认,旧「单击循环+双击
|
||
确认」= 26 §7 反模式 5);guide 层看门狗双轨/无进展轨/sys_help 帮助溢出全部
|
||
废止(三档提示 + 迷茫接管 + 手势阶梯取代)。
|
||
|
||
## 4. 台词类别映射(类别标注;增量清单见 `tools/voice_lines_26/keeper.md`)
|
||
|
||
- `critical`:sys_first_tap(开场 ≤3s 唯一指令);p1~p7、p8_boat/p8_reef、p9a/p9b
|
||
选项句(整句,首播不可跳);rev_teach(时空签名教学,全局一次);插叙锚句首播
|
||
六句(rev_g1/g4、rev_f1/f5、rev_c1/c5);c3_dark(灯灭差分);结局链首句
|
||
(e1~e4_a);恢复链首句(F4 等价)。
|
||
- `content`:b_*、c*、amo_*、rev_* 中段句、e*_b、ep_*、prog_m1..3、prog_mem_g/f、
|
||
tr_deep/half/thin、tru_zero..full、lull_1/2、sys_turn_teach、sys_skip、
|
||
sys_unlock_*、sys_new_night、sys_continue_ask、sys_menu、menu_help、
|
||
sys_menu_resume/restart/lobby、sys_settings。
|
||
- `hint`:sys_hint_continue_full/short、sys_hint_choice_short、sys_hint_again_full、
|
||
sys_hint_menu_short、sys_only_choice、sys_demo_continue、sys_demo_choice、
|
||
sys_mem_back、status_choice、sys_say_again(「再说一遍:」前缀,engine/hints.js
|
||
播出)、sys_first_tap_short。
|
||
- `feedback`:inv_nosel、inv_mem、inv_generic、inv_up_locked、inv_down_locked、
|
||
sys_praise(「对。」首抉择链头)。
|
||
- 音色组:系统/交互层句子全部 `_SHORE`(岸台值班员=系统音,与 radio PROD、
|
||
abyss DECK 同型);剧情守塔人 `_KEEPER`、NPC 平铺(阿澈/父/祖)不动。通用系统
|
||
句与 STORYLINE_LINES 的 sys_* 段逐字一致;键盘变体照 KEY_LINES["storyline"];
|
||
多音字「重听」→「重新听/再听一遍」。
|
||
- 完整登记(新增 24 / 修改 1 / 删除 8)见 `tools/voice_lines_26/keeper.md` 与
|
||
[21-keeper-voice-ids.md](21-keeper-voice-ids.md) R2 增量节。
|
||
|
||
## 5. 测试记录
|
||
|
||
- 无头 playtest:`web/keeper/test/playtest.mjs` **272 断言全绿**(`tools/node/bin/node
|
||
web/keeper/test/playtest.mjs` 退出码 0;`node --check web/keeper/game.js` 通过;
|
||
`game.floor.bedEnabled = false` 防周期定时器不排水)。覆盖:§4.10 首 3 分钟
|
||
(3s 内动手、critical 指令句、规则念白零播出、开局 40s 内听到阿澈);剧本 A~F 六路径
|
||
四结局 + 信任/真相逐档验算 + 尾声回响差分;时空签名(vinyl 与 storm/room/engine
|
||
播放区间零重叠、memory_in/out 成对 ×3、BGM 切换、危机拍前置 2s);上滑状态链
|
||
G6 顺序(prog → tr → tru → 动作)与 tr_thin 首夜豁免;pendingChoice 时序
|
||
(旁白滑动缓存 → 选项句播完 → ui_confirm → 结果句);锚句首播 critical /
|
||
复播可掐;结算保护(结局链/c3_dark 首句 critical,幽灵手势不截链);等待点三档
|
||
(continue 13/30、主动重听「再说一遍:」);迷茫接管三级;手势阶梯(阶段 1/2
|
||
教学句挂链头/尾);§4.12 断点复述(ASK 左/右、MEMORY 恢复链时位句先行、F4
|
||
恢复首句锁);菜单(左右滑换项 + 单击确认 + 插叙中开关菜单底噪不丢);CALM
|
||
幕间打断后喘息照走;资产缺失续链(Floor 哨兵 3s 宽限);键盘变体路由;
|
||
**§6.2 乱点机器人**:无声操作 0、被动原句重复 0、打断失败 0、第一次操作
|
||
≤10s、第一次成功 ≤20s、乱按不锁死(零死锁收在合法等待态或结局);静态审计
|
||
(无运行时 id 拼接、8 句废止台词零引用、20 条游戏引用新台词待生成豁免 +
|
||
引擎侧 4 条入清单一致)。
|
||
- 玩家感知转写:`web/harness/sessions/keeper.mjs` 已同步 26 号口径(开场第一下
|
||
tap、floor.state 链级门控、菜单左右滑换项+单击确认、插叙中不动手——「回忆没有
|
||
选择」、CALM/危机拍 BUSY 段等它走完),`tools/node/bin/node
|
||
web/harness/transcribe.mjs keeper`(含 `--session sessionStress`)两剧本零死锁
|
||
跑通(主线虚拟 290s 到 E1 灯与归人;乱按剧本 643s 同样走完结局链)。
|
||
- 新语音资产(24 句 SHORE 组 + 键盘变体)由主会话统一生成;playtest mock 与真实
|
||
资产解耦(voice 层按可用处理,Floor 哨兵兜底缺失)。
|
||
- 真人冷启动:待测(26 §6.3,3~5 人)。
|