# 26-apply · 《深海回音》重构施工单(26 号文档落地) > **设计单正文见 [17-abyss.md](17-abyss.md) 附录 C**(状态×手势矩阵、等待点配置、 > 首 3 分钟逐秒脚本、失败表、氧气经济与四结局判定——不在本文重复)。本文件是瘦版 > 施工单:只写实现接线、偏差记录、台词类别映射、测试记录。台词登记见 > [17-abyss-voice-ids.md](17-abyss-voice-ids.md) R3 增量节;清单见 > `tools/voice_lines_26/abyss.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)(双音色组同型 + HOST/PROD 系统句归类先例)。 ## 1. 一句话核心动作 **听一段深海救援:单击继续或发声呐脉冲;听到选项时左右滑做决定。** ## 2. 实现接线(web/abyss/game.js + index.html) 六模块(§4.1/§4.2/§4.3/§4.4/§4.5/§4.12)全部接通,玩法核心(节点链 + seqToken + pendingChoice 骨架、氧气步进经济、9 抉择、4 结局 + 尾声回响、DIVER/DECK 双音色 + NPC 平铺、存档 summary)不动: - **接线**:`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()` (构造时按 `ladder.stage() >= 3` 开旁白加速)。`index.html` 照 storyline 同构: `makeInputPipeline({ floor, confusion, receipts, invalidHints, game, ladder, opts: game.PIPELINE_OPTS })` + `attachGestures(root, pipe, { doubleTapWindow: 0 })`, 启动遮罩保留。 - **PIPELINE_OPTS = `{ longpressCore: true }`**:长按由游戏层裁定——新手期 invalid (回执 + gd_invalid 首次解释),阶段 3 起 = 引擎旁白加速(`hold`,等待点保留)。 doubleTapWindow: 0 → 未解锁双击由管道折算成 tap(搜索等待=再发一枚脉冲)。 - **`onGesture` 返回值收口**(§4.2):`'ok'`(继续/抉择/菜单/状态)|`'hold'` (重听、菜单换项——等待点保留)|`'silent-ok'`(**搜索等待单击=发脉冲**:声呐脉冲 即内容音,省 ok 回执)|`'invalid'`(pipeline 给回执 + explainOnce + 迷茫信号)。 - **打断快照 `_chainSpeaking`**(本轮新增,见偏差 5):pipeline 的 `floor.handleInput(滑动/长按)` 按 §4.1「打断并执行」先杀链把 floor 置 BUSY,游戏侧 `floor.speaking` 已为 false——`_chainSpeaking` 在 speak 置位、onDone/interrupt/ `_yieldTurn` 清零,让被掐断的旁白链按矩阵落「旁白中」分支(滑动命中抉择点 → 缓存 pendingChoice + 跳选项句;未命中/长按 → `_recoverInterrupted()` 交回 continue 等待点,零无声死锁)。 - **pendingChoice(V1 播完才生效)**:旁白链中滑动只缓存方向并跳去 p* 选项句 (`_swipePromptTarget`:本节点是抉择节点或 next 直入抉择节点);p* 整句 critical (tap=busy 不可跳),`_armChoice` 在选项句播完的当刻让缓存生效:ui_confirm 结算音 → 结果句,零插播(W2 契约 + 盲玩公平)。 - **critical 清单**(§8 台词类别 / 结算死窗等价实现):sys_first_tap(开场第一句)、 dv1_teach(下潜链尾教学句)、p1~p9 选项句(整句)、lockFirst 节点首句(g9_in / c_none / dry_a / dry_b——结算链首句死窗)、结局链首句、恢复链首句(F4: `_resumeGuard` 播完即清)。**替代旧 800ms 手势死窗**:覆盖整句而非定时窗。 - **sonarGate(§5 声呐与语音互斥)保留**:信息类声呐音(echo_*/pipe_knock)只在 voice idle 时播出,否则挂队、链歇补发(`_sonar`/`_drainSonar`);玩家脉冲只在 搜索等待(voice 恒静)受理,按构造不重叠。 - **声呐步 = BUSY(`_runSteps` + `_seqBusy`)**:pre/post 信息音、危机前置(spark_flash/ landslide 前置 2s + danger 震循环 ≥1000ms 间隔)期间手势统一 busy 回执——旧 「tap 快进回波余音」废除。 - **存档(§4.12/§7)**:DIVE 进入、SURFACE 进入、每个抉择结算后、ENDING 进入、 stop() 退出、菜单「保存并回大厅」前(leaving 标记);MENU 态落盘记回原状态;ASK 态不落盘(stash 原存档才是真进度)。runs 口径 = `newRun()` 时 +1(冷启动 0 起; ASK 右滑与结算单击同口径)。 - **手势阶梯教学句挂链尾**:第一次脉冲成功(`_firePing` → `ladder.onGoal()`)→ `sys_unlock_up` 挂本条回波链尾;一局结局(`enterEnding` → `onGameDone()`)→ `sys_unlock_down` 挂结局链尾。`teachLine()` 已带 `voice/` 前缀(novice.js 修复后 语义),游戏侧**直接引用不再拼前缀**(双前缀是曾出现过的坑,静态审计拦截 `` `voice/${` `` 模板拼接)。 - **上滑「我在哪」**(§3/§9 顺序:潜次 → 已救人数 → 氧档 → 一个动作):`interrupt` 摘 等待点(状态链期间静默 timer 清零,见偏差 6)→ 状态句全量 `markHintSaid` → 播完 **按原等待点类型重建**(搜索重挂/抉择重挂/结算/continue——radio 口径不重播节点)。 crit 档附 warning 震按档进入去重;`oxy_crit_deck` 水面变体(R2-F5)保留。 ## 3. 偏差记录(与 26 号手册 / 17 号附录 C 的实现偏差) 1. **搜索等待声(17 号 §2)**:引擎 Bed 为全局统一音色,游戏层在 ping 等待点置 `floor.bedEnabled = false`——等待声由 amb_deep 常驻 + 回波余音的深海声场承担 (`_yieldTurn` 按等待点 id 翻转)。测试侧统一压 false(mock 防周期定时器不排水)。 2. **critical 粒度**:floor 的 critical 是整链一刀切,句级保护靠**拆链**实现 (`_speakNodeVoices` 按 critical 分段顺序播)。非 auto 叙事节点的尾句**不做**首播 critical——链级一刀切会把「滑动打断并执行 / 上滑我在哪」一并挡成 busy(§4.1 宪法);叙事尾句跳过的代价只是提前进等待点,由三档提示兜底复述。 3. **auto 过场链尾恒 critical**(`_tailLocked`):dv1_teach(教学句)、s_tier 档位句、 p9 结果链尾、取消链尾——「next 是 @x 过场」时链尾即过场门,不可跳。 4. **搜索等待单击恒出脉冲(silent-ok)**:包括等待声中的抢拍单击——比「轮到你提示音 后才受理」更宽(17 号 §4:再单击=再听);四潜各恰一枚玩家脉冲由 playtest 断言。 5. **打断快照为游戏层补丁**:引擎 pipeline/floor 未提供「这次手势打断了一条链」的 回执,`_chainSpeaking` 由游戏包装 floor.speak/interrupt 维护(上游修复后可撤)。 无效滑动打断旁白 → invalid 回执 + 解释句 + **交回 continue 等待点**(不重播原句, 原则 9;链末单击继续到下一节点)。 6. **引擎缺口(上报,未改引擎)**:`engine/hints.js` 的 `use()` 在 `ledgerSay(id)` 之后 才 `floor.speak`(kind hint)——静默提示到点时恰逢 SPEAKING(如上滑状态链播放中) 句子被丢弃**但台账已记**,之后 30s 内同句被动提示会错降档。游戏侧规避:上滑先 `interrupt`(清 timer,状态链期间零 fire)。建议引擎改为「speak 返回 false 时不登 台账」。同类:`InvalidHints.explainOnce` 的 ledgerSay 也在 speak 前。 7. **结算重开链**:`sys_new_dive → b_open → b_goal → b_go`(重开不念开场教学,原则 11——sys_first_tap/sys_skip 每实例一次);开场链首句 critical 指令、b_goal 极短 目标句、b_rule/gd_brief 规矩念白废除(§4.10)。 8. **状态链动作句**:search=「点一下,发脉冲。」(sys_act_ping 兼首次解释句); choice=「正在抉择。左右滑。」(status_choice);settle=「单击,开始新救援。」 (sys_hint_settle_full);continue=「单击,继续。」——全部 DECK 组、进台账。 ## 4. 台词类别映射(类别标注;增量清单见 `tools/voice_lines_26/abyss.md`) - `critical`:sys_first_tap(开场 ≤3s 唯一指令);dv1_teach(下潜链尾教学);p1~p9 选项句(整句,含 p7_chen/p7_zheng);g9_in / c_none / dry_a / dry_b(结算链首句 死窗);结局链首句(e1~e4_a);恢复链首句(F4)。 - `content`:b_*、各潜对白(dv*/sv*/z1/bai/chen/gao/d*_e 组)、结局与尾声 (e*_b、ep_*)、prog_d1..4、prog_saved1..5、oxy_full/ok/low/crit/crit_deck/gain、 sys_turn_teach、sys_unlock_*、sys_menu、menu_help、sys_menu_*、sys_new_dive、 sys_continue_ask、sys_skip。 - `hint`:sys_hint_ping_full/short、sys_hint_continue_full/short、 sys_hint_choice_short、sys_hint_settle_full、sys_only_choice、sys_demo_continue、 sys_demo_choice、status_choice、sys_say_again(「再说一遍:」前缀,engine/hints.js 播出)。 - `feedback`:sys_act_ping(搜索等待无效滑动首次解释)、inv_nosel、inv_up_locked、 inv_down_locked、gd_invalid(通用格,保留旧句)、sys_menu_restart_confirm(重开 二次确认)。 - 音色组:系统/交互层全部 `_ABY_DECK`(母船调度=系统音,与 radio PROD 同型); 剧情潜水员 `_ABY_DIVER`、幸存者 ABYSS_NPC 平铺(不动)。通用系统句与 STORYLINE_LINES 的 sys_* 段逐字一致;键盘变体照 KEY_LINES["storyline"]。 - 完整登记(新增 23 / 修改 1 / 删除 10)见 `tools/voice_lines_26/abyss.md` 与 [17-abyss-voice-ids.md](17-abyss-voice-ids.md) R3 增量节。 ## 5. 测试记录 - 无头 playtest:`web/abyss/test/playtest.mjs` **243 断言全绿**(`tools/node/bin/node web/abyss/test/playtest.mjs` 退出码 0;`node --check web/abyss/game.js` 通过)。 覆盖:§4.10 首 3 分钟(3s 内动手、critical 指令句、规则念白零播出);剧本 A~F 六路径四结局 + 氧气逐检查点验算 + 氧气门三形态 + 干涸链 + 尾声回响差分; pendingChoice V1 时序(缓存 → 选项句播完 → ui_confirm → 结果句);sonarGate 零重叠 + 四潜各一脉冲;结算死窗(取消链/结局链首句 critical,幽灵手势不截链); 等待点三档(ping 8/18、continue 13/30、choice 25 台账降档);迷茫接管三级 (L1 短句 → L2 缩小 → L3 替发脉冲);手势阶梯(阶段 1/2 教学句挂链尾); §4.12 断点复述(ASK 左/右、F4 恢复首句锁、上滑状态链);菜单(左右滑换项 + 单击确认 + 二次确认重开 + 回大厅);资产缺失续链;键盘变体路由;**§6.2 乱点 机器人**:无声操作 0(0/309)、被动原句重复 0、打断失败 0、第一次操作 ≤10s (2120ms)、第一次成功 ≤20s(2120ms)、乱按不锁死(零死锁收在合法等待态)、 关键路径手势预算 9 ≤70;静态审计(无运行时 id 拼接、11 句废止台词零引用、 19 条游戏引用新台词待生成豁免 + 引擎侧 4 条入清单)。 - 玩家感知转写:`web/harness/sessions/abyss.mjs` 已同步 26 号口径(菜单左右滑换项 + 单击确认、结局结算后下滑逛菜单——阶段 2 解锁时机、SURFACE 签收链中段链末 单击继续、COMMS 走神触发 13s 首档提示),`tools/node/bin/node web/harness/transcribe.mjs abyss`(含 `--session sessionStress`)两剧本零死锁 跑通(主线虚拟 279s 到 E1 海燕归钟;乱按剧本同达 E1)。 - 新语音资产(23 句 DECK 组 + 键盘变体)由主会话统一生成;playtest mock 与真实 资产解耦(缺失按可用处理,Floor 看门狗兜底)。 - 真人冷启动:待测(26 §6.3,3~5 人)。