Files
SoundGame/docs/design/26-apply-cipher.md
T
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

15 KiB
Raw Blame History

26-apply · 《暗码》重构施工单(26 号文档落地)

设计单正文见 19-cipher.md 附录 C(一句话核心动作 / 状态 / 手势表与解锁 / 状态×手势矩阵 / 等待点 / 首 3 分钟脚本 / 失败表 / 台词账目 / 断点复述),本施工单不重复,只写:实现接线(§1)、台词类别映射(§2)、 偏差记录(§3)、测试记录(§4)。交互规则权威来源: 26-audio-interaction-playbook.md、 00-global-ux.md。台词登记 19-cipher-voice-ids.md R4 节;清单 tools/voice_lines_26/cipher.md。 范式对齐 26-apply-storyline.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 人)。