Files
SoundGame/docs/design/17-abyss.md
T
xsl c8843233b0 research(new8/batch2): abyss + detective 迭代闭环收口——双双 R1-R2 连续达标裁定可交付
abyss《深海回音》:
- R1 补 synthesis(5/5 愿意玩 P0=0)+ 补录 R1 缺失裁决材料
- R2 十修全部现场坐实:F1 ping迟到/尾句保护首播制/链末看门狗不拆/回波静默一拍/dv4_dual扣账口播/crit水面变体/看门狗13s/震循环1.1s/震去重/状态链人数句
- 补录语音18段(flock串行,QC无离群);playtest 242→278
- 终裁:4/4 人设满分档,连续 2 轮达标 → 可交付 ✅

detective《雨夜专案》:
- R1 取证轮:补跑 live 两会话(认真新手33分钟全弧线/乱按200s三级保底抢救),5/5 愿意玩 P0=0
- R2 十一修全部坐实:intro完整落声/tut不被掐/按轴取句/人名指认/结案确认闸/指路句/选句记忆位/hint轻震/st_wrong当场记账/tut_skip补动词/L2疑点齐分支
- 补录+改词33条(29新增+4重录);playtest 265→279
- 终裁:6/6 愿意玩,连续 2 轮达标 → 可交付 ✅

公共:00-program 批次2进度账收账、13号文档 R2 断言关键词、gen_voice_dual 两游戏段、voice_text.json
验证:abyss 278/278 + detective 279/279 + guide 122/122 + check_all_assets 全项目全绿
2026-09-14 00:05:42 +08:00

