Files
xsl ee5d4d2544 feat(playbook): 26 号交互手册全量落地——剩余 12 款深度重构 + 语音全量重制
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 置完成。
2026-10-02 12:33:20 +08:00

180 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 26-apply · 《暗码》重构施工单(26 号文档落地)
> **设计单正文见 [19-cipher.md](19-cipher.md) 附录 C**(一句话核心动作 / 状态 /
> 手势表与解锁 / 状态×手势矩阵 / 等待点 / 首 3 分钟脚本 / 失败表 / 台词账目 /
> 断点复述),本施工单不重复,只写:实现接线(§1)、台词类别映射(§2)、
> 偏差记录(§3)、测试记录(§4)。交互规则权威来源:
> [26-audio-interaction-playbook.md](26-audio-interaction-playbook.md)、
> [00-global-ux.md](00-global-ux.md)。台词登记 [19-cipher-voice-ids.md](19-cipher-voice-ids.md)
> R4 节;清单 `tools/voice_lines_26/cipher.md`。
> 范式对齐 [26-apply-storyline.md](26-apply-storyline.md) / [26-apply-radio.md](26-apply-radio.md)
> (叙事型主参考;OP/WIND 双音色组与 radio 的 HOST/PROD 同构)。
> 本次只动交互层:电文生成(4 封固定字符)、摩尔斯运行时合成(morse.js)、暴露度
> 隐藏账本、4 结局、OP/WIND 双音色一概不动。
## 1. 实现接线(引擎六模块,对齐 storyline/radio 参考)
- **装配**(game.js 构造器 + index.html):`this.receipts / this.floor / this.confusion /
this.invalidHints(= new InvalidHints().bind(floor))/ this.ladder` 全部暴露;
`audio.setRateBoostEnabled(false)`(长按不是玩法、也不触发旁白快进;摩尔斯 u 恒定
不受 settings.speed)。index.html 照 storyline/radio:`makeInputPipeline({ floor,
confusion, receipts, invalidHints, game, ladder, opts: game.PIPELINE_OPTS || {} })` +
`attachGestures(root, pipe, { doubleTapWindow: 0 })`——叙事+解码型无核心玩法手势
opt(doubletapCore/swipeCore/longpressCore 均 false),双击未解锁折算单击由管道
新手门承担。
- **onGesture 收口**:返回 `'ok' | 'hold' | 'invalid' | 'silent-ok'`(摩尔斯即内容音
的操作给 hold 附 ok 回执;无 silent-ok 档位用法——全部操作都有回执或内容音)。
`stateName()` → menu / ask / decode / confirm / settle / story / choice / night;
`invalidHint(state, g)` → locked:up=inv_up_locked、locked:down=inv_down_locked、
locked 其余=gd_invalid;left/right:menu=null(切项合法)、settle=sys_hint_again_full、
其余=inv_nosel;默认 gd_invalid。
- **floor 状态流**:叙事/提示链 `floor.speak`(critical:sys_first_tap、dv1_teach、
揭晓/收尾链首句 dv1_right / d9_send / d9_send_e / d9_caught_e / d9_sleep / d9_sleepe
——判定出口 800ms 手势死窗的 26 号等价物:抢拍 tap 给 busy 回执不吞揭晓)。
摩尔斯排程 = BUSY(手势 busy 回执,G4 首播保护的等价物):RX 首播 `_rxGuard` 保护窗
(floor BUSY + 作废令牌)、T4 danger 拍前置 2s(step_halt 渐强 + danger 签名/震同窗,
直置 floor.state,见偏差 7)、两段确认电文重播(confirm 等待点在挂但摩尔斯可先起)。
等待点 8 个全配 full/short/silenceMs/expected/onDemo(19 号附录 C §5 表 + ask/menu)。
- **morseGate(19 号 §3.4 R1-R5)**:`_busyVoice() = floor.speaking ||
audio.isVoicePlaying()`(整条链含句间空隙都算语音在播),在播则挂队 150ms 轮询补发;
候选预听链 = 候选摩尔斯(620Hz / u=110 / 字母隙 5u 对照档)→ 静默一拍 ≥500ms →
cand_* 词义句(给耳朵留对照时间);转写账本 `morseLog`(kind/word/preset/逐元素
at/endMs,被掐断时收敛 endMs,零重叠断言口径)。
- **BGM dip(R3 + §6.1)**:引擎 duck 独占 _bgmBus,游戏层在 _bgmBus → _bgmPrefBus
之间插私有增益节点:摩尔斯排程期间 ×0.5(−6dB,排程到 endAt 自动恢复)、MENU 同曲
低响度 ×0.4,复合相乘。
- **confusion**:信号由管道喂(含锁定手势,2026-10 修复后锁定门在 record 之前);
`onConfused → 当前等待点 takeover(lvl)`(引擎 takeover(1/2) 已带 30s 台账去重,
游戏侧零自研路径);双击重听/再说一遍喂 recordReplay;L3 `onDemo` 分档:intro/
continue 替玩家走一步、choice 只复述不替选、decode = 慢放电文 0.5× 一遍(u=160、
字母隙 6u)+ T1 附免费教学页(hint_t1,不扣页数,Prime→Teach 兑现)再交还浏览。
- **novice 阶梯**:T1 译对 = `ladder.onGoal()`(阶段 1),返回的 teachLine **已带
voice/ 前缀**(2026-10 novice.js 修复,直接进 floor.speak,不要再加前缀)挂
dv1_right 揭晓链尾;一局结束 = `ladder.onGameDone()`(阶段 2/3),教学句挂结局链尾,
一局只报一次(_gameDoneCounted);第二夜起 `goalMet = ladder.stage() >= 1` 跨局
记住不重教(做对过的事不教第二遍,原则 11)。
- **存档(§8.1)**:schema 1、summary 人话必带(`第{N}封·{名} · 暴露:{档} · 结局{k}/4`
/ 收台 `已收台·{结局名} · 结局{k}/4`);写入时机 = 每封 RX 进入 / NIGHT 进入 /
每个抉择结算后 / ENDING 进入 / 菜单「保存并回大厅」。断点回来 5s 内
sys_continue_ask → 单击恢复(DECODE 恢复链 prog_t → book 档 → 重挂浏览 + 自动
预听当前候选,30s 台账内不重复;gd_tune2 已停用)/ 右滑新夜班。
- **环境音**:静电底噪 static_bed(RX/DECODE)与值班室 amb_room 为游戏层 buffer
loop(sfx 层无 loop API,持句柄起停;无头测试 ambLoopEnabled=false 防周期定时器
不排水)。
## 2. 台词类别映射(瘦版;全量登记见 19-cipher-voice-ids.md R4 节与 `tools/voice_lines_26/cipher.md`)
- `critical`:sys_first_tap(开场 ≤3s 唯一一次指令)、dv1_teach(改写版「向右滑,
听下一个词。」,19 号附录 C §6)、揭晓/收尾链首句(dv1_right、d9_send、d9_send_e、
d9_caught_e、d9_sleep、d9_sleepe——判定出口死窗等价物,首句 <3s)
- `content`:b_open/b_goal/b_chief(移驻 NIGHT(1) 链首)、n1..n3、rx1..4_in、rev_*、
r*/m*_chief_c/g9_*/p2..p9/r1a..r4b、hint_t1..t4、cf_confirm、sys_turn_teach、
sys_unlock_*(阶梯教学)、sys_menu、sys_menu_resume/book/restart/lobby、
sys_new_night、prog_t1..t4、book_3..0、exp_calm/low/mid/hot、end_c1..c4、
e1..e4 主句 + ep_*(OP/WIND/处长按 19b 原组)
- `hint`:sys_hint_continue_full/short、sys_hint_choice_short、sys_hint_again_full、
gd_tune1 / gd_tune1_short、cf_hint_full/short、sys_only_choice、sys_demo_continue、
sys_demo_choice、sys_continue_ask、sys_skip
- `hint 前缀`:sys_say_again「再说一遍:」(主动重听,全项目一句)
- `feedback`:inv_up_locked、inv_down_locked、inv_nosel、gd_invalid(改写版补
「单击,继续。」)、dv1_wrong(T1 译错即时归因真话,摩尔斯写死句内)
- 音色组:系统/交互层句子全部走 **WIND 北风组**(北风电台=上线联络+系统音,radio
PROD 同型);剧情句沿用原组(OP 报务员 / 处长 NPC 平铺)。
- 账目:新增 20 / 修改 3 / 删除 10,主句 132 → **142**;`_k` 变体 11 → 24 对
(`web/harness/voice_text.json` cipher 主表已同步 142 条)。
## 3. 偏差记录(与 26 号/19 号附录 C 的落地差异及理由)
1. **DECODE 左右滑切候选返回 hold(附 ok 回执)而非设计单的 silent-ok**:管道无
「silent-hold」档——silent-ok 会被视为完成当前等待点(摘掉浏览点,三档提示计数
清零),而切候选没有完成这个点;取 hold 保住等待点,回执由摩尔斯预听的内容音
+ ok 双保险(§4.2「已有明显内容音」的反向适用)。
2. **上滑状态链三句(prog_t → book 档 → exp 档),无「动作」尾句**:19 号附录 C §3
写「第几封。密码本余页。暴露档。左右滑听词。」——落地与 radio 同型(动作由等待
声/等待点提示承担,原则 3「一句话」);DECODE 态要听词时的动作指令由 decode
等待点三档提示(gd_tune1 / gd_tune1_short)就近给。
3. **无 sys_praise「对。」**:19 号附录 C §6 ≈25s「『对。』dv1_right」——首正反馈由
dv1_right 本句(「货船,译对了。」)+ success 震承担,不再单设肯定句(避免
「对」与「货船,译对了」连播两拍;radio 的 sys_praise 是其主播线无归因句所致)。
4. **开场叙事移驻 NIGHT(1) 链首**:§4.10 首夜第一句只剩 critical 指令 + 状态教学,
b_open/b_goal/b_chief 移驻 NIGHT(1) 链首补全叙事、sys_skip 每夜一次挂链尾;
b_rule(规矩念白=重听承诺,随双击教学废止)、gd_brief、dv1_teach2、dv1_ask 停用
(dv1_ask 仍留在 TELS 数据源但 T1 走 dv1_teach 改写版,不可达)。
5. **阶段 3 双击语义分态**:19 号附录 C 只写「重听电文原速;再请求=慢放」——落地:
DECODE = 重听电文(dtCount 连击:第 2 次起 0.5×,其他手势重置);叙事/菜单态 =
等待点 requestReplay(「再说一遍:」+完整句)或无等待点时重播当前节点(NIGHT =
重播夜链、ENDING = 重听结局链);RX 态 invalid(引入链一句在播,等待点不存在)。
6. **翻密码本入口 = 菜单项(阶段 2)+ 迷茫 L3**:长按捷径废止(长按 → invalid +
gd_invalid 解释一次);19 号 §7「页数用尽再翻 = paper_page + 无语音轰炸」照旧;
非破译态选「翻密码本」= gd_invalid(feedback 通道,话轮保住)。
7. **danger 拍前置 2s 直置 `floor.state = 'BUSY'`**:引擎无「演出 BUSY 定时」公开
API(floor 只有 speak/yieldTurn/enterAction),游戏层直置 + seqToken 作废令牌,
2s 后 _speakPayoff 接管(代码注释已标;dungeon 同款直置先例)。**引擎建议**:
floor 补 `busyFor(ms)` 公开演出窗 API。
8. **摩尔斯首播保护窗**:`_rxGuard` + floor BUSY(管道路径 busy 回执)双保险;
会话/直调(transcribe 的 onGesture 直调不走管道)由 _rxGuard 返回 silent-ok
兜底不推进状态(G4 轻震等价物移到回执层)。
9. **intro 等待点 full=null**:指令句刚说过(§4.10 找猫示例同型),静默第一档直接
short(sys_first_tap_short);第二夜起 newNight 只播 sys_new_night 不再重播
sys_first_tap。
10. **menu 等待点 short 复用 gd_tune1_short「左右滑。」**:菜单三档提示省一句资产
(full=sys_menu 报幕句刚播过,30s 台账自动降档;两处「左右滑」语义一致)。
11. **confirm 等待点 onDemo = 替确认(_settleDecode)**:迷茫兜底要能推进(19 号
附录 C §5「替确认」);choice 的 onDemo 不替玩家做有后果的抉择(sys_demo_choice
复述两向后交回);settle 的 onDemo 只给提示音(不替玩家开新夜班)。
12. **m4_chief_a/b 登记未接线**:19b M4 组的处长试探 ①/② 在 26 号改造前(HEAD)即
未进节点表(rev4 → @p8 直连),本轮维持现状不动玩法;台词资产维持登记不删。
13. **乱点机器人白名单口径(§6.2 被动原句重复=0 的「主动查询」判定)**:a)「再说
一遍:」前缀后的完整句;b) 玩家 8s 内主动操作索引的句子(候选词/状态句/
dv1_wrong/dv1_right/cf_confirm/hint_t/rev_ 归因与确认句)。
## 4. 测试记录
- 无头 playtest:`tools/node/bin/node web/cipher/test/playtest.mjs`,**16 段、断言
191 条全绿、退出码 0**(2026-10-02;`game.floor.bedEnabled = false` 关等待声周期
定时器,mock 口径 2s/句)。覆盖:
1. 开场 §4.10 + RX(1):第一句 critical 指令(抢拍 tap=busy)、3s 内动手、
gd_brief/b_rule/gd_tune2 零播出、首候选预听 ≤12s、BAY 编码/时值逐元素
(§2.2 表)、R3 摩尔斯 dip 0.5、首播保护窗 tap/left=busy;
2. DECODE 浏览 / 两段确认 / T1 译对:右滑 ×3 循环候选、确认取消回浏览、揭晓首句
critical、success 震、sys_unlock_up 挂链尾、抉择点「再说一遍:」+ 完整句;
3. T1 译错即时归因 + 无限重选零惩罚(expose 不动、t1 恒收敛);
4. 剧本 A(E1 全对全右 p9 左,guide=full):NIGHT(1) 开场叙事移驻各一次、四封
编码逐元素断言(TOP/HAND/SPY)、danger 拍(step_halt+签名+震同窗)、ep 逐封
点名 flags 单源、end_c1 分档计数、summary、手势数 ≤170(实际 24)、第二夜
不重播 sys_first_tap;
5. 剧本 B(E2)/ 剧本 D(E4,全译错全左 endingPre=8)playNight 驱动;
6. 剧本 C(E3):暴露度逐 NIGHT 验算(0→2→5→8,§5.2 单源表)、跨档播报
exp_low/exp_hot 各一次、warning 震、同档零重复;
7. 手势阶梯:locked 首次解释一次(原则 7)、未解锁双击折算单击、上滑状态链
G6 顺序、sys_help 零播出;
8. 三档提示:decode 10s/25s、continue 13s/30s、choice 25s(台账降档直接 short)
/50s(>30s 给完整句)、两级用尽即停;
9. 迷茫接管:L1 短句 → L2 只放行 expected(L3 白盒 takeover(3):慢放 u=160/
字母隙 6u + T1 免费教学页不扣页数,演示后重挂浏览);
10. 菜单(seed 阶段 3):左右滑循环换项、上滑重听说明+设置提示、下滑关闭回
DECODE(局面复述 + 30s 台账内自动预听不重复)、「翻密码本」= 回 DECODE 提示
流程(T1 免费页 + paper_page)、「重新开始今晚」= 新夜班、「保存并回大厅」=
leaving + 存档;MENU 同曲低响度 dip 复合断言;
11. 阶段 3 双击:重听电文原速 → 再请求慢放 0.5×(字母隙 960ms 时值断言)、
切候选重置连击;
12. 断点复述:预置存档 phase=DECODE(tel=3/hints=1/cand=1)→ sys_continue_ask →
单击恢复(数值全还原、恢复链 prog_t3 → book_1、gd_tune2 零播出、自动预听
SAND);右滑 = 新夜班标量重置;
13. §6.2 乱点机器人(120 步、seed 42):无声操作 0/114、第一次操作 2120ms ≤10s、
第一次成功 2120ms ≤20s、打断失败 0、被动原句重复 0(白名单口径见偏差 13);
14. morseGate:全剧本摩尔斯起振区间与 voice 零重叠、候选词义句全部 ≥500ms 接播;
15. 资产缺失:屏蔽 rev_top 仅 console.error/warn,Floor 单句哨兵续链不卡死仍到 e1、
gd_tune2/gd_prog1 零播出;
16. 键盘变体(input=key:gd_tune1 走 key/ 目录)与语速边界(speed=2.2 首播 u 仍
80ms)。
- 玩家感知转写:`web/harness/sessions/cipher.mjs` 已同步 26 号口径(开场第一下 tap、
主动重听、上滑状态链、走神听三档、结局后下滑逛菜单、`floor.state` 链级门控,
radio 范式),`tools/node/bin/node web/harness/transcribe.mjs cipher`(含
`--session sessionStress`)两剧本零死锁跑通(主线虚拟 174s 到 E1 并开第二夜 /
乱按 416s:解码 12 步、5 抉择、乱试 66 次、逛菜单 11 次仍到结局)。
`web/harness/voice_text.json` cipher 主表同步 142 条。
- 待生成资产:新增 20 + 重录 3(`tools/voice_lines_26/cipher.md`,主会话统一生成;
playtest 对缺失 voice 资产按可用 mock,与 TTS 生成解耦)。
- 真人冷启动:待测(26 §6.3,3~5 人)。