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

207 lines
18 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 号文档落地)
> **设计单正文见 [16-dungeon.md](16-dungeon.md) 附录 C**(一句话核心动作 / 状态 /
> 手势表 / 等待点 / 首 3 分钟脚本 / 失败表 / 台词账目 / 断点复述),本施工单不重复,
> 只写:实现接线(§11)、偏差记录(§12)、台词类别映射(§8)、测试记录(§10)。
> 交互规则权威来源:[26-audio-interaction-playbook.md](26-audio-interaction-playbook.md)、
> [00-global-ux.md](00-global-ux.md) §5.3。台词登记 [16-dungeon-voice-ids.md](16-dungeon-voice-ids.md)。
> 本次只动交互层:地窟生成、回声编码、战斗数值、遗产经济一概不动。
## 1. 一句话核心动作
**朝四个方向滑动在洞窟里走,单击发出声波脉冲听地形,按住屏幕问路。**
## 2. 玩家可感知的状态(3 个)与声音身份
| 状态 | 听感 | 来源 |
|---|---|---|
| 探索(轮到你) | 洞穴环境垫 amb_cave(静默 20s 补一层)+ 脚步/回声/怪物脚步就是状态声 | §4.1 ACTION |
| 听讲(游戏在说) | 教学/遭遇/结算语音,BGM 整链压低(floor.holdDuck) | §4.1 SPEAKING |
| 危险(战斗) | battle/boss BGM;敌方前摇音 tel_* + beat 震;血 ≤25% warning 震 | ACTION + 战斗曲即状态 |
撞墙 = 闷响 bump + invalid 回执,不算第四态(一次性教,见 §7)。
## 3. 新手手势表(四向滑动是玩法 → §4.3 裁定:帮助换到长按)
| 手势 | 含义 | 说明 |
|---|---|---|
| 四向滑动 | **面朝相对移动**:上=前进一格;左=向左一格并转向;右=向右一格并转向;下=后退一格(面朝不变) | swipeCore,阶段 0 即核心,不按阶梯锁 |
| 单击 | 脉冲(探索);楼梯格=下潜确认;战斗我的回合=挥击;敌方前摇窗内=格挡 | 核心动词;旧「双击=免冷却重发」「双击=格挡」废除 |
| 长按 | 我在哪(层[+血 if 伤]+面朝+目标方位,≤20 字) | longpressCore;旧「按住前进」废除;对局期关闭旁白快进 |
| 双击 | 新手期按单击处理;阶段 3 = 重听(结算/标题重听等待点句;探索 = 冷却外再脉冲) | 禁绑「立刻免冷却重播」 |
| 下滑 | 探索/战斗=后退/逃跑(玩法占用);菜单仅阶段 2 且 TITLE/SETTLE | 00 §5.3 |
管道:`makeInputPipeline({ ..., opts: { swipeCore: true, longpressCore: true } })` +
`attachGestures(root, pipe, { doubleTapWindow: 0 })`。
## 4. 状态 × 手势矩阵(新手期)
| 状态 \ 手势 | 单击 | 四向滑动 | 长按 | 双击(未解锁) | 连点/乱滑 |
|---|---|---|---|---|---|
| TITLE | 开始/继续 + ok | 右=新周目;上=元进度链;左/下(未解锁)=invalid+解释 | 重听询问句 | 按单击 | 计入迷茫 |
| 教学作答 | 按步骤(脉冲/确认) | 按步骤走/转向;步骤外 invalid+inv_drill | 帮助(D5 即教学内容) | 按单击 | 计入迷茫 |
| 探索(ACTION) | 楼梯格=下潜;否则脉冲+回声组(silent-ok);冷却中 invalid+inv_pulse | 移动=脚步(silent-ok);撞墙=bump+invalid+归因+方位真话 | 帮助(层/血/面朝/方位) | 按单击(=再脉冲) | 乱滑计入迷茫;冷却连点 invalid |
| 描述在播 | 跳过这句 + ok | 打断并移动 | 打断,帮助 | 按单击 | 第一下有效 |
| 战斗·我的回合 | 挥击(面前才中) | 左/右=转身(缠斗怪 btl_no_turn 每场一次);下=逃跑(Boss 拒绝);上=战斗状态 | 帮助(血+敌方位) | 按单击 | 计入迷茫 |
| 战斗·敌前摇 | **格挡**(面对 ×0.2 / 未面对 ×1.0) | 左/右=转身(来得及则面对) | invalid+inv_tel | 按单击=格挡 | 只处理第一下 |
| DESCEND 演出 | 跳句 + ok | 打断演出(兜底定时器续流程) | 同左 | 按单击 | busy/ok |
| 结算(critical 链) | busy 回执(死亡/通关归因链不打断) | busy 回执 | busy 回执 | busy | busy |
| 结算(等待) | 再探一次 + ok | 上=元进度链;左=invalid+inv_settle;下(阶段2)=菜单 | 重听结算句 | (阶段3)重听结算句 | 只处理第一下 |
| 菜单 | 确认当前项 + ok | 右=下一项 / 左=上一项(hold);上=帮助 | 重听菜单说明 | 按单击=确认 | busy |
| 迷茫 L2 期间 | 照常 | 只放行接管方向,其余 invalid(不移动) | 照常 | 照常 | 照常 |
验收:无空格、无「没有声音」的格子;连点/乱滑无破坏性效果(重开走菜单二次确认)。
## 5. 等待点配置表(教学期)与探索期接管
教学等待点(6s/15s 两档;wrong 手势 = invalid 回执 + explainOnce,三级接管后 75s 兜底自动过步):
| 步 | 期待 | 指令句(full) | short |
|---|---|---|---|
| D0 | 单击 | d0_new「黑暗。点一下,听回声。」(critical) | sys_first_tap_short「点一下。」 |
| D1 | 再单击(巩固) | tut_praise + tut_front_rule(回声规则) | 「点一下。」 |
| D2 | 上滑(前进) | sys_turn_teach → tut_move「朝前,滑一下。」 | hint_swipe「滑一下。」 |
| D3 | 左/右滑(侧移转向) | tut_side_move「向左滑,转身走。」 | hint_swipe |
| D4 | 走到金币格(四向) | tut_coin「房里有金币,走过去捡。」→ 拾取即 ladder.onGoal() | hint_swipe |
| D5 | 长按 | tut_help_hold「迷路了,按住屏幕。」→ 帮助链即答案 | 「按住屏幕。」 |
| D6 | 教学战:前摇单击格挡→单击挥击 | tut_mob → tut_parry_tap → tut_attack | btl_turn_short「点一下,打。」 |
| D7 | 自行走到楼梯→单击下潜 | tut_stairs「铁器的回声,就是下潜的楼梯。」 | hint_swipe |
其他等待点:
| id | 期待 | full | short | 静默 | L3 演示 |
|---|---|---|---|---|---|
| title | tap/right | sys_first_tap / sys_continue_ask | 「点一下。」 | 12s/30s | 提示音 |
| battle | tap/left/right | gd_turn「轮到你了。单击挥击。」 | btl_turn_short「点一下,打。」 | 8s/15s | 游戏替你挥一拳 |
| settle | tap | settle_again「单击,再探一次。」 | 「点一下。」 | 20s/45s | 提示音 |
| menu | tap/left/right | menu_help | menu_help | 20s/45s | 提示音 |
探索期(无固定等待点,静默 40s 起每 20s 喂迷茫信号)三级接管:
| 级 | 做什么 | 台词 |
|---|---|---|
| 1 | 说目标(铁器暗示/窟主守梯,30s 台账去重) | stair_hint_a「找找铁器的回声,那是楼梯。」/ stair_hint_b |
| 2 | 只放行最短路首步方向(面朝相对换算) | sys_only_*「现在,只要向左滑。」 |
| 3 | 听我做一次:游戏自己朝楼梯走一步 | sys_demo + 脚步声 + sys_now_you |
进展事件(拾取/击杀/踏上楼梯/下潜/下潜层)清零重来。
## 6. 首 3 分钟逐秒脚本
| 时间 | 游戏说 / 播 | 期待玩家做 | 没做 | 做错 |
|---|---|---|---|---|
| 0~3s | amb_cave →「黑暗。点一下,听回声。」(critical) | 单击脉冲 | 6s:「点一下。」15s:提示音 | 滑动:invalid + inv_drill |
| 3~8s | pulse + echo + 材质 →「对。」「回声回来越快,墙就越近。」 | 再点一次(巩固) | 「点一下。」 | 冷却内连点:invalid + inv_pulse |
| 8~10s | 第二组回声 + 奖励音(levelup 低音量)= 第一次成功 | — | | |
| 10~25s | 「听到这个声音,就是轮到你了。」→ 交话轮 →「朝前,滑一下。」 | 上滑前进 | 「滑一下。」 | 撞墙:bump + wall + wall_dir_front |
| 25~40s | 「向左滑,转身走。」→ 转身声+脚步 | 左/右滑侧移 | 「滑一下。」 | 其他方向也走(D3 接受左右) |
| 40~60s | 「房里有金币,走过去捡。」→ 拾金币 + onGoal | 四向滑动 | 三档提示 | 撞墙归因 |
| 60~75s | 「迷路了,按住屏幕。」→ 帮助链(层/面朝/楼梯方位) | 长按 | 「按住屏幕。」 | 其他:inv_drill |
| 75~90s | 「前面有东西!放心,它伤不到你。」→ 教学蝠逼近 | 听脚步 | 接管 | 乱动:战斗内纠错 |
| 90~180s | 「听到动静,单击格挡。」→ 前摇单击格挡 →「现在单击,挥拳打它」至击杀 →「铁器的回声,就是下潜的楼梯」→ 自行走到楼梯 →「楼梯就在脚下。单击,下潜。」 | 单击 | 三档/接管 | 挥空:btl_swish 每场一次 |
节奏检查 ✓:3s 内脉冲;~8s 第一次成功(听懂回声);每 30s 有可听进展(金币/击杀/下潜);开场零规则念白(intro_1/intro_2 砍掉)。
## 7. 失败表
| 失败类型 | 判定 | 归因句 | 回到哪里 | 新手期代价 |
|---|---|---|---|---|
| 撞墙 | 目标格越界 | 首次 wall「那边是墙。」;每方向整局一次 wall_dir_{front,left,right,back},之后只 bump+invalid | 原地 | 无伤 |
| 脉冲冷却 | 1.2s 内再点 | inv_pulse「回声还没回来,稍等。」(状态×手势一次) | 原地 | 无 |
| 挥空 | 怪不在面前 | btl_swish 每场一次(之后只挥空声) | 本回合 | 无 |
| 格挡失败 | 前摇内未点/未面对 | 「慢了。听到就点。」= tut_parry_miss(教学期);背后 = btl_behind 每场一次 | 下回合 | L1–2 怪打不死你 |
| 转身无效 | 缠斗怪时左右滑 | btl_no_turn 每场一次,之后仅 invalid | 本回合 | 无 |
| 死亡 | HP≤0 | die_cause_* 按 lastHit;die_adv_* 按真实格挡/逃跑账(critical 链,不许打断) | SETTLE→新周目,金币入 meta | 学期不可能死 |
| 迷路 | 迷茫信号 | 接管 L1→L2→L3 | 原地 | 无 |
## 8. 台词表(类别标注;清单见 tools/voice_lines_26/dungeon.md)
- `critical`:d0_new(开场指令 <3s);死亡链 die_intro→…→settle_again 整链(结局,§4.6);通关链同
- `content`:intro 砍掉后的教学链(tut_praise/tut_move/tut_side_move/tut_coin/tut_help_hold/tut_stairs/tut_mob/tut_attack…)、st_*(层/血/金/面朝/威胁)、evt_*、btl_*(遭遇/半血/击杀/战利)、unlock_*、settle_win_*、tut_stairs_on
- `hint`:长按帮助链(st_floor/st_face/stair_dir_*)、gd_turn、btl_turn_short、hint_swipe、sys_first_tap(_short)、sys_continue_ask、stair_hint_a/b、menu_help
- `feedback`:wall、wall_dir_*、inv_*(pulse/title/settle/drill/tel/menu_locked)、btl_swish、btl_behind、btl_no_turn、tut_parry_miss、die_cause_*、die_adv_*、sys_only_*、sys_demo、sys_now_you、menu_restart_confirm
- 通用系统句与 storyline 逐字一致:sys_first_tap / sys_turn_teach / sys_say_again / sys_unlock_down / sys_unlock_dt(00 号 §5.2 阶梯表)。
- 数字全分档整句不变(层 7 / 血 4 / 面朝 4 / 威胁 4 / 战利 3 / 死因 3 / 建议 3 / 元进度 3;
金位 = meta_gold 3 档 + gold_some 真话句——旧上滑状态链的 st_gold_1..4 随链下线)。
- 净变化:新增 35、修改 4、删除 25(明细见 `tools/voice_lines_26/dungeon.md`);127 → 137 句 ×2 性别。
## 9. 断点复述句
- 保存:拾取/战斗胜负/下潜/死亡/菜单保存/stop() 自动存(schema 1,summary 字段不变:`第{floor}层 · {血量档} · 金币{meta.gold} · 遗产{n}/5`)。
- 回来:TITLE 播 sys_continue_ask → 交出话轮;单击 = 恢复 + 复述链 `[st_floor_N, st_hp_X, st_face_X, 楼梯方位句]`(窟主未清换 stair_hint_b)→ 回 ACTION;右滑 = 新周目(meta 保留,开局 sys_new_run + 同款复述链)。
## 10. 测试记录
- 无头 playtest:`tools/node/bin/node web/dungeon/test/playtest.mjs`,**11 场景 + 资产审计,
断言 1551 条全绿、退出码 0**(2026-10-02,seed 42/2026 等种子复现)。覆盖:
1. seed 复现与七层网格不变式(两次构造逐格相等 / 连通 / 楼梯=最远房 / 安全区 / 密度 / L1 教学硬约束);
2. 新教学链 D0~D7 全走通 + 回声调度全扫描(全地板格 ×4 面向 ===grid 真值,含延迟/音量/侧向时序)+ 下潜复现;
3. 四向相对移动矩阵 / 撞墙归因(wall + wall_dir_* 每方向一次)/ 脉冲冷却 invalid / 双击(阶段 3)冷却外再脉冲 / 长按帮助 / 呼吸层;
4. 战斗数学(单击格挡 ×0.2 / 缠斗纠错 / 下滑逃跑 / Boss 拒绝 / 绕位背刺 / 半血 ATK+1 / 自动喝药);
5. 死亡归因真话 + 遗产自动购置 + 结算 critical 链(在途手势全 busy)+ 菜单(左右滑换项 / 单击确认 / 重开二次确认);
6. 断点复述(stop 自动存档 / continue_ask / 复述链 / 右滑新周目 meta 保留);
7. 迷茫接管三级(静默 40s 起 L1 目标句 → 乱撞 L2 sys_only_* 只放行 → 连续无效 L3 演示+交回;进展事件清零重来);
8. 屏蔽 voice 资产流程不卡死(_sayFlow/_drillChain 兜底);
9. L1..L7 全网格回声一致性 + 贴身即接战;
10. 管道契约(stateName / invalidHint / PIPELINE_OPTS = { swipeCore, longpressCore });
11. §6.2 乱点机器人(150 步):无声操作 0 / 打断失败 0(防连点 busy 回执不计——§4.2 刻意抑制且已有可听反馈)/ 被动原句重复 0 / 首次操作 ≤10s / 首次成功 ≤20s。
- 资产审计:静态引用 192 id 全量双格式(ogg+m4a)齐全;新句以 PENDING_VOICE 35 条虚拟资产
模拟(照常播放可断言),主会话生成落地后清空清单即自动转实物断言(台词数断言 127 → 137,
口径见 `tools/voice_lines_26/dungeon.md`「生成与验收」)。
- 真人冷启动:待测。
## 11. 实现接线(引擎六模块,对齐 escape 参考)
- **装配**(game.js 构造器 + index.html):`this.receipts / this.floor / this.confusion /
this.invalidHints(= new InvalidHints().bind(floor)) / this.ladder` 全部暴露;
`audio.setRateBoostEnabled(false)`(长按=帮助是核心手势,不触发旁白快进)。
`get PIPELINE_OPTS() = { swipeCore: true, longpressCore: true }`;index.html 照 escape:
`makeInputPipeline({ floor, confusion, receipts, invalidHints, game, ladder, opts })` +
`attachGestures(root, pipe, { doubleTapWindow: 0 })`。
- **onGesture 收口**:返回 `'ok' | 'hold' | 'invalid' | 'silent-ok'`(移动=脚步声
silent-ok;撞墙/冷却/步骤外=invalid;转身(玩家回合)=hold 不消耗回合)。
`stateName()` → title/drill/explore/battle/tel/settle/menu/descend;`invalidHint(state, g)`
→ inv_pulse / inv_title / inv_settle / inv_drill / inv_tel / menu_help / locked=null
(四向+长按均核心,无锁态)。
- **floor 状态流**:探索/教学自由步 `floor.enterAction()`(玩法声即状态);描述/归因/
帮助链走 `floor.speak`(`_sayContent`,播完回 ACTION 或等待点);流程链 `_sayFlow`
(自然播完或超时先到先推进,资产缺失不卡死);教学链 `_drillChain`(arm 有界兜底)。
等待点 `WaitPoint`:drill0..7 / title / battle / settle / menu,全配 full/short/
silenceMs/expected/onDemo(§5 表)。
- **confusion**:record 由管道喂(新手门之前,锁定手势也喂信号);探索态静默喂信号
40s 首次、每 8s 再喂(窗口内两记静默可累计权重 2);`onConfused → 等待点 takeover
(wp.takeover)`,探索态/教学态走游戏侧 `_takeover / _drillTakeover`(三级,台账去重)。
- **novice 阶梯**:D4 拾金币 = `ladder.onGoal()`(阶段 1;返回的 up 教学对四向玩法
无意义,与 escape 同样忽略);死亡/通关 = `ladder.onGameDone()`(阶段 2/3),返回的
teachLine(**已带 voice/ 前缀,2026-10 novice.js 修复后不要再加前缀**)接在结算链尾。
- **存档**:schema 1、summary `第{floor}层 · {血量档} · 金币{meta.gold} · 遗产{n}/5`
字段不动;拾取/战斗/下潜/死亡/菜单/stop() 自动存。
## 12. 偏差记录(与 26 号/16 号的落地差异及理由)
1. **D1 巩固步等待点不即时挂**:设计单口径「巩固等待立即挂上」,实现改为回声组落地
(+1.3s)后播规则链、链播完才 yieldTurn——管道契约是「onGesture 返回时 floor 在
YOUR_TURN 即视为完成当前等待点」,手势内同步 yieldTurn 的新等待点会被当已完成摘掉,
随后 invalid 解释链播完无人恢复话轮(floor 停 BUSY 吞手势,D1 卡死根因)。0~1.3s
窗口由回声组内容音覆盖;窗口内抢先单击照常成立(case 1 直答)。
2. **孤儿 BUSY 看门狗**(`_healOrphanBusy`,800ms 周期,仅 stop() 停):引擎
InvalidHints 解释链的 onDone 只在「挂着等待点」时恢复话轮,探索/教学自由步没有
等待点,链播完 floor 停 BUSY——管道在 floor.handleInput 就拦下 busy,游戏层永远
收不到手势。看门狗按当前态拉回规范态(探索=ACTION / 答题步=重挂等待点 / 前摇=
ACTION / TITLE·SETTLE=重交话轮)。**引擎缺口**:InvalidHints/feedback 链结束后
的状态恢复依赖调用方,建议引擎侧统一兜底(escape 用无条件 resumeTurn 达成同效)。
3. **教学态接管不走 wp.takeover**:其 L2(restrictLine/short 兜底)无 30s 台账检查,
会把刚说过的指令句原样重播,违反「被动原句重复 = 0」红线;改走游戏侧
`_drillTakeover`(L1 短句 → L2 重教指令句 → L3 `_drillDemo`,全过台账)。
**引擎缺口**:WaitPoint.takeover(2) 建议补台账去重。
4. **战斗玩家回合转身返回 hold**:转身不消耗回合,hold 让管道 settleWaitPoint(false)
保住战斗等待点(血量/敌方位长按链播完要回它;silent-ok 会把等待点当已完成摘掉)。
5. **D4 推进走 1.6s 有界定时**:evt_coin 链被乱手势打断也不丢 onGoal/进 D5(对齐
_sayFlow 兜底语义,步骤号先守恒)。
6. **tut_praise 不进 D4 链**:原则 9 逐字重复禁令(D2/D3 已各一次「对。」),D4 直接
布置任务 tut_coin。
7. **遭遇自动面对落在 _encounter**:走进怪格/相邻扑来先把玩家面朝转到怪的方向
(报名「就在你面前」即真话),保证 D6 单击格挡教学确定性(16 号 §5「遭遇自动面对」)。
8. **btl_no_turn 补 onDone**:链播完回战斗等待点(无 onDone 会孤儿 BUSY,见偏差 2)。
9. **教学不存步骤**:断点回来教学未完从头再练(16 号 §10 口径),DRILL 步骤不进存档。