520 lines
51 KiB
Markdown
Raw 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.
# 17 · 《深海回音》(abyss) — 深海救援叙事 RPG 设计文档
> **给开发的实现要点摘要**
> 1. 叙事模式(声命线范式,radio 同骨架):节点链 onComplete 串行 + seqToken 打断作废 + pendingChoice 缓存;复制 `web/radio/` 结构起步,状态机 6 宏状态(INTRO/DIVE/COMMS/SURFACE/ENDING + MENU 覆盖层)。
> 2. 手势零豁免:全部走全局默认映射(单击=继续/**检查(搜索等待=发声呐脉冲,宪法 §5.1「检查」义,escape 先例)**/跳句、双击=重听、左右滑=抉择、上滑=状态、下滑=模态菜单、长按=引擎快进)。
> 3. 台词依据 [17-abyss-voice-ids.md](17-abyss-voice-ids.md)(148 句:双性别 127 + 平铺 NPC 21);NPC 水声电台滤波在**资产侧** ffmpeg 带通(radio 先例),引擎零改动。
> 4. 氧气=步进资源非实时倒计时:只在潜次边界与阈值跨越时播报(分档整句、账目真实);软失败=「被迫取消第四潜/单载」而非玩家死亡,结算链完整播出(含 800ms 手势死窗)。
> 5. 声呐双通道:回波类信息音与语音**绝不重叠**(游戏层 sonarGate 空闲门,R1 规则 §6.2);并发声流 ≤2。
> 6. 章程任务 N2(research/new8/00-program.md §2):单局 15-25 分钟、9 抉择点(实走 7-9)、4 结局、分支局部化。
> 7. 断言清单住本文档 §9(radio §20 先例);13 号文档已登记指针(§21)。
## 1. 概述
- **一句话**:你是深潜救援员,声呐里是被困海沟的潜艇「海燕号」——氧气在倒数,每次下潜都是抉择:先救谁、花多少气、留不留、上不上去。
- **题材(声音即世界)**:黑暗深海里视觉归零,**声呐回波=空间、电台人声=生命、氧气=代价**——「用耳朵救人」与纯音频载体天然同构。声呐脉冲/回波/管路敲击/船体呻吟是叙事本体,不是装饰。
- **单局**:一次完整救援(一条抉择路径)20-22 分钟;四次下潜共享主干,分歧局部化在每潜内部;9 个抉择点(单局实走 7-9 个),4 结局。
- **核心循环**:下潜(声呐搜索拍:单击发脉冲→回波→调度解读)→ 通讯(船员对话+抉择)→ 带人上浮 → 水面结算(氧气扣账+档位播报)→ 下一潜。跨潜积累氧气存量与船员生死/好感,最后一潜的氧气余量决定救援形态(双载/单载/取消),收队前 p9 决定你自己的心结。
- **与 radio 的差异点**(骨架同、压力源不同):radio 零压力(口碑只涨不跌);abyss 引入**氧气稀缺**——但全部宽限化:无实时倒计时、无中途死亡、软失败只损失「机会」不损失「已有」、最优省气路径恰好全员生还(P4 友好默认)。
## 2. 状态机
### 2.1 状态列表
| 状态 | 内容 | BGM | guide.state |
|---|---|---|---|
| `INTRO` | 开场链:甲板口播 + 一句规矩 + 今晚目标 | `bgm/deck` | explore |
| `DIVE` | 下潜过程:下潜链(auto)→ 搜索等待(单击=发脉冲,声呐主导)→ 回波+接通 | `bgm/dive` | explore;搜索等待由 turn cue 承担 |
| `COMMS` | 通讯叙事:船员对白链 + 抉择点 + 结果 | `bgm/comms` | explore;抉择点=choice |
| `SURFACE` | 上浮结算:出水确认 + 氧气扣账/档位 + 补给(D2 后),自动链 | `bgm/deck` | settle |
| `ENDING` | 结局链 + 尾声回响(epilogue),结算态 | `bgm/ending` | settle |
| `MENU` | 模态覆盖层(任意叙事态下滑呼出,暂停一切链) | `bgm/deck`(低响度淡入) | menu |
### 2.2 转移图(文本)
```
┌──────────── 结算单击=新救援 ─────────────┐
▼ │
INTRO ──链完单击──> DIVE(1) ──脉冲找到──> COMMS(1) ──链完──> SURFACE(1) ─┤→ DIVE(2)
│下潜链 auto;搜索等待=单击脉冲 │ …(D2/D3 同型)…
└─────────────────────────────────────────┘
SURFACE(3) ──氧气门──┬─ oxy≥4: DIVE(4 双载) ──链完──> SURFACE(4) ──链完──> 签收 ◆p9
├─ oxy=3: DIVE(4 单载: p7_chen|p7_zheng) ──链完──> SURFACE(4) ─┘
└─ oxy≤2: 终局取消链(dv4_none→gao_final) ─────────────────────┘
▼
d9 结果链 ──> ENDING(e1..e4 + ep 回响链)
任意叙事态(INTRO/DIVE/COMMS/SURFACE/ENDING):下滑 → MENU(模态)
MENU:单击=循环切换选项 → 双击=确认执行 → 上滑=帮助 → 下滑/「回到水下」项=关闭回原状态(重播当前节点)
```
- INTRO/DIVE/COMMS/SURFACE/ENDING 均为节点链驱动(radio playNode 模式);下潜链与上浮结算链 `auto:true`,COMMS 叙事节点链末等待单击(radio 实现核对 #1 同口径)。
- SURFACE 自动链 ≤15s:扣账句(d*_surf)→ 补给句(仅 D2 后)→ 档位句(跨档才播)→ 直接进下一 DIVE;第四潜位置由**氧气门**分三形态(§4.3)。
- 终局取消链与结局链入口处盖 `_settleGraceUntil = now+800ms` 手势死窗(dungeon H1 热修同款),窗内手势一律吞掉,结算链完整播出。
- MENU 打开时 `guide.disarm()` + 取消一切看门狗/自动链;关闭后重播当前节点(局面复述,radio closeMenu 先例)。
### 2.3 每状态手势映射(6+2 全覆盖,零豁免)
| 手势 | INTRO | DIVE(下潜/搜索) | COMMS | SURFACE | ENDING | MENU |
|---|---|---|---|---|---|---|
| 上滑 | 帮助+状态,随后重播当前段 | 帮助+状态(潜次+氧档),留在搜索等待 | 帮助+状态,随后重播当前节点 | 帮助+状态,自动链继续 | 帮助+状态(结局名+氧档) | 帮助(sys_help) |
| 下滑 | 模态菜单 | 模态菜单 | 模态菜单 | 模态菜单 | 模态菜单 | 关闭菜单 |
| 左滑 | 无效 → error 震+gd_invalid | 无效 → gd_invalid(喂无进展轨) | 抉择=左 | 无效 → gd_invalid | 无效 → gd_invalid | 无效 → error 震 |
| 右滑 | 无效 → gd_invalid | 无效 → gd_invalid(喂无进展轨) | 抉择=右;ASK 态=新救援 | 无效 → gd_invalid | 无效 → gd_invalid | 无效 → error 震 |
| 单击 | 播放中=跳过本句;链末=开始下潜 | 播放中=跳过本句;**搜索等待=发一枚声呐脉冲** | 播放中=跳过本句(p* 不可跳);链末=继续 | 跳过本句(自动链) | **再来一局**(新救援) | 循环切换选项 |
| 双击 | 重听当前段 | **重听上一条回波+其解读句**(尚无回波=无效,首潜前零静默) | 重听当前节点台词(含抉择提示) | 重听结算链 | 重听结局链 | **确认执行**(菜单约定覆盖重听) |
| 长按 | 引擎接管:旁白临时 1.7×(12 号 §2.2,叙事类不豁免) | 同左 | 同左 | 同左 | 同左 | 同左(快进菜单语音) |
| 长按松开 | 引擎接管:恢复 settings.speed | 同左 | 同左 | 同左 | 同左 | 同左 |
- 豁免登记:**无**。六个基础手势全部符合全局宪法默认语义——单击在搜索等待=「检查」(宪法 §5.1 原文即含检查,escape 检查房间同款);菜单走 §5.2 R3 模态约定(单击循环+双击确认)。
- ASK(存档询问态,radio 同款):单击=继续上次 / 右滑=新救援;上滑/下滑/左滑/双击=无效(error 震+gd_invalid)。
- 抉择 prompt(voice/p1..p9)播放中单击不可跳(PROMPT_RE 门)、左右滑缓存 `pendingChoice` 播完即生效(radio W2 契约照抄)。
- 继承 radio G5 跳过公平契约原文:**首播必完整(p* 首播不可跳 / 链末关键句保护 / ASK 恢复首句锁)、复播可即掐(锁不随复播重新武装、链中段句可跳、复播滑动照常预缓存)**;p* 复播(双击重听/看门狗复述)仍不可跳、句尾生效。
- 无效手势零静默:所有「无效」格均有 error 震 + gd_invalid(onceKey 同局限次,F1.4 idle-only:语音在播时跳过语音只发震)。
- 键盘通道:三通道并行,游戏代码零改动;操作提示句按 `inputMode()` 选 `_k` 变体资产(§7.3、voice-ids 集中分组)。
## 3. 抉择树
### 3.1 全局骨架(四潜共享主干,分歧局部化)
```
INTRO: b_open → b_rule → b_goal → b_go ──> 下潜①
【D1 找船·教学潜】救小舟 · 2 抉择(p1/p2)──> SURFACE(1) ──> 下潜②
【D2 机舱·老白】危机拍#1 · 2 抉择(p3/p4)──> SURFACE(2·补给+3) ──> 下潜③
【D3 病舱·生死抉择】回波预警 · 2 抉择(p5/p6)──> SURFACE(3) ──氧气门──> 下潜④ 或 终局取消链
【D4 指挥舱·高潮】滑坡危机 · p7(双形态)+ p8 ──> SURFACE(4) ──> 签收 ◆p9(心结)──> ENDING
```
分支纪律:**每潜内部分支、上浮时收敛**到结算句 → SURFACE;跨潜只传标量(oxy、saved{}、stayer、5 个 flag),不生成组合节点。最坏路径与最好路径节点数同型(剧本 A 与剧本 C 深度相同)。
### 3.2 分潜结构(◆=抉择点,〔〕=条件差分)
```
【D1 找船·小舟】(教学潜)
下潜链 dv1_down → dv1_teach →【搜索等待:单击脉冲】→ echo_metal_l + dv1_metal(教「金属回波=钢板」)
→ dv1_link → COMMS: z1_a → z1_b → z1_knock(垫 pipe_knock)→ ◆p1
├左 听暗号: d1_listen → pipe_knock → d1_code(knowsPipe=true,-1 气)─┐
└右 直接上: d1_up → z1_bye ─────────────────────────────────────────┤
▼
小舟拉住你 → ◆p2
├左 等日志: d1_log(hasLog=true,-1 气)─┐
└右 不等: d1_nolog ────────────────────┴→ 上浮 → d1_surf(扣 3 格)→ 档位 → SURFACE(1)
【D2 机舱·老白】
dv2_down →【搜索:脉冲 → echo_debris+pipe_knock → dv2_found】→ bai_a → bai_b → ◆p3
├左 泄压慢撬: d2_pry →〔knowsPipe ? d2_pry_ok(-0): d2_pry_slow(-1)〕─┐
└右 切割快切: d2_cut →【危机拍: danger 签名+spark_flash 前置 2s】→ d2_flash ─┤
▼
bai_out → ◆p4
├左 陪关阀门: d2_valve → valve_close → d2_valve_e(valve=true,-2)→ bai_thanks ─┐
└右 直接上浮: d2_leave ──────────────────────────────────────────────────────────┴→ d2_surf
→ d2_supply + oxy_gain(应急瓶+3,全局唯一一次)→ 档位 → SURFACE(2)
【D3 病舱·陈医官/小郑(小郑无台词,吊床固定)】
dv3_down →【搜索:脉冲 → echo_groan → dv3_groan(船体呻吟预警)】→ chen_a → chen_b → ◆p5
├左 先带陈医官: d3_chen → chen_up → d3_chen_e(小郑留守)─┐
└右 先带小郑: d3_zheng → d3_zheng_e → chen_stay(stayer=chen)┤
▼
◆p6
├左 留备用瓶: d3_spare → d3_spare_e(spareGift=true,-2,floor 至 0 触发干涸链)─┐
└右 约定来接: d3_promise ────────────────────────────────────────────────────────┴→ d3_surf → 档位 → SURFACE(3)
【SURFACE(3) 氧气门】oxy≥4 → D4 双载 | oxy=3 → D4 单载 | oxy≤2 → 终局取消链(dv4_none → gao_final,800ms 死窗)→ ◆p9
【D4 指挥舱·高船长】
dv4_down →【搜索:脉冲 → echo_groan 渐强 → 危机拍: danger+landslide 前置 2s → dv4_slide】→ gao_a → gao_b
双载形态 → ◆p7(快慢浮)
├左 慢浮: d4_slow → d4_slow_e(-2)─┐
└右 快浮: d4_fast → d4_fast_e ──────┤
单载形态 → ◆p7_chen|p7_zheng(按 stayer 选提示,谁上)
├左 带船长: d4_pick_gao →〔stayer=chen ? chen_last : d4_stay_lost〕─┐
└右 带留守者: d4_pick_chen|d4_pick_zheng → gao_last(p8 跳过)─────┴→ 直达 d4_surf
▼(船长在乘时)
〔hasLog ? gao_confess : gao_c〕→ ◆p8
├左 说出来: d4_talk → gao_peace(gaoPeace=true)─┐
└右 不问: d4_silent ────────────────────────────┴→ d4_surf → 档位 → SURFACE(4)
【签收】g9_in → g9_knot → ◆p9
├左 取船钟(需 oxy≥1): d9_bell → bell_toll → d9_bello(bell=true)─┐
└右 进医疗舱: d9_rest ────────────────────────────────────────────────┤
〔oxy<1 时左路封锁: g9_dry 播出后自动走右,不摆假选项〕 ▼
ENDING: e*_a → e*_b(按 savedCount × p9 四选一)→ ep_lead → ep_*(按好感/存活/flag 选播)
```
### 3.3 分支局部化说明
- 跨潜状态全部是标量:`oxy`(int)、`saved`(5 个 0/1)、`stayer`(null/chen/zheng)、flag 5 个(knowsPipe/hasLog/valve/spareGift/gaoPeace)+ p9 派生 bell。没有任何「潜 i 分支 × 潜 j 分支」组合节点。
- 条件差分全部是**节点内选播**(radio w1_say/w1_say2 先例):d2_pry_ok/slow(knowsPipe)、gao_c/gao_confess(hasLog)、p7 双/单形态(氧气门)、p7_chen/p7_zheng 与留别句(stayer)、ep_* 选播。树形不变。
- 树规模:148 句台词、约 70 个节点;任一路径实走 7-9 个抉择点(D4 取消路径少 p7/p8;单载右路少 p8)。
### 3.4 四结局达成条件
| 结局 | 条件 | 主题 | 开环钩子 |
|---|---|---|---|
| e1 海燕归钟 | saved=5 且 p9=左 | 全员生还+船钟出水,告别完整 | 「明天,想再潜一次」 |
| e2 满载的黎明 | saved=5 且 p9=右 | 人都救回来了,钟留在沟底 | 「半夜总听见钟在响」 |
| e3 最后一潜 | saved<5 且 p9=左 | 没能都救回,但下去敲响了钟 | 「钟声替他们响了一声」 |
| e4 按规程上浮 | saved<5 且 p9=右 | 规程之内,难过之外 | 「安静得像三年前那天」 |
- 尾声回响(P2 回响感):epilogue 逐人点名——小舟/老白/陈医官/小郑/船长各按「好感/生死」双线播(voice-ids EP 组),沟底一句按 bell 二选一;玩家每一潜的救与错过都被点名(radio ep_* 同款)。
## 4. 数值设计
### 4.1 变量
| 变量 | 类型 | 初值 | 说明 |
|---|---|---|---|
| `oxy`(氧气格) | int | 12 | 步进资源:只在事件点扣(下潜/抉择),无实时倒计时 |
| `saved` | map | zhou:0 其余 0 | 小舟/老白/陈医官/小郑/高船长 各 0/1 |
| `stayer` | str | null | p5 后留守病舱者('chen'/'zheng') |
| `knowsPipe/hasLog/valve/spareGift/gaoPeace/bell` | bool | false | p1 左/p2 左/p4 左/p6 左/p8 左/p9 左 置位 |
| `supply` | bool | false | D2 上浮后应急瓶已发(全局唯一) |
| `endings{}` / `runs` | map/int | — | 结局收集与重玩计数(存档展示用) |
### 4.2 抉择计分表(左=走心·多花气 / 右=克制·省气)
| 抉择 | 左(走心) | 右(克制) | 备注 |
|---|---|---|---|
| p1 听暗号/直接上 | -1 气,knowsPipe=true | 0 | 左路获 D2 情报(下潜 EFF 的唯一免气左路) |
| p2 等日志/不等 | -1 气,hasLog=true | 0 | hasLog 兑现 D4 船长坦白差分 |
| p3 慢撬/快切 | knowsPipe?0:-1 气 | 0 | 右路挂危机拍#1(火花虚惊,两路都活着) |
| p4 关阀门/直接走 | -2 气,valve=true | 0 | 左路兑现在回响(干净海) |
| p5 先带医官/先带小郑 | 0(chen 上,stayer=zheng) | 0(zheng 上,stayer=chen) | 唯一无气差的双义抉择;stayer 决定 D4 单载差分 |
| p6 留备用瓶/约定来接 | -2 气,spareGift=true(可触发干涸链) | 0 | 左路兑现在留守者存活回响 |
| p7 慢浮/快浮(双载) | -2 气 | 0 | 右路兑现在 e1/e2 回响(医疗舱灯) |
| p7 带船长/带留守者(单载) | 0 | 0 | 另一人随海燕号滑入沟底(措辞克制) |
| p8 说出来/不问 | 0,gaoPeace=true | 0 | hasLog 时 gao_confess 差分前置 |
| p9 取船钟/进医疗舱 | 需 oxy≥1,bell=true | 0 | oxy<1 时左路封锁(g9_dry,不摆假选项) |
### 4.3 氧气经济(单位/消耗/宽限/软失败)
| 项 | 规则 |
|---|---|
| 单位 | 「格」(内部 int)。初始 12(叙事=双瓶满气,够四次往返);D2 上浮后应急瓶 +3(`oxy_gain` 播报,全局唯一),总预算 15 |
| 固定消耗 | 每次下潜开始扣 3(往返);D4 双载额外 +1(慢浮+减压成本按 p7 计) |
| 抉择消耗 | 见 §4.2;**mid-dive 扣至负数一律 floor 到 0**,不中断当前救援(人已挂上吊索,照常带上) |
| 阈值播报 | 分档整句(下表),**只在跨档时播**(挂上浮结算链尾);上滑 status 随时可查;oxy_crit 各播报位附 warning 震(radio G2 同款) |
| 第四潜氧气门 | SURFACE(3) 结算时:oxy≥4 → 双载(带船长+留守者);oxy=3 → 单载(p7 谁上);oxy≤2 → **终局取消链**(第四潜取消,船长与留守者随海燕号滑入沟底——措辞克制、零描写) |
| 宽限设计 | ① 学习期零惩罚:D1 两个左路合计 -2,恰好被 D2 后应急瓶 +3 兜底,教学潜永不亏局;② 到点不是死:软失败只损失「机会」(少带一人/少下一潜),已救上的人**永不失去**;③ 无实时倒计时、无 mid-dive 打断(P4 无高压);④ p9 左路是「氧气的礼物」——省下的气兑换终局抉择 |
| 干涸链(软失败结算) | mid-dive floor 至 0 触发:dry_a →(当前救援照常完成)→ 上浮结算内 dry_b(归因真话:指向实际花掉最后两格的那个左选择)。链入口 800ms 手势死窗 |
**氧气档位表(账目真实:每格取值下句义皆为真)**
| 档 | oxy 区间 | 整句 | 真实性核验 |
|---|---|---|---|
| full | ≥9(floor(oxy/3)≥3) | 「气瓶还足,救得动。」 | ≥3 潜气量 ✓ |
| ok | 6-8(floor=2) | 「氧气还够两潜。」 | 恰可下 2 潜 ✓ |
| low | 3-5(floor=1) | 「氧气告急,只够最后一潜。」 | 恰可下 1 潜 ✓ |
| crit | 0-2(floor=0) | 「氧气见底,必须上浮。」 | mid-dive=必须上浮 ✓ / 水面=一潜都下不去 ✓ |
- 档位以 `floor(oxy/3)`(可下潜次数)划界而非绝对半数——**句子说的就是判定用的**(radio rep_low 教训内化:措辞与数值对得上)。
- 低氧混音签名:tier=low/crit 期间 DIVE/COMMS 垫 `breath_loop`(低响度呼吸声,SA3 循环);引擎无运行时滤波,BGM 闷化降级为该呼吸层(显式降级,不阻塞管线)。
### 4.4 船员好感/存活矩阵
| 船员 | 好感来源(bond) | 存活条件 | 回响句 |
|---|---|---|---|
| 小舟(声呐兵) | p1 左 或 p2 左 | **恒活**(D1 教学潜保底救上) | ep_zhou_ok / ep_zhou_no |
| 老白(轮机长) | p3 左 或 p4 左 | **恒活**(D2 恒可成——预算数学保证,见 §9 验算) | ep_bai_ok / ep_bai_no |
| 陈医官 | p5 左(当场救上);或留守时 p6 左 | p5 左即活;p5 右留守 → D4 双载或被选则活,否则失 | ep_chen_ok / ep_chen_lost |
| 小郑(无台词角色) | —(吊床上的沉默存在,radio 老主播「带子里只有电流声」先例) | p5 右即活;p5 左留守 → D4 双载或被选则活,否则失 | ep_zheng_ok / ep_zheng_lost |
| 高船长 | p8 左(gaoPeace) | D4 双载 或 单载选左则活;单载右/终局取消则失 | ep_gao_peace / ep_gao_up / ep_gao_lost |
- 好感只影响回响选播(情绪差分),**不影响结局判定**——结局由 savedCount × p9 决定,账目单一可验算。
- 生死分支覆盖 3 人(陈医官/小郑/高船长),措辞全部克制(「滑进了沟底」「灯还亮着」),无临场描写;失败归因走干涸链/取消链的真话句。
### 4.5 氧气收支验算(设计期穷举,playtest 剧本 A-D 引用)
设 `lefts` = 前 3 潜左路气耗之和(p7/p9 不在前 3 潜):
- SURFACE(3) 时 oxy = 15 − 9(基础)− lefts = **6 − lefts** → 双载 ⟺ lefts≤2;单载 ⟺ lefts=3;取消 ⟺ lefts≥4。
- 双载收队 oxy_end = 2 − lefts(p7 右)或 0 −(lefts−2)(p7 左,floor 0)→ p9 左可用 ⟺ lefts≤1。
- 单载收队 oxy_end = 3 − lefts = 0(lefts=3)→ p9 左恒封锁 → 单载路径必 e4。
- 取消收队 oxy_end = 6 − lefts ∈ {2,1,0}(lefts=4,5,≥6)→ lefts≤5 时 p9 左可用 → e3。
- 前 3 潜恒可完成的预算证明:D1 最大支出 5(12→7);D2 最大支出 6(7→1)后应急瓶 +3(→4)≥3 ✓;D3 恒可下(最差 4→1,p6 左 floor 0 走干涸链)✓。**教学潜与机舱潜永不被氧气卡死**(P4)。
## 5. 音频三层映射
### 5.1 BGM(状态→曲目,Mureka instrumental,4 首 ≈ $0.12)
| 状态 | 曲目 | 切换 | Mureka prompt 草稿(英文,seamless loop) |
|---|---|---|---|
| INTRO/SURFACE/MENU | `bgm/deck` | 开局淡入 800ms;MENU 呼出低响度淡入 500ms | "Night sea surface from a rescue ship deck, sparse cello and low piano over gentle waves, distant rigging creak and engine hum, melancholic but purposeful, no vocals, seamless loop, 60-90 seconds" |
| DIVE | `bgm/dive` | crossfade 800ms 进;接通 COMMS 淡至 comms 600ms | "Deep ocean descent ambience, dark ambient drone with a slow sonar-like pulse, immense water pressure, distant whale-like tones, subtle tension, no vocals, seamless loop, 60-90 seconds" |
| COMMS | `bgm/comms` | crossfade 600ms;上浮淡回 deck | "Underwater radio contact scene, warm muted piano over a deep water drone, faint hydrophone static, intimate and quietly hopeful, no vocals, seamless loop, 60-90 seconds" |
| ENDING | `bgm/ending` | p9 结果链播完后淡入 1500ms | "Dawn on the sea after a deep sea rescue, warm strings and soft piano, exhausted hope and quiet triumph, no vocals, seamless loop, 60-90 seconds" |
- `bgm=0` 时全部静默(引擎约定);menu 用 deck 同曲(省 1 首,海面场景同构);共性后缀(10 号 §8)照 radio 追加。
### 5.2 SFX 清单(SA3=Stable Audio 3 / SYN=程序合成;one-shot 真峰值 ≤-3dBFS;amb 循环由游戏层 `buffer.loop` 实现,radio phone_ring 先例)
| id | 用途 | 来源 | prompt(英文)/ 合成参数 |
|---|---|---|---|
| `sfx/sonar_ping` | 你发出的声呐脉冲(搜索单击) | SYN | 1.4→0.9kHz 正弦 chirp 220ms,指数衰减,-6dBFS(回波家族统一音头) |
| `sfx/echo_metal_l` / `_r` | 钢板回波(左/右声像;「金属回波=海燕号」承诺音) | SA3 | "Sonar echo returning from a steel submarine hull, resonant metallic ping with slow decay, panned slightly left(→right), underwater, 2 seconds" |
| `sfx/echo_debris` | 碎石回波(D2 机舱外) | SA3 | "Sonar echo returning from an underwater rubble and debris field, diffuse granular reverberation, 2 seconds" |
| `sfx/echo_groan` | 船体呻吟(D3 预警/D4 滑坡前奏,可变增益复用) | SA3 | "Stressed submarine hull groaning deep underwater, long metallic moan under immense pressure, 4 seconds" |
| `sfx/pipe_knock` | 管路敲击暗号(老白 SOS) | SYN | 三短三长三短固定节拍金属敲击(节拍精确可复现、断言可数) |
| `sfx/link_beep` | 电台接通 | SYN | 双音上行 0.3s |
| `sfx/static_burst` | 通讯静电/断续 | SA3 | "Shortwave radio static burst with faint ghost of a distorted voice, 2 seconds" |
| `sfx/amb_deep` | 深潜底噪 loop | SA3 | "Deep ocean ambient loop, low pressure hum, distant current wash, occasional far creak, seamless loop" |
| `sfx/amb_deck` | 甲板底噪 loop(风+浪+缆绳) | SA3 | "Night ship deck ambience, wind, gentle waves against hull, distant rigging creak and engine hum, seamless loop" |
| `sfx/breath_loop` | 低氧呼吸 loop(仅 low/crit 档垫,混音签名) | SA3 | "Slow scuba regulator breathing loop, calm but slightly labored, underwater, seamless loop" |
| `sfx/spark_flash` | D2 危机拍:切割火花+氢气虚惊 | SA3 | "Underwater cutting torch sparks with a brief muffled gas pop, 2 seconds" |
| `sfx/valve_close` | 重阀关闭(p4 左) | SA3 | "Heavy submarine valve wheel shutting, metal rotation clunk and water seal, 2 seconds" |
| `sfx/landslide` | D4 危机拍:海底滑坡轰鸣(前置 2s 渐强) | SA3 | "Underwater landslide rumble, rolling sediment and rocks, ominous low roar building, 4 seconds" |
| `sfx/surf_splash` | 破水出水(每潜上浮) | SA3 | "Diver breaking through the sea surface, splash and one gasp, 2 seconds" |
| `sfx/ascent_chime` | 出水确认音 | SYN | 上行三音琶音 0.4s |
| `sfx/bell_toll` | 船钟(p9 左 payoff 音) | SA3 | "Old ship brass bell, single clean toll with warm resonance, 3 seconds" |
| `sfx/ui_confirm` / `ui_menu` | 菜单/确认(程序合成兜底) | SYN | 同全局先例 |
- 预警窗纪律:D2 火花与 D4 滑坡均**前置 2s 起渐强**(L2),danger 签名与音效同窗(radio C5 刹车先例);D4 另叠 `danger` 震循环(每遍间隔 ≥400ms)。
- 承诺兑现绑定:dv1_metal 教「金属回波=钢板」后,凡叙事提到船身/找到船的节点必播 echo_metal*;D2 找人必播 pipe_knock(§6.3 清单 + §9 断言)。
### 5.3 Voice 划分(详见 17-abyss-voice-ids.md)
| 组 | 声部 | 落位 | 数量 |
|---|---|---|---|
| 潜航员(你)DIVER | 双性别(female=Serena / male=Uncle_Fu + DIVER instruct 常量) | `voice/female\|male/` | 30 句 |
| 调度/系统/向导 DECK | 双性别(female=Vivian / male=Dylan + DECK instruct 常量) | `voice/female\|male/` | 97 句 |
| 船员 NPC ×4 | 平铺单音色(小舟=Serena / 老白=Uncle_Fu / 陈医官=Vivian / 高船长=Eric),**不做双性别** | `voice/` 根(平铺回退命中)+ 电台滤波转码 | 21 句 |
- 船员 ≤4 个单音色约束满足;玩家自己的声音跟随全局 `settings.voice`;调度是向导人设(L4 角色化系统声,radio 导播同型)。
- 合计 148 句(≤170 GPU 预算 ✓,含 `_k` 键盘变体 11 对);音频文件 = 127×2 + 21 = 275 条(×2 格式 = 550 文件)。
## 6. 声呐双通道设计(本作核心差异层)
### 6.1 引擎能力边界(对照 web-engine-api.md)
| 能力 | 引擎现状 | 方案 |
|---|---|---|
| voice 运行时 EQ/滤波 | ❌(voice 链无滤波插入点) | **资产侧转码滤波**(radio §6.2 先例):ffmpeg `highpass+lowpass+压缩器` 逐船员定档(§5.3/voice-ids) |
| sfx 循环 | ❌ 即发即弃 | 游戏层 `buffer.loop=true` 持句柄(amb_deep/amb_deck/breath_loop),切换档位时停(radio `_ringSrc` 先例) |
| 语音 duck 垫乐 | ✅ 内置(duck 20%,300ms) | 直接依赖;BGM 不再手工让路 |
| sfx vs voice 互让 | ❌ sfx 总线独立,无 duck | **游戏层 sonarGate 空闲门**(§6.2 R1):信息类回波只在 `!audio.isVoicePlaying()` 时播出,否则挂队列表、voice onComplete/句间轮询补发 |
| sfx 声像 | ❌ 运行时无 pan API | 资产侧 pan(echo_metal_l/_r 两条,escape 先例);其余方向信息由调度语音明说(「左前方」) |
| 变速不变调 | ✅ SoundTouch | 直接依赖(长按 1.7× / 2.2× 视障档) |
### 6.2 通道仲裁规则(并发声流 ≤2 约束)
- **通道定义**:声呐通道 = sfx 层的脉冲/回波/敲击/呻吟/环境 loop;通讯通道 = voice 层(调度+船员+你)。BGM 是第三层背景(voice 播时引擎自动 duck)。
- **R1 回波不叠语音**:信息类回波(echo_*、pipe_knock、groan 渐强段)与任何 voice 播放区间**零重叠**——sonarGate 只在 voice idle 时放行,在播则排队到句间空隙(≥600ms);玩家主动脉冲只在搜索等待(voice 恒静)受理,按构造不重叠。
- **R2 声呐主导段**(DIVE 搜索等待):前景=脉冲+回波余音+amb_deep,BGM=dive 曲低响度;voice 只出调度短句(解读句),且解读句固定在回波**结束后**接播(回波→静默一拍→解读,给耳朵留定位时间)。
- **R3 常驻脉冲退位**:语音在播时,环境内的周期性声呐元素(母船远程脉冲垫)降 -6dB 或暂停(游戏层 gain 控制),不消失(保持声场存在感,A2 无静默死局)。
- **R4 危机拍优先**:D2/D4 危机音(spark/landslide)为即发即弃 sfx,可叠在 amb 上;danger 签名同窗;当前 voice 句播完后危机链才插续(radio C5 同款;呼救类短句若必须抢时,走 sfx 层不占 voice)。
- **R5 回波可重听**:搜索等待双击=重听「上一条回波+其解读句」(P1 关键线索可重听);COMMS 双击=重听当前节点(radio 同款)。
- 声流账:任意时刻 ≤2 条前景流(voice 串行 1 + 声呐信息音 1),BGM/amb 为背景层——满足 ASA ≤2 注意流约束。
### 6.3 教学承诺清单(dungeon「铁器=楼梯」教训:每句承诺的机制必须真实存在且可感知)
| 教学句 | 承诺 | 兑现点(断言锁定) |
|---|---|---|
| dv1_metal「金属回波,钢板。听见这个,就是海燕号的船身」 | 金属回波=找到船 | D1 找到/D4 接近指挥舱均播 echo_metal*;全剧本任何「船身」节点零漏播 |
| dv1_teach「单击,发一枚脉冲」(+gd_ping1) | 单击=脉冲 | 搜索等待单击恒出 sonar_ping(非跳句、非无效);无超时不惩罚 |
| b_goal「五个人。你的双瓶,够下四潜」 | 5 人/4 潜 | 基础消耗恰 3 格×4=12(§4.5 验算);剧本 A 恰 4 潜走完 |
| b_rule「气不够一潜,人就必须上浮」 | 氧气门 | 取消门恰在 oxy<3(§4.3/§4.5);剧本 C/D 实证 |
| d1_surf「往返,扣三格气」 | 每潜扣 3 | 每次下潜开始扣 3(账目断言) |
| d1_code「记住这个敲法」 | 敲管=老白的信号 | D2 搜索以 pipe_knock 引导找到机舱 |
| oxy_gain「应急瓶接上了,多三格」 | +3 | 恰一次、恰 +3、恰在 D2 上浮后 |
## 7. 向导层接入
### 7.1 状态签名 earcon / turn cue
| 时机 | 向导动作 | 说明 |
|---|---|---|
| INTRO 进入 | `guide.state('explore')` | 叙事态签名 |
| 搜索等待武装(下潜链末) | `guide.turn({haptic:true})`(无语音) | turn 签名+confirm 震=「轮到你发脉冲」;首次前 dv1_teach 已口播 |
| 每个抉择点 p* 起播 | `guide.state('choice')` | 选项句本身即 turn cue(W2 契约零插播) |
| SURFACE 进入 | `guide.state('settle')` | 结算签名 |
| ENDING 进入 | `guide.state('settle')` | 同上 |
| MENU 打开 | `guide.state('menu')` | 菜单签名 |
| D2/D4 危机拍 | `guide.state('danger')` | 音效前置 2s 渐强同窗(全游戏仅两处) |
| 无效手势 | `guide.error({voice:'gd_invalid', onceKey})` | error 震 + full 档语音,idle-only(F1.4) |
- earcon 词表:只用全项目统一 8 音(menu/explore/battle/choice/settle/danger/turn/nudge),**零新增**;battle 不用。
### 7.2 看门狗双轨(radio F1 + dungeon E0-1 教训:乱按也要被抢救)
**无操作轨**(guide watchdog,任何手势复位;两级不同句、用尽即停、永不惩罚):
| 等待点 | full 档 | standard 档 | 级别内容 |
|---|---|---|---|
| 搜索等待无脉冲 | [10, 25] | [10, 25](onEscalate 游戏层补播) | ① gd_ping1 ② gd_ping2(**必须不同句**);两级后仅 amb+余音 |
| INTRO/COMMS/SURFACE 链末等待 | [25, 50] | [13, 30] | ① gd_continue ② gd_again(storyline F1.1 同款) |
| 抉择点 p* 播完无操作 | [25, 50] | [25, 50] | 重播当前 p* 提示句(13 号 §16 先例;空点单击=重挂计时) |
| ENDING 结算等待 | [25, 50] | [13, 30] | gd_continue / gd_again |
**无进展轨**(游戏层计时,dungeon E0-1 ②同型;仅挂搜索等待与 COMMS 链末两个可饿死点):
| 项 | 规则 |
|---|---|
| 进展定义 | 节点推进 / 发出脉冲 / 抉择结算。**无效手势(滑错、空点)不计进展**——无操作轨被它们复位、无进展轨不被它们复位,乱按 96s 也必被抢救 |
| 阈值 | 60s → gd_prog1;120s → gd_prog2;用尽即停(每等待点一轮) |
| 有真实进展 | 全程零 prog 播出(不误伤正常玩家,dungeon 断言同款) |
| 双轨去重 | 两轨短句内容同级交叉登记;同一等待点先播过 gd_ping* 则无进展轨跳过同义级 |
- 向导插播一律 `queue:true`,绝不顶掉在播对白;error/watchdog 类语音 idle-only(在播则跳过且过期丢弃,F1.4)。
- 单句语音看门狗(资产缺失兜底):30s 超时且 `isVoicePlaying()===false` 才自动续链,长句续约不掐(radio N1 先例照抄)。
### 7.3 键盘变体机制(PC 键盘玩家)
- 运行时按 `gesture.inputMode()`(settings.input auto/touch/key)选 `_k` 变体资产:key → `voice/<gender>/<id>_k.*`,touch → 无后缀版;**auto 跟随最近使用通道**(keydown 后即切 key)。`_k` 缺失回退无后缀版(资产侧保证成对,check_all_assets 校验)。
- 变体清单(11 对,集中分组见 voice-ids §G0):gd_brief / gd_continue / gd_again / gd_ping1 / gd_ping2 / sys_help / sys_menu / sys_skip / sys_continue_ask / dv1_teach / p1。
- 抉择提示 p2..p9 措辞统一「向左滑/向右滑」;键盘玩家由 p1_k 与 sys_help_k 完成首次绑定(左=←,右=→),后续提示语义不变。
- 启动遮罩文案引擎自理(「按空格开始」/「轻触开始」),游戏零改动。
### 7.4 继承 radio 三轮验证机制(照抄不重设计)
| 机制 | 来源 | 落点 |
|---|---|---|
| 帮助链去重(同等待点第 2 次上滑只震+nudge) | radio G1 | `_helpSeen` 按等待点键控 |
| 状态链顺序「状态在前、帮助在后」 | radio G6 | prog_dN → oxy 档 → sys_help |
| 空点确认轻震(不可跳句上单击补 beat [8]) | radio G4 | p*/链末句/F4 锁各 tap 屏蔽分支 |
| oxy_crit 播报位附 warning 震 | radio G2 | 全部 crit 出口(含上滑复听) |
| 菜单报名游戏侧排队零截断 | radio 实现修复 #3 | `_menuQueue` 排水 |
| stop() 先 `seqToken++` 作废在途链 | radio 实现修复 #6 | 同款 |
| 新局清 choiceArmed/pendingChoice | radio 实现修复 #7 | newRun() |
| 长按误报无效(longpress 全状态合法) | radio 实现修复 #2 | 仅 left/right(ASK 另含 up/down/doubletap)报无效 |
## 8. 存档设计
### 8.1 进度 data 结构(PUT /api/progress {game:'abyss'})
```json
{
"schema": 1,
"updatedAt": "2026-09-09T12:00:00Z",
"summary": "第2潜·机舱 · 氧气6格 · 结局1/4",
"phase": "COMMS",
"node": "bai_b",
"dive": 2,
"oxy": 6,
"supply": true,
"saved": { "zhou": 1, "bai": 0, "chen": 0, "zheng": 0, "gao": 0 },
"stayer": null,
"flags": { "knowsPipe": true, "hasLog": false, "valve": false, "spareGift": false, "gaoPeace": false, "bell": false },
"endings": { "e2": true },
"runs": 3
}
```
- 写入时机:每潜 DIVE 进入、每个抉择结算后、SURFACE 进入、ENDING 进入、菜单「保存并回大厅」。
- 断点续玩:读档 → sys_continue_ask(_k)(单击=继续上次 / 右滑=新救援);继续=恢复 phase+node+全部数值并**重播当前节点链**=一句话局面复述(L5);重进 ≤5s 可玩;恢复首句受 F4 跳句锁(radio G5 例外条款)。
- summary 格式:进行中 `第N潜·<舱段> · 氧气K格 · 结局X/4`;收队 `救援收队·<结局名> · 结局X/4`(大厅卡片直接显示)。
## 9. playtest 断言清单(登记 13 号 §21 指针;`settings.set({guide:'off'})` 纯玩法验证,向导行为住 guide.test)
**关键路径**(剧本 A:全右+左p9 → e1,≤70 次手势):
1. 遮罩点按 → INTRO 链(b_open 起,voice 层有播放记录)→ 链末单击 → DIVE(1) 下潜链 auto → 搜索等待武装(turn 事件 + amb_deep 循环起播)。
2. 搜索单击 → sonar_ping → 回波静默一拍 → echo_metal_l → dv1_metal(承诺音)→ link_beep → COMMS(1):z1_a/b/knock → p1 右 → p2 右 → 上浮:surf_splash+ascent_chime → d1_surf → oxy=12-3=9 → oxy_full 播出(账目断言)。
3. DIVE(2):脉冲 → echo_debris+pipe_knock → dv2_found → bai_a/b → p3 右(danger 签名前置 2s 与 spark_flash 同窗断言)→ p4 右 → d2_surf → d2_supply → oxy_gain(6→9,恰一次)。
4. DIVE(3):脉冲 → echo_groan → dv3_groan → chen_a/b → p5 右 → p6 右 → d3_surf(oxy=6,ok 档)。
5. 氧气门:6≥4 → DIVE(4) 双载:滑坡危机拍(landslide 前置 2s 渐强 + danger 签名 + danger 震)→ gao_a/b → p7 右 → gao_c → p8 右 → d4_surf → oxy=2(crit 档+warning 震)。
6. 签收:g9_in → g9_knot → p9 左(oxy=2≥1 可用)→ d9_bell + bell_toll → ENDING(e1):e1_a/b + ep_lead + ep 链 → 结算单击 → 新救援回 INTRO。
**边界断言**:
- **四结局可达(含氧气逐点验算)**:剧本 B(同 A 但 p9 右)→ e2;剧本 C(p1左 p2左 p4左,余右):D1 后 oxy=7(ok)/D2 后 2→supply→5(gain+low 档)/D3 后 2(crit)→ 氧气门走**终局取消链**(dv4_none→gao_final 完整播出)→ p9 左(2≥1)→ e3(saved=3);剧本 D(p1左 p4左,余右):D1 后 8(ok)/D2 后 3→supply→6(ok)/D3 后 3(low)→ **单载形态**(p7 按 stayer 选 p7_chen|p7_zheng)→ p7 左=带船长 → 留守者留别句(chen_last|d4_stay_lost)→ p8 右 → oxy_end=0 → p9 左**封锁**:g9_dry 播出后自动右 → e4(saved=4)。各局 oxy 逐检查点与 §4.5 公式一致。
- **氧气账目真实**:每次扣减后播出的档位句与 floor(oxy/3) 桶一致;上滑 status 链顺序 prog_dN → oxy 档 → sys_help(G6);supply 全局恰一次;剧本 E(p2左 p3[无knows]左 p4左 p6左)mid-dive floor 至 0 → dry_a/dry_b 播出且当前救援者照常救上(人已挂上吊索不失去)。
- **双通道仲裁**:全剧本转写断言——echo_*/pipe_knock/groan 信息音播出时刻与任一 voice 播放区间**零重叠**(sonarGate 排队语义:晚于在播句结束);搜索等待单击恒=脉冲(sonar_ping 计数=1,无跳句无 error);双击搜索等待=回波+解读句同序重播;语音在播时 amb_deep 持续、远程脉冲垫降 -6dB(R3)。
- **教学承诺兑现**(§6.3 清单逐条):dv1_metal 后所有「船身」节点 echo_metal* 零漏播;D2 找到机舱前 pipe_knock 恒在;b_rule 与取消门一致(剧本 C 证明);b_goal 四潜=剧本 A 恰 4 潜;每潜起播扣 3 断言。
- **看门狗双轨**:搜索等待快进 10s→gd_ping1、25s→gd_ping2(两级 id ≠,绝不逐字重播;两级后仅 amb);**乱按 96s**(每 2s 注入一个无效滑动):无操作轨零播出(被手势复位)+ 无进展轨 60s→gd_prog1、120s→gd_prog2(乱按饿不死);有真实进展(单击脉冲推进)全程零 prog 播出;COMMS 链末 25s/50s → gd_continue/gd_again;p* 播完 25s/50s 重播 p* 两次后静默;`guide=off` 全程零定时器零向导事件。
- **键盘变体**:settings.input='key' 会话播出 gd_brief_k/sys_help_k/gd_ping1_k/dv1_teach_k/p1_k 且对应无后缀版零播出;'touch' 反之;'auto':keydown 后下一句用 _k。
- **中断复述**:杀进程重进(存档 node=bai_b)→ sys_continue_ask → 单击 → 重播该节点链(恢复首句不可跳,第二句起可跳);右滑=新救援(runs 口径同结算重开)。
- **结算链死窗(H1 内化)**:终局取消链/干涸链/e* 结局链开播后 800ms 内注入在途 tap(mock 补派 touchend+300ms 同款序列)→ 手势被吞、链完整有序播出、结算出口手势照常。
- **pendingChoice**:p* 播放中左右滑 → 播完立即生效(不丢、不重复、连滑取最后一次)。
- **p* 不可跳**:p* 播放中单击 → 不跳句(PROMPT_RE 门)+ beat 轻震(G4)。
- **idle-only**:向导 gd_* 在对白在播时跳过且不占 onceKey(F1.4);queue 句排在当前句后零截断。
- **菜单**:下滑开菜单 → 上滑帮助 → 双击「回到水下」→ phase/node 不变、当前节点重播;单击循环顺序 = [回到水下, 重新开始这次救援, 保存并回大厅];确认回大厅 → sys_menu_lobby 播出且 PUT/localStorage 有最新 data。
- **资产缺失**:屏蔽任意 voice 资产 → 30s 看门狗自动续链到结局不卡死;NPC 滤波资产缺失回退原声可播。
- **数字规范**:prog_*/oxy_*/gd_* 全部整句字面量,静态审计无运行时拼接碎片。
- **低氧混音签名**:tier=low/crit 期间 breath_loop 在播、升档/收队即停。
**失败即不通过**:任何等待 >5s 无声(静默死局);无效手势零反馈;回波与语音重叠;结局计数与 §3.4 不符;氧气档位句与 oxy 值不符。
## 10. 节奏曲线(每次下潜目标时长)
| 段 | 内容 | 目标时长 | 设计目标 |
|---|---|---|---|
| 开场+下潜链 | 甲板简报+第一潜下水+搜索 | 1-1.5 min | 首输入 <60s(开场链 ~20s 链末单击,或 ~40s 搜索首脉冲);规矩一句讲完(P4) |
| D1 找船 | 教学潜:脉冲/回波/接通/2 抉择 | 4-4.5 min | **首个正反馈 ≤3min**(~2.5min 金属回波+接通小舟=「看见它了」);两抉择条条有出路;左路气耗被补给兜底=学习期零惩罚 |
| D2 机舱 | 老白:撬/切+阀门抉择 | 3.5-4 min | 首个危机拍(火花,前置 2s);补给+3 兑现宽限;暖拍(热粥,P4) |
| D3 病舱 | 生死抉择+回波预警 | 3.5-4 min | 船体呻吟埋 D4 险;留守约定=段落边界(P3 中断友好:每潜一个完整存档颗粒) |
| D4 指挥舱 | 滑坡高潮+氧气门三形态 | 4-4.5 min | 紧张峰值:danger+滑坡渐强+双人竞速;生死分支收敛 |
| 终局+结局 | p9 心结+结局链+逐人回响 | 2.5-3 min | 氧气余量兑换终局抉择;ep 点名(P2 回响);开环钩子拉下一局 |
- 合计 ≈ 20-22 min(15-25 达标);每 30-60s 一个反馈颗粒(回波/解读短句/结果句/档位句),无沉默尴尬期(P3)。
- 难度曲线 = 道德复杂度(教学二选一 → 情报投资 → 生死取舍 → 自己的心结),操作复杂度恒定(永远二选一+单击脉冲)——P4「规则一遍听懂」由恒定操作面保证。
## 11. 自检清单(策划交付自验;☐ = 待开发/测试轮填)
| # | 硬约束/教训 | 落地 | ✓ |
|---|---|---|---|
| 1 | FTUE 首输入 <60s | 开场链 ~20s 链末单击;~40s 搜索首脉冲(§10) | ☑ |
| 2 | FTUE 首个正反馈 ≤3min | ~2.5min 金属回波+接通小舟(§10 D1) | ☑ |
| 3 | 学习期零惩罚 | D1 左路 -2 被 D2 后 +3 兜底;搜索无超时惩罚;D1/D2 恒可完成(§4.5) | ☑ |
| 4 | 失败三问归因+归因真话律 | 干涸链 dry_b 指向实际左选择;gao_final/d4 留别句克制不假安慰;§4.4 无假归因句 | ☑ |
| 5 | 资源账目真实(radio rep_low 教训) | 氧档以 floor(oxy/3) 划界、每档句义逐格为真(§4.3 表);补给恰一次 +3;扣 3 恰每次下潜 | ☑ |
| 6 | 手势宪法 6 手势默认语义零豁免 | §2.3 全覆盖;单击搜索=「检查」(宪法 §5.1+escape 先例);菜单 §5.2 模态约定 | ☑ |
| 7 | 长按显式登记 | 无豁免:长按=引擎快进(12 号叙事类不豁免),§2.3 在案 | ☑ |
| 8 | 向导:签名/turn cue/看门狗两级不同句 | §7.1/§7.2;gd_ping1≠gd_ping2;gd_continue→gd_again | ☑ |
| 9 | 看门狗双轨(dungeon E0-1/radio F1 教训) | 无操作轨+无进展轨(§7.2);乱按 96s 必被抢救;有进展零打扰 | ☑ |
| 10 | 插播 queue 不顶对白;纠错 idle-only 过期丢弃 | §7.2(F1.4 契约) | ☑ |
| 11 | 死亡/失败结算链完整(dungeon H1 教训) | 终局取消链/干涸链/结局链 800ms 手势死窗;已救者永不失去(§2.2/§4.3) | ☑ |
| 12 | 教学承诺兑现(dungeon「铁器=楼梯」教训) | §6.3 承诺清单 7 条逐条绑兑现点+断言 | ☑ |
| 13 | 单次播报 ≤3 选项 | 全部抉择恒 2 选项;菜单 3 项循环 | ☑ |
| 14 | 并发声流 ≤2 + 双通道仲裁 | voice 串行+声呐信息音互斥(R1-R5,§6.2);断言零重叠 | ☑ |
| 15 | earcon ≤7、统一词表、语义靠语音 | 只用统一 8 音中 7 个(battle 不用),零新增 | ☑ |
| 16 | 台词一句一文件 ≤40 字、数字分档整句、无运行时拼接 | voice-ids 全表 ≤30 字(最长 p8=30);prog/oxy 分档整句;句内数字写死 | ☑ |
| 17 | 多音字规避/同 instruct 常量/无语速词 | voice-ids 头部+文末自查(重写→补、脏/晃/闷标注试听) | ☑ |
| 18 | PC 键盘变体(提示句集中+标注) | 11 对 _k 变体集中分组(voice-ids §G0);inputMode() 运行时选择 | ☑ |
| 19 | P1:可打断/重听(含回波)/status key/不当白痴 | 跳句+双击(回波可重听 R5)+上滑状态链+成功零补救教学 | ☑ |
| 20 | P2:3 分钟见效/结局回响 | ~2.5min 首个改变世界的时刻;ep 逐人点名+bell 差分;e1 巧妙路径(p1左+p3左 省气取钟) | ☑ |
| 21 | P3:3 分钟颗粒/中断复述/单手 | 一潜=3.5-4.5min 完整段落+边界自动存档;重进重播当前节点;全手势免视单手 | ☑ |
| 22 | P4:一遍懂/容错/无高压倒计时/不骂人 | 一句规矩;无实时倒计时(步进资源+跨档才播);软失败零死亡;NPC 句 ≤24 字+逗号停顿 | ☑ |
| 23 | 抉择树 4 结局可走通验算 | §4.5 公式+§9 剧本 A-D 逐 oxy 检查点(e1:lefts≤1+双载;e2:lefts≤2;e3:lefts=4-5+取消;e4:单载 lefts=3 或取消右) | ☑ |
| 24 | 单局 15-25 min | §10 曲线 ≈20-22 min | ☑ |
| 25 | 分支不爆炸(radio 纪律) | 跨潜仅标量(oxy/saved/stayer/6 flag);~70 节点固定深度(§3.3) | ☑ |
| 26 | 台词 ≤170、船员 ≤4 单音色 | 148 句(双 127+NPC 21)/4 个船员音色(voice-ids 账目) | ☑ |
| 27 | 引擎边界诚实 | §6.1 能力表:滤波/loop/声像/互让全写明资产侧或降级方案 | ☑ |
| 28 | 存档带 summary、断点续玩 | §8:data+summary 格式、≥5 恢复、局面复述 | ☑ |
### 批 1 教训内化确认(5 条全落地)
| 教训 | 本作机制 | 断言 |
|---|---|---|
| 教学承诺必须兑现 | §6.3 七条承诺清单(金属=钢板/单击=脉冲/四潜/氧气门/扣三格/敲管/补给) | §9「教学承诺兑现」块 |
| 看门狗双轨 | §7.2 无操作轨+无进展轨;无效手势不复位进展轨 | §9「看门狗双轨」块 |
| 档位措辞真实 | §4.3 档位=floor(oxy/3) 可下潜次数,句义逐格为真 | §9「氧气账目真实」块 |
| 失败结算链完整不被打断 | 800ms 手势死窗盖取消链/干涸链/结局链;seqToken 作废在途链 | §9「结算链死窗」块 |
| PC 键盘操作提示 | 11 对 _k 变体+inputMode() 选择+集中分组标注 | §9「键盘变体」块 |
## 12. R2 修订记录(2026-09-13,依据 research/new8/abyss/round1/synthesis.md)
> 追加记录,不改旧裁定;凡本节与上文冲突,以本节为准。全部改动已落地 web/abyss/game.js,断言增量见文末注记。
| # | 旧裁定 | 新裁定(R2 起生效) | 依据 |
|---|---|---|---|
| R2-F1 | 玩家脉冲走 sonarGate 空闲门(§6.2 R1 按构造不重叠) | 玩家脉冲**先于门**:搜索等待态 voice 层只可能是向导散句(gd_ping*/gd_prog*),单击发脉冲时先掐散句、ping 立即出音。信息类回波仍走空闲门不变 | live R1 P1-1:ping 迟到 12~17s「回波先于脉冲」(3/5 复现),教学潜核心承诺错序 |
| R2-F2 | F5 尾句保护按 \_nodeSteps 重算尾句;签收链无死窗 | 尾句保护改认**实际播放链尾**(dyn 注入的档位句纳入保护:档位句不可被单击掐断);签收链(g9_in)入口盖 800ms 死窗(与取消/干涸/结局链同款) | live R1 P1-3:oxy_low 播 0.71s 被掐、g9_in 被在途 tap 剁到 0.28s |
| R2-F3 | 回波 wait 常数 2200/2600/4000ms | W_Metal/W_Knock/W_Groan = 3300/3500/5000ms(资产实长 2.76/2.96/4.42s + 静默一拍),兑现 §6.2 R2「回波→静默一拍→解读」。玩家单击仍可快进回波余音(_sonarSkip 不变) | live R1 P2-1 + P1 评审:回波尾巴压解读句开头 0.35~0.55s,定位一拍被吃 |
| R2-F4 | D4 双载额外 +1 不口播 | 双载形态下潜链追加 `dv4_dual`「两个人下同一潜。这趟往返,扣四格气。」(3+1=4 扣账口播化) | R1 P1:心算账 12−3−3+3−3−3=3 与耳朵听到的 crit 档差一格 |
| R2-F5 | crit 档全场景播「氧气见底,必须上浮。」 | 水面场景(SURFACE/ENDING)播新句 `oxy_crit_deck`「氧气见底,一潜都下不去了。」;水下(DIVE/COMMS)保留原句。档界 floor(oxy/3)=0 不变,两句各自逐格为真 | R1 P1:人已出水后「必须上浮」是说错地点的正确话 |
| R2-F6 | G5「首播必完整、复播可即掐」(§2.3):实现只兑现前半句(尾句保护为永久态) | 非 auto 叙事节点尾句保护改**首播制**:链自然播完登记 `_tailHeard`,此后该节点重播(菜单/状态复述)尾句可单击跳过;auto 过场尾句、直入抉择尾句、p* 提示、F4 锁恒保护不变。sys_skip(_k) 同步改词(见 voice-ids R2-F6) | live R1 P1-2:跳句承诺名存实亡(B 局全程零句可跳);radio F5 吃尾根因由首播保护继续挡住 |
| R2-F7 | 叙事链末/ENDING 无操作轨 full [25,50] / standard [13,30](§7.2) | full 档对齐 standard:**[13,30]**(搜索 [10,25]、抉择点 [25,50]、无进展轨 60/120s 不变) | R1 P2+P3:接通小舟后 25s 静默掐在钩子落地上(走神/怀疑锁屏) |
| R2-F8 | D4 danger 震循环 500ms(§5.2,≥400ms) | **1100ms**(危机拍 4s 窗内约 4 遍;≥400ms 下限仍满足) | R1 P2+P4:4 秒约 10 连震,贴身躺玩/年长者过刺激 |
| R2-F9 | 状态链每逢 crit 档附 warning 震(G2) | 按「进入 crit 档」去重:档不变复听不重复震;s_tier 跨档播报位的 warning 震保留 | R1 P4:压力局摸状态十几回,回回挨三连震 |
| R2-F10 | 状态链 = 潜次 → 氧档 → 帮助(G6) | 状态链 = 潜次 → **已救人数(prog_saved1..5,saved=0 不播)** → 氧档 → 帮助 | R1 P3:被打断后捡局面缺「船上躺着谁」 |
### §9 断言增量(R2 登记,已随 playtest.mjs 落地)
- F1:gd 行在播(stub 引擎在播)时搜索单击 → sonar_ping 立即起播且 gd 行被掐(不再入队迟到)。
- F2:档位句(oxy_* dyn 注入)播放中单击不跳(beat 轻震);SURFACE(4)→g9_in 过场 800ms 内在途 tap 被吞、签收链完整。
- F3:回波起播→解读句起播间隔 ≥ 对应 wait 常数量级(含静默一拍)。
- F4:双载 D4 播 dv4_dual;单载/取消不播。
- F5:SURFACE 的 crit 播报 = oxy_crit_deck;水下状态链 crit = oxy_crit。
- F6:首播中叙事尾句单击不跳;链自然播完后经菜单复述重播,尾句单击即跳(复播可即掐)。
- F7:full 档 COMMS 链末 13s→gd_continue、30s→gd_again(原 25s/50s 断言同步改 13s/30s)。
- F8:D4 危机拍 danger 震间隔 ≥1000ms(实测 = 1100ms)。
- F9:同档 crit 期间连续两次上滑状态链,warning 震仅一次。
- F10:状态链含 prog_savedN 且 N=当前 savedCount;顺序 潜次→人数→氧档→帮助。