Files
SoundGame/docs/design/25-escape-map-flow.md
T
xsl 963dfa1cc8 fix(escape): 暗室状态引导方案落地(25号策划文档 B1-B8 → F1-F9 实施)
策划轮:25-escape-map-flow.md as-built地图/玩家流程/信息账本+盲区清单(S1-S7)+方案B1-B8
实施(玩法零改动,只加状态引导层):
- F1 撞墙方位句「左边是墙」(wall_dir_*×4,同向10s限频)
- F2 目标句并入方位「铁柜,在右面的房间」(gd_*_dir×3)
- F3 无进展150s活跃计时器+gd_prog_1..3进度重述(只重述不泄题,专治活跃迷路玩家——修卡关计时从「无进展」退化为「无操作」的既定偏差)
- F4 双击兜底直通L2降级L1(不再白送答案)
- F5 空房首访描述(4空房可区分)
- F7 busy窗口手势缓存补判(原静默吞手势)
- F8 结算链单击守卫(time_band/again完整到达)
- F9 跳过引导补播目标句
- F6 空房方位特征句按25号裁定留档后续轮
新增语音10句×2性别(flock串行QC无离群,ASR逐字正确)
验证:escape playtest 235/235(+85新断言)+ guide 132/132 + 终检全绿 + 14游戏回归零回退
live复跑10.4分钟通关:零截断,S1-S7盲区逐条核销
档案 research/escape-state/round1-2;01号文档追加修订记录R1
2026-09-15 00:37:41 +08:00

646 lines
50 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.
# 25 · 《暗室》地图与玩家流程文档 + 状态引导改进方案
> **as-built 声明**:本文档为「以代码为准」的现行实现文档化,权威来源是
> `web/escape/game.js`(837 行,R2 重设计 + round1~round4 历次 live 修复),
> 与 01 号设计文档(01-escape-redesign.md)对账,**凡不一致处以代码为准**,
> 差异全录于 §1.5。语音 id 全集与全文以 `tools/gen_voice_dual.py` 的
> `ESCAPE_LINES`(80 句)为准。
>
> 阶段三「状态引导改进方案」的取证材料在 `research/escape-state/round1/`
> (mock 转写 + live 真机一局 + P1/P3 人设评审)。方案只产出设计,未实施。
>
> 轮次:escape-state round1(2026-09-14)。靶问题(用户真机反馈):
> **玩家不知道自己的状态**——不知道自己在哪、不知道目标、不知道进度。
---
# 阶段一 · as-built 文档化
## 1. 地图章
### 1.1 房间总图(3x3,坐标 x 0西→2东,y 0北→2南)
```
x=0(西) x=1 x=2(东)
y=0 ┌───────────┬───────────┬───────────┐
(北) │ (0,0) │ (1,0) │ (2,0) │
│ 电箱房 │ 空房·风 │ 空房·嗡鸣 │
│ amb_power │ amb_wind │ amb_hum │
├───────────┼───────────┼───────────┤
y=1 │ (0,1) │ (1,1) │ (2,1) │
│ 空房·嗡鸣 │ 起点 │ 铁柜房 │
│ amb_hum │ amb_drip │ amb_cabinet│
├───────────┼───────────┼───────────┤
y=2 │ (0,2) │ (1,2) │ (2,2) │
(南) │ 储物间 │ 空房·风 │ 大门 │
│ amb_storage│ amb_wind │ amb_gate │
└───────────┴───────────┴───────────┘
(通电后 amb_gate → amb_gate_live)
```
- **连通关系**:全部相邻房间四向连通——`_move()` 只做 0..2 边界检查
(game.js:394-417),内部 12 条边全部可走。**全图无锁门、无单向通道**,
唯一"锁"是动作门(双击使用物品的前置条件),不是空间门。
- **撞墙边界**:仅外圈 12 段墙。撞墙 = 目标坐标出界 → `sfx/wall` 闷响 +
`error` 震 + `voice/wall`「那边是墙,换个方向。」(每次撞都播,
game.js:397-404)。
- **起点**:(1,1) 正中央,检查语音 `start_room`「你站在设施中央的房间里」
——"中央"是全图**唯一一次**给出的绝对方位锚点(见 §3 盲区 S1)。
### 1.2 每房间资产与点位表
| 房间 | 坐标 | 环境音 id | 进入描述(仅首到) | 单击检查语音(状态相关) | 双击使用 |
|---|---|---|---|---|---|
| 电箱房 | (0,0) | `sfx/amb_power` | `room_power`「这个房间的墙上,有一只电箱。」 | 未修 `inspect_box_dead` / 已修 `box_fixed` | hasFuse → `fix_box`(修电) |
| 空房 | (1,0) | `sfx/amb_wind` | 无 | `empty_1`「空空荡荡,只有你自己的回声。」 | 兜底 |
| 空房 | (2,0) | `sfx/amb_hum` | 无 | `empty_3`「除了远处的水滴声,这里什么都没有。」 | 兜底 |
| 空房 | (0,1) | `sfx/amb_hum` | 无 | `empty_2`「摸到的只有冰冷粗糙的墙壁。」 | 兜底 |
| 起点 | (1,1) | `sfx/amb_drip` | 无 | `start_room`(中央房间+操作总览) | 兜底 |
| 铁柜房 | (2,1) | `sfx/amb_cabinet` | `room_cabinet`「这个房间里,立着一个铁柜。」 | 未开 `inspect_cabinet_locked` / 已开 `empty_1` | hasKey → `unlock_cabinet`(得保险丝) |
| 储物间 | (0,2) | `sfx/amb_storage` | `room_storage`「货架上堆满杂物,角落里传来水滴声。」 | 有钥匙且柜未开 → **拾取钥匙** `found_key`;否则 `empty_2` | 兜底 |
| 空房 | (1,2) | `sfx/amb_wind` | 无 | `empty_3` | 兜底 |
| 大门 | (2,2) | `sfx/amb_gate`(通电后 `amb_gate_live`) | `room_gate`「面前,是一扇沉重的铁门。」 | 未通电 `inspect_gate` / 已通电 `inspect_gate_ready` | powerOn → 逃脱结算 |
要点:
- **环境音 6 类铺 9 房**:4 间空房只有 2 种音(wind×2、hum×2)且成对角分布
((1,0)/(1,2) 风、(0,1)/(2,0) 嗡鸣)——**空房之间声学不可区分**(盲区 S5)。
- **首到描述只播一次**(`visited` 集合,game.js:112,412-416);重访关键房间
零语音,只换环境音。
- **进房间自动播环境音**(每次移动都播,game.js:429-431),单击检查也先重播
环境音再播描述(800ms 间隔让环境音先进耳朵,game.js:446-448)。
- 大门通电后环境音切换 `amb_gate_live` 是**唯一**一条"谜题状态改变可听觉感知"
的环境通道(01 号文档 §4.4 的设计意图,代码兑现)。
### 1.3 解谜链依赖图
```
[储物间 (0,2) 单击] ──得钥匙──▶ [铁柜房 (2,1) 双击] ──得保险丝──▶
[电箱房 (0,0) 双击] ──通电──▶ [大门 (2,2) 双击] ──▶ 逃脱结算
hasKey=true cabinetOpen=true powerOn=true
hasFuse=true
```
- 链上 4 个动作,每个都是"在对的房间双击",前置 = 上一环的物品/状态。
- 物品即进度:`hasKey`/`hasFuse` 为真即代表链推进到对应环;`cabinetOpen`/
`powerOn` 为真代表该环完成。`_stage()`(game.js:538-543)按此回退链判定
当前阶段:`key → cabinet → box → gate`。
- **最短通关路径**(11 次移动 + 1 单击 + 3 双击,约 2-3 分钟):
(1,1)→左(0,1)→下(0,2)【单击拾钥匙】→右(1,2)→右(2,2)→上(2,1)【双击开柜】
→左(1,1)→左(0,1)→上(0,0)【双击修电箱】→右(1,0)→右(2,0)→下(2,2)【双击逃脱】。
注意这条最短路径**恰好绕过所有空房**,且铁柜房/电箱房的方位句
(H1)在这条路径上分别给出"右面的房间"(储物间→铁柜房)与
"左面的房间"(铁柜房→电箱房)。
### 1.4 房间 → 谜题 → 状态映射表
| 阶段 `_stage()` | 判定条件 | 目标房间(L3 带路 STAGES) | L1 暗示 | L2 明示 | 进展事件(清已播级别) |
|---|---|---|---|---|---|
| `key` | `!hasKey && !cabinetOpen` | (0,2) 储物间 | `stuck_key_1` 铁锈味意象 | `stuck_key_2` 西南角储物间单击 | 拾钥匙、**首到储物间** |
| `cabinet` | `hasKey` | (2,1) 铁柜房 | `stuck_cabinet_1` 钥匙在你身上 | `stuck_cabinet_2` 东边铁柜房双击 | 开柜(得保险丝) |
| `box` | `cabinetOpen && !powerOn` | (0,0) 电箱房 | `stuck_box_1` 电流声意象 | `stuck_box_2` 西北角电箱房双击 | 修电箱 |
| `gate` | `powerOn` | (2,2) 大门 | `stuck_gate_1` 电已通 | `stuck_gate_2` 东南角大门双击 | 逃脱 |
- 进展事件调 `_progressEvent()`:清空 `_stuckShown`,下段静默期从 L1 重新分级
(game.js:578-582)。
- 代码把**首到储物间**也算进展事件(KEY_ROOMS 含 (0,2)),01 号文档 §3.1
只列了铁柜房/电箱房/大门——差异见 §1.5-③。
### 1.5 代码 vs 设计文档差异清单(以代码为准)
| # | 01 号设计文档 | 现行代码 | 性质 |
|---|---|---|---|
| ① | §3.1/§3.2 卡关计时"以上一次**进展事件**起算"(无进展 60/120/180s) | 按"**无操作时长**"计时:PLAY 阶段任何有效手势重置计时(game.js:26-31,160-164,589-612) | round1-live 修复(原机关枪式报答案+顶语音),设计意图被反向修订 |
| ② | §2.1 G0「单击继续」+ CONFIRM 震 | intro 播完 **1.2s 无操作自动推进 G1**(game.js:205-211);G0 单击仍有效 | round2-live 修复(intro 后死寂 17~91s) |
| ③ | §3.1 进展事件列"首到铁柜房/电箱房/大门" | 首到**储物间**同样计进展(KEY_ROOMS 四房间统一处理) | 代码超集 |
| ④ | §2.1「引导期间任何步骤可下滑跳过」 | 仅 G0/G3/G5/G6 可下滑跳过;G1/G2/G4 下滑=作答(game.js:239-245) | 13 号文档裁定,round2-live 修复教学链断裂 |
| ⑤ | §3.2「双击无目标兜底:先播背包,再播 L2」 | 在**关键房间**内播 `use_fail` 而非背包;非关键房才播背包(game.js:519-527) | 代码细化 |
| ⑥ | 14 号文档 v1 裁定 5/§2:「连续撞墙 ≥2 次 → full 档卡关三级阈值提前一级」 | **未落地**:game.js 无任何撞墙连击/阈值提前逻辑(已核实无 wallStreak 类状态) | 文档先行、代码缺失,以代码为准(=无此功能) |
| ⑦ | §2.1 G2 反馈含"未撞墙:脚步 + SWIPE_OK"分支 | 引导期位置固定,**滑动即撞墙反馈**(每滑必 wall 闷响+error 震+wall 语音),无"未撞墙"分支(game.js:264-272) | 设计草案分支不存在 |
| ⑧ | §2.1 G6 反馈「兜底播报背包 + CONFIRM 震」 | G6 双击答对播 `inv_key`+`g6_done`+`g7_goal` 链(无单独 confirm 震,靠引擎双击内置 confirm) | 等价 |
| ⑨ | (01 号文档无 REOPEN 相) | 存在 REOPEN 二周目阶段:上滑重听引导/单击直接开始(game.js:54-55,143-151) | 01 号文档 §2.1 末段提到过一次,代码为完整实现 |
| ⑩ | §3.2 L1 附"语音提示前播'需要帮助吗?'" | `stuck_ask` 仅 L1 前插播,L2/L3 无(game.js:607-611) | 一致(此处列出防误扩) |
| ⑪ | 01 号文档 §4.2「环境音扩到 6 种」 | 实际 8 个环境音资产 6 类(含 `amb_gate_live` 通电变体) | 一致(超集) |
另注:14 号文档 round2 F2.3「撞墙接播 guide_blunt_<dir> 方位句」曾被落地、
后被 round1-live **回退**为只播 `voice/wall` 极简句(game.js:400-403 注释),
教学腔方位句仅限引导期 G3 纠错路径——这条历史是阶段三 B1 方案的前车之鉴
(方位信息要给,但措辞不能再走教学腔)。
---
## 2. 玩家流程章
### 2.1 全局状态机
```
┌────────────── 首次开局 ──────────────┐
▼ │
start ─▶ REOPEN(二周目才有:上滑重听引导/单击直接开始)
│ 首局自动
▼
GUIDE(脚本化教学,玩家手势推进,G0→G7)
│ G6 双击答对 → g7_goal 链播完 _finishGuide
│ G0/G3/G5/G6 下滑跳过 → guide_skip → PLAY
▼
PLAY(自由探索 + 卡关三级提示计时器;谜题链阶段 key→cabinet→box→gate)
│ 大门双击(powerOn)→ _escape()
▼
ENDING(结算链 escape→time_band→again;单击重开 / 下滑菜单)
│ 下滑
▼
MENU(模态:单击循环选项 / 双击执行 / 上滑帮助 / 下滑关闭)
```
- **会话令牌** `_session`:重开/跳过/回大厅时递增,使旧计时器与语音链回调
全部失效(game.js:98,826-831)。
- **语音纪律基础设施**:待播队列 `_pendingVoice`(kind=`stuck` 卡关提示 /
`describe` 补发检查),只在当前语音自然播完后播出,**绝不顶句**;待播期间
玩家任何操作即作废卡关提示(未播级别不计入已播,game.js:125,783-810)。
- 单击跳句(12 号文档):可跳句播放中单击 = `skipVoice()` 跳过本句 + 补发检查
(环境音即时重播、描述进待播队列);不可跳句(引导 G0-G7、关键获得链、
L3 带路)单击 = 忽略(game.js:377-388,445)。
### 2.2 引导子状态 G0-G7(GUIDE 阶段)
| 步 | 语音(全文见 §3 账本) | 等待手势 | 答对反馈 | 答错(3 次给 blunt+warning 震) |
|---|---|---|---|---|
| G0 | `g0_intro`(剧情) | 单击;**1.2s 无操作自动推进** | g1_move+skip_hint | intro 播放中滑动=忽略;空档滑动=`guide_offpath`「先跟我走。」 |
| G1 | `g1_move`「朝任意方向滑一下」 | 任意滑动(下滑也算作答,不可跳过) | step + `g1_done` + `g2_wall` | `guide_wrong_gesture` |
| G2 | `g2_wall`「再滑两下,试试碰碰墙」 | 滑动×2(每滑必播 wall 闷响+error 震+`wall`) | 满 2 次 → `wall`+`g3_left` | 非滑动 = wrong_gesture |
| G3 | `g3_left`「向左滑。」 | 左滑(→(0,1)) | step + `g4_down` | 其他滑动 = `guide_wrong_left` |
| G4 | `g4_down`「再向下滑。」 | 下滑(→(0,2) 储物间) | step + `amb_storage` + `g5_inspect` | 其他滑动 = `guide_wrong_down` |
| G5 | `g5_inspect`「单击检查」 | 单击 → **拾钥匙** | pickup + `g5_key` | 非单击 = wrong_gesture |
| G6 | `g6_doubletap`「双击试试」 | 双击 | `inv_key`+`g6_done`+`g7_goal` | 非双击 = wrong_gesture |
| G7 | `g7_goal`「找到能用这把钥匙打开的东西…」 | (无,链播完自动进 PLAY) | — | 手势忽略 |
- 引导期位置是脚本写死的:(1,1)→(0,1)→(0,2),与真实地图一致;G2 的"撞墙"
是伪撞墙(位置并未移动)。
- `_guideBusy` 推进语音播放中**忽略一切非跳过分手势**——这是阶段四 live 局
抓到的事故点(盲区 S6):G3 答案在 busy 窗口内被吞,玩家紧接着的下滑被
当成"跳过引导"。
### 2.3 手势语义总表(逐阶段)
| 手势 | REOPEN | GUIDE | PLAY | ENDING | MENU |
|---|---|---|---|---|---|
| 上滑 | 重听引导 | 下滑跳过仅限 G0/G3/G5/G6,**上滑无跳过语义**;其余步上滑=答错/忽略 | **移动(dy=-1)** | 忽略 | 播 `help_ops` 操作说明 |
| 下滑 | 忽略 | G0/G3/G5/G6=跳过引导;G1/G2/G4=作答 | **移动(dy=+1)** | 打开菜单 | 关闭菜单 |
| 左/右滑 | 忽略 | 按步骤判定(G3 左滑=答案) | **移动(dx=∓1)** | 忽略 | 忽略 |
| 单击 | 直接开始(stopVoice+进 PLAY) | 按步骤判定(G0/G5=推进/拾取) | voice 播放中=跳句+补检查;无 voice=检查 | **重开一局** | 循环切换菜单项(播选项名) |
| 双击 | 忽略 | 按步骤判定(G6=答案) | 使用物品:有目标=解谜动作;无目标=**兜底**(背包或 use_fail + `stuck_<stage>_2` L2 明示) | 忽略 | 执行当前项 |
| 长按 | 引擎:旁白快进(后松开) | 同左 | 同左 | 同左 | 同左 |
- escape 的手势映射**全面偏离全局默认**(四向滑=移动、双击=使用而非重听),
00 号文档 §5.3 豁免表已备案;偏离内容在引导 G1/G2/G5/G6 逐一手把手教过。
- **PLAY 阶段没有菜单、没有上滑帮助**(00 §5.3:帮助入口 = 双击兜底 +
卡关计时器)——"主动查状态"在 escape 里只有双击兜底一个口子,而它直通
L2 明示(盲区 S4)。
### 2.4 卡关三级提示(PLAY 阶段自动触发)
| 级别 | 触发(现行代码:无操作时长) | 内容 | 附带 | 重复规则 |
|---|---|---|---|---|
| L1 暗示 | 60s 无任何手势 | `stuck_ask`「需要帮助吗?」+ `stuck_<stage>_1` 意象句 | warning 震 | 每进展段 1 次 |
| L2 明示 | 120s | `stuck_<stage>_2` 直接点明房间+动作 | warning 震 | 每进展段 1 次 |
| L3 带路 | 180s | `stuck_<stage>_3` + `nav_*` 按当前坐标算路(每轴一句,最多两句,先东西后南北) | warning 震 | 此后每 60s 无操作重复,按当时坐标重算 |
- **队列与作废**:提示只进待播队列,等当前语音播完;待播期间玩家任何操作
即作废(`_dropPendingStuck`,game.js:803-810)。→ 活跃玩家可能连 L1 都
听不到,但它本来就不该听到(计时按无操作)。
- **双击兜底 = 另一条 L2 通道**(game.js:519-527):在关键房间播 use_fail、
在非关键房播背包,**都接 `stuck_<stage>_2`**——玩家主动探状态会被直接
喂答案(阶段四 live 两次实录,见盲区 S4)。
- 14 号文档裁定的"连续撞墙 ≥2 次阈值提前一级"未落地(§1.5-⑥)。
### 2.5 关键路径与分支
**主路径(引导完整走)**:REOPEN(跳过)→ G0 intro → G1 滑一步 → G2 撞墙×2
→ G3 左滑 → G4 下滑进储物间 → G5 单击拾钥匙 → G6 双击 → G7 目标陈述 →
PLAY(已持钥匙,阶段=cabinet)→ 自由探索开柜→修电箱→开门。
引导结束瞬间玩家已知:移动方式、撞墙反馈、检查、双击、背包里有钥匙、
下一步目标 = 找锁。**这是全游戏信息最充裕的时刻。**
**分支与边界路径**:
1. **跳过引导**(G0/G3/G5/G6 下滑):`guide_skip`「跳过引导,直接开始自由
探索。」→ 直接 PLAY,**不持钥匙、没听过 g7_goal**——开局只有
`start_room` 一句操作总览,目标维度完全空白(盲区 S4,live 实录)。
2. **二周目** REOPEN:上滑=重听引导;单击=直接开始(播 start_room)。
3. **撞墙**:外圈 12 段;反馈 wall 闷响+error 震+`wall` 极简句。无方位
信息(教学腔方位句已被 round1-live 回退,§1.5 注)。
4. **双击兜底**:非关键房 = 背包句 + L2;关键房 = use_fail + L2。
(live 实录:柜已开后再双击铁柜房,听到 use_fail + `stuck_box_2`——
玩家并没有卡关,答案句白送。)
5. **空房**:进入只播环境音(与邻房可能同音),单击才播 empty_*。
6. **重访关键房间**:零语音(首到描述只一次)。
7. **结算**:escape 链(不可跳)播完淡入 bgm/menu;单击=立即重开
(`_restart` 不管结算链是否播完——live 实录结算链被单击截断,
time_band/again 未播,见盲区 S7);下滑=菜单(重开/回大厅/返回)。
8. **菜单**:暂停一切(escape 无局内计时器,影响仅限语音队列)。
### 2.6 各阶段玩家可获得的信息渠道
| 阶段 | 语音 | earcon/音效 | 震动 | 环境音 | BGM |
|---|---|---|---|---|---|
| REOPEN | reopen_prompt | turn cue | —(等抉择) | — | bgm/drone |
| GUIDE | 逐句指令+纠错(不可跳) | step/wall/pickup | error(答错)/warning(3 连错)/pickup(拾钥匙)/引擎内置 swipe/confirm | amb_storage(G4 到储物间) | bgm/drone |
| PLAY | 首到房间描述/检查句/进展链(found_key→gd_room_*)/卡关三级/双击兜底 | step/wall/pickup/unlock/door_creak/spark/power_on/gate_open/win_chime | error(撞墙/无效使用)/success(开柜修电/逃脱)/pickup(拾钥匙)/warning(卡关提示)/引擎 swipe | 每房进来自动播,单击可重听 | bgm/drone |
| ENDING | escape+time_band+again(不可跳) | gate_open(后移 2.6s)+win_chime | success | — | drone 淡出→bgm/menu 淡入 |
| MENU | menu_open+选项名/help_ops | — | confirm(双击执行) | — | bgm/menu |
信息渠道的四division 铁律(14 号裁定 7):语音说"你要做什么"、earcon 说
"哪类事/哪个状态"、BGM 说"宏观状态"、震动说"刚才那下对不对"。escape 的
earcon 仅向导层 8 签名(explore/menu/settle/turn)+ 内容音效;**游戏层没有
任何自研 earcon**——方位/进度类信息目前没有 earcon 通道可用(新增需走
14 号文档的引擎词表修订,或改用语音/环境音通道)。
---
## 3. 信息账本(语音 id 全集三类归档 + 盲区清单)
### 3.1 语音 id 全集(80 句,按「状态告知 / 操作反馈 / 叙事·教学」三类)
**A 类 · 状态告知**(回答"我在哪/我有什么/进展如何/系统处于什么状态"):
| id | 全文(摘要) | 覆盖的状态维度 |
|---|---|---|
| `start_room` | 你站在设施中央的房间里。滑动移动,单击检查,双击使用物品。 | 绝对方位(唯一一次)+ 操作总览 |
| `room_storage` / `room_cabinet` / `room_power` / `room_gate` | 储物间/铁柜/电箱/大门 首到描述 | 房间身份(仅首到) |
| `empty_1/2/3` | 空空荡荡/冰冷墙壁/远处水滴 | 空房身份(仅单击检查时) |
| `inspect_cabinet_locked` / `inspect_box_dead` / `box_fixed` / `inspect_gate` / `inspect_gate_ready` | 各互动物状态描述 | 谜题对象状态 |
| `inv_empty/key/fuse/both` | 背包四档 | 物品持有 |
| `wall` | 那边是墙,换个方向。 | 边界负信息(无方位) |
| `stuck_ask` + `stuck_<stage>_1` ×4 | 需要帮助吗?+ 意象暗示 | 目标(L1 级,意象) |
| `stuck_<stage>_2` ×4 | 点明房间+动作 | 目标(L2 级,明示) |
| `stuck_<stage>_3` ×4 + `nav_udlr` ×4 | 带路 | 目标+路线(L3 级) |
| `gd_next_fuse` | 保险丝到手。接下来,去找电箱。 | 目标(进展事件当下) |
| `gd_room_l/r/u/d` | 左/右/上/下面的房间。 | 目标相对方位(无目标名) |
| `time_band1-4` | 用时四档整句 | 结算用时 |
| `use_fail` | 这里没有用得上东西的地方。 | 无效使用(状态否定) |
| `menu_open/restart/home/back` + `help_ops` + `reopen_prompt` | 菜单与帮助 | 系统状态 |
| `box_fixed`(重复列出处归入检查) | — | — |
**B 类 · 操作反馈**(回答"刚才那下对不对/成了没"):
| id | 全文(摘要) | 触发 |
|---|---|---|
| `g1_done` / `g5_key` / `g6_done` | 引导答对确认 | 引导 G1/G5/G6 |
| `found_key` | 摸到钥匙(长句,不可跳) | 拾取 |
| `unlock_cabinet` | 开柜得保险丝(不可跳) | 解谜成功 1 |
| `fix_box` | 装保险丝通电(不可跳) | 解谜成功 2 |
| `escape` | 逃脱结算(不可跳) | 通关 |
| `replay` | 黑暗再次合拢(重开) | 重开 |
| `again` | 单击再来一次(结算链尾) | 结算 |
| `use_fail`(兼 A 类) | 无效使用 | 双击无目标 |
| `guide_wrong_up/down/left/right/gesture` ×5 | 不是这个方向/操作 | 引导答错 |
| `guide_blunt_up/down/left/right` ×4 | 直白重述(3 连错) | 引导连续答错 |
| `guide_offpath` | 先跟我走。 | 引导期越轨滑动 |
**C 类 · 叙事·教学**(铺氛围/教手势,不承担状态):
| id | 全文(摘要) |
|---|---|
| `g0_intro` | 黑暗。你在废弃的地下设施里醒来…… |
| `g1_move` / `g2_wall` / `g3_left` / `g4_down` / `g5_inspect` / `g6_doubletap` / `g7_goal` | 引导指令六步 + 目标陈述 |
| `skip_hint` / `guide_skip` | 跳过旁白提示 / 跳过确认 |
| `reopen_prompt`(兼 A) | 二周目抉择 |
### 3.2 状态维度 × 信息渠道矩阵(空白格 = 盲区)
| 状态维度 | 语音 | 环境音 | 震动 | earcon/BGM | 结论 |
|---|---|---|---|---|---|
| 绝对方位(我在图上的哪格) | 仅开局 `start_room` 一句"中央";L2 角落词 | 无 | 无 | 无 | **盲区 S1** |
| 相对方位(目标在我哪边) | 仅进展链当下(gd_room)+ L3 带路 | 无 | 无 | 无 | 半盲区(时机太窄) |
| 撞墙方位(哪面不通) | 无(教学腔已被回退) | 无 | 无(撞墙=error 通式) | 无 | **盲区 S1b** |
| 已探索地图(我来过这吗) | 无(首到描述不重复,重访零语音) | 无 | 无 | 无 | **盲区 S2** |
| 空房身份(这屋是哪间) | 仅单击检查时 empty_*,且文本无方位 | 2 种音铺 4 房,不可区分 | 无 | 无 | **盲区 S5** |
| 谜题链全局进度(几环完成/还剩几环) | 无主动渠道;inv_* 只报物品 | 仅大门通电有 amb_gate_live | 无 | 无 | **盲区 S3** |
| 当前目标(L0 轻量重述) | 仅引导尾 g7_goal(跳过即永久丢失)+ 进展链当下一句 | 无 | 无 | 无 | **盲区 S4** |
| 主动查询入口 | 双击兜底 = 直通 L2 明示(泄漏) | — | — | — | **盲区 S4b(机制错配)** |
| 物品持有 | inv_* ✓(双击兜底时) | 无 | 拾取时 pickup ✓ | 无 | 基本覆盖 |
| 互动物状态(锁/电/门) | 检查语音 ✓ | 大门通电 ✓ | 解谜 success ✓ | 无 | 覆盖(需玩家记得检查) |
| 卡关计时器状态 | 无(提示到点才出现) | — | — | — | 设计内,不动 |
| 引导 busy 窗口(我手势算数吗) | 无 | 无 | 仅引擎 [10] | 无 | **盲区 S6**(事故级) |
| 结算完整性(我通关用时/重开确认) | 结算链可被单击无条件截断 | — | — | — | **盲区 S7** |
> S1/S2/S3/S4/S5 是用户真机反馈"不知道自己在哪/目标/进度"的精确落点;
> S6/S7 是 live 局新抓到的机制级盲区。S1b(撞墙无方位)是 P1 评审的死线项。
---
# 阶段三 · 状态引导改进方案(escape-state round1)
## 4.0 取证摘要
- **mock 转写**(`research/escape-state/round1/transcript-mock.md`):引导完整
路径(G0-G7 全覆盖),证明"引导结束时信息最充裕";此后唯一信息增量来自
进展链(mock 行 107-108、126:gd_next_fuse + 方位句)。
- **live 真机一局**(`research/escape-state/round1/live-transcript.md`,女声响
full 档,约 14 分钟,打到通关并重开):52 个语音段,55 条判定行
(OK 35 / SFX_BGM_ONLY 18 / TRUNCATED 2;两条 TRUNCATED 均有玩家手势
归因,非资产缺陷)。
关键事件:引导期 busy 吞手势(行 30-35)、误触下滑跳过引导(行 61-74)、
双击兜底两次直通 L2 明示(行 161-178、230-243)、结算链被单击截断
(行 253-275)、L1/L2/L3 三级按时触发且 L3 每 60s 按坐标重算重播
(行 75-86、288-294、295-346)。
- **人设评审**:P1 视障(`p1-blind.md`)+ P3 白领(`p3-office.md`),双席对
盲区清单达成一致。
设计约束(本方案全部条目的共同红线):
- **00 号宪法**:整句预生成、禁运行时拼接;数字信息分档整句;震动语义按
00 §2.2 既有 10 震型、不新增语义震;手势映射零改动(上滑仍是移动)。
- **14 号向导层**:三档语义(full=earcon+语音+震 / standard=耳标+震 / off=
全静);新 earcon 不进本方案(引擎 8 签名词表修订成本过高,方位与进度信息
一律走语音通道,游戏反馈层全档位,与 gd_next_fuse/gd_room 先例同级)。
- **归因真话律**:进度句报的完成数必须等于真实状态机(hasKey/cabinetOpen/
powerOn 派生),不许多报少报。
- **难度不降低**:所有新语音只报"状态与方向",不报"做法";L2/L3 的到达不因
新通道提前或变频繁。
- **节奏不破**:单局 15-30 分钟的宏观节奏由谜题链决定,新播报全部挂在
"进展事件 / 首次 / 限频"上,不给活跃玩家增加语音负担。
## 4.1 方案条目
### B1 · 撞墙报方位(陈述式,非教学腔)
- **问题(证据)**:撞墙反馈只有 `wall`「那边是墙,换个方向。」——无方位。
live 行 40-55 连撞两次地图零增量;P1「每次错都学不到东西」、P3「盲操的
试错成本全是沉没成本」。14 号 F2.3 曾用 `guide_blunt_*`「手指贴着屏幕,向
X 推一下」补方位,被 round1-live 以"教学腔中后期出戏"回退——回退掉的是
措辞,方位信息本身没有错杀的理由。
- **目标状态**:玩家每次撞墙都能建立一个"此方向不通"的地图标记,措辞是
世界内陈述句而非操作教学。
- **方案**:新增 4 句整句(游戏反馈层全档位,撞墙既有链路 `sfx/wall`+error
震+`wall` 播完后接播,被下一次移动打断则自然作废——复用 F2.3 原接线模式):
- `wall_dir_l`「左边是墙。」 / `wall_dir_r`「右边是墙。」 /
`wall_dir_u`「上边是墙。」 / `wall_dir_d`「下边是墙。」
- 触发:PLAY 阶段撞墙、`voice/wall` 播完后、同方向 10s 内限频 1 次
(防贴墙连撞连播)。**仅报方位事实,不说"换个方向/推一下"**——后半句
现有 `wall` 已覆盖。
- 方位 = 滑动方向(escape 移动坐标系),按真实撞墙方向取句,归因真话。
- 震动:沿用 error,不新增。
- **宪法条款**:00 §2.2(既有 error 震型);00 §3.2(整句预生成);14 裁定 7
(语音报事实、耳标报类别——本句是纯语音通道);14 F2.3 先例(接线模式)。
- **拥挤评估**:撞墙在 3x3 图中的理论上限是外圈 12 段;限频后一局新增语音
≤6-8 句短句(各 ≤5 字),对 15-30 分钟单局节奏影响可忽略。风险:贴墙探索
型玩家会听到成串方位句——10s 同向限频 + 移动即作废已把上限压住。
### B2 · 目标方位句并入目标名(H1 的措辞升级)
- **问题(证据)**:H1 方位句只有方向词无目标名:「右面的房间。」(live 行
112-116、156-160、201-208)。P3:嘈杂环境 1.3s 短句被吃掉后只剩"……的
房间"(行 206-208 ASR sim 0.75 实证);P1:"右面是哪?我赌不起"。三处
触发点(found_key / gd_next_fuse / fix_box)在谜题链正常路径上位置固定,
方位可枚举。
- **目标状态**:进展链尾玩家听到"目标名 + 方位"一体句,单句即可行动。
- **方案**:新增 3 句整句替换三处 `gd_room_*` 接播(接线点不变,全档位):
- found_key 链尾 → `gd_cabinet_dir`「铁柜,在右面的房间。」(储物间 (0,2)
→ 铁柜房 (2,1),轴序先东西,dx=+2 固定为右)
- unlock_cabinet/gd_next_fuse 链尾 → `gd_box_dir`「电箱,在左面的房间。」
(铁柜房 (2,1) → 电箱房 (0,0),dx=−2 固定为左)
- fix_box 链尾 → `gd_gate_dir`「大门,在右面的房间。」(电箱房 (0,0) →
大门 (2,2),dx=+2 固定为右)
- 原 `gd_room_l/r/u/d` 4 句保留不删(guide.test.mjs 的 H1 方向映射断言与
playtest 断言引用它们,删除触碰断言零改动红线);新句并行接线,仅替换
三处触发点。方位若在异常路径上计算结果与枚举不符(理论上不存在——三处
触发点坐标固定),兜底回落 `gd_room_*`。
- **宪法条款**:00 §3.2(整句);归因真话(方位按房间图枚举核对过:三处
触发点→目标均同排/跨排但轴序先东西,方向唯一);14 H1 裁定(接线模式
原样,只改词)。
- **拥挤评估**:3 处替换、0 处新增播出次数;句长 +4 字。对节奏零影响。
附带收益:修了 P3 的"关键信息听不清"——信息密度翻倍,重听价值也翻倍。
### B3 · 无进展计时器 + L0 进度重述(活跃迷路通道)
- **问题(证据)**:卡关计时按"无操作"(§1.5-①,round1-live 修订),**活跃
但无进展的玩家永远不会收到任何状态输入**——live 局我在解谜链外探路时
(行 87-94、179-220 各段 transit)游戏只给我脚步和环境音。01 号文档原始
设计是"无进展起算",代码把"无进展"改成"无操作"后,"活跃迷路"这个状态
从提示体系里消失了。用户反馈"不知道进度"正是这个空洞。
- **目标状态**:玩家长时间有操作但零进展时,收到一条**不泄题**的进度重述,
知道自己已完成几环、目标仍是什么,而不是答案。
- **方案**:
- 新增独立计时:以 `_progressEvent()` 起算的"无进展时长"(与现有无操作
计时器并存,互不复位)。触发点:无进展 ≥150s 且期间有 ≥5 次有效手势
(=活跃探索,排除挂机——挂机交给现有 60s L1 体系)→ 每进展段限 1 次。
- 播报内容(整句分档,不拼接,归因真话——完成数由 `_stage()` 反推):
- `gd_prog_1`「钥匙已经到手。找到能用它的锁。」(stage=cabinet)
- `gd_prog_2`「铁柜已经打开。电还没有通。」(stage=box)
- `gd_prog_3`「电已经通了。去找那扇大门。」(stage=gate)
- key 阶段无进展 150s 不走本通道(现有 L1/L2/L3 已覆盖"找不到钥匙",
避免双计时器对同一阶段轰炸)。
- 队列语义沿用 `_pendingVoice`(绝不顶句,待播期间有操作即作废);warning
震与 L1-L3 同款。
- **宪法条款**:00 §3.2(整句分档);归因真话律;14 裁定 5(escape 卡关
骨架保留,本通道是**新增平行通道**,不动既有 60/120/180 任何一级);01 号
文档 §3.1 原始意图的部分回收。
- **拥挤评估**:触发频率 = 每进展段最多 1 次 × 最多 3 段(cabinet/box/gate)
= 全局限 3 句;且要求 150s 活跃零进展,正常通关(2-3 分钟/环节)永远触发
不到。只补"该响没响"的洞,不给顺畅玩家加噪。
### B4 · 双击兜底降级:背包 + L1 意象,不再直通 L2
- **问题(证据)**:双击无目标 = 玩家**主动查询**的唯一入口,现状直通
L2 明示:live 行 161-178(柜已开后再双击铁柜房 → use_fail + `stuck_box_2`
把电箱房和动作全报出来)、行 230-243(同理 `stuck_gate_2`)。P1 定为 P0
"谜题被白送"——没卡关的人收到卡关 120s 才该有的答案,既是泄漏也稀释 L2
的稀缺性(难度变相降低)。
- **目标状态**:主动查询返回"背包 + 当前目标的 L1 级暗示",答案仍锁在
计时器 L2/L3 与进展链里。
- **方案**:`_useItem()` 兜底链第二句由 `voice/stuck_<stage>_2` 改为
`voice/stuck_<stage>_1`(资产现成,4 句意象暗示:铁锈味/钥匙在身上/
电流声/电已通——全部不点明房间与动作,零新资产)。`use_fail` 与背包句
不动。已在 `_stuckShown` 里记过 L1 的进展段不重复播(沿用既有去重)。
- **宪法条款**:00 §5.3(escape 帮助入口语义不变,只是档位归位);01 号
§3.2「主动求提示保留:先播背包,再播 L2」——本条是对 01 的**有意修订**
(泄漏 > 帮助,证据双实录),在本文档留痕;归因真话(L1 句无答案可泄漏)。
- **拥挤评估**:播出次数不变(一次兜底两句);内容变轻。L2 触发总量下降
(对非卡关玩家),L3 不受影响。对卡关玩家:60s L1 → 120s L2 → 180s L3
的阶梯完整保留,难度不降。
### B5 · 空房首访描述(复用现有资产)+ 方位线索句(新词,二选一落地)
- **问题(证据)**:进入空房零语音(live 行 87-94、179-186、209-220),
4 间空房 2 种环境音不可区分(amb_hum 行 90/182;amb_wind 行 120/212)。
玩家对 5/9 的地图面积永远建不起模型——用户反馈"不知道自己在哪"的大头。
- **目标状态**:每间空房至少有一个可建立心智地图的听觉锚(首访一次性,
之后可单击复核)。
- **方案**(两档,按实施预算二选一,建议 a 先行):
- **a(零新资产)**:空房**首访**时补播现有 `empty_1/2/3`(现只在单击
检查时播;进入时播、单击时不再重复首访句)。三个 id 恰好够分给
(1,0)/(0,1)/(1,2)+(2,0)…4 房 3 句,需把 (1,2)/(2,0) 拆开:建议
(1,0)→empty_1、(0,1)→empty_2、(1,2)→empty_3、(2,0)→复用 empty_3 或
保持沉默(两间"远处水滴"空房对称分布也是合法的记忆点)。
- **b(3-4 句新词,推荐终态)**:empty_* 改词重录为**含固定方位特征**的
句子(房间特征是固定的,方位词不随玩家状态变,归因真话):
`empty_1`→「北面墙缝透进一丝风。这里什么都没有。」((1,0) 北外墙)、
`empty_2`→「回声从东面的墙折回来。这里空空荡荡。」((0,1) 东侧内墙——
内墙方位特征需与地图核对后定稿)、`empty_3a/3b` 同理。每句给一个方向
锚 + 一个可分辨特征,四间空房从此声学可区分。
- 触发:进入空房首访(visited 集合扩到空房坐标);震动/earcon 不动。
- **宪法条款**:00 §3.2(整句);归因真话(方位特征写死前按房间图逐间核对);
14 裁定 7(描述句走语音通道)。
- **拥挤评估**:每房一生一次,全局 +≤4 句;单击检查路径不变。对节奏零影响。
风险:empty_* 改词会动既有资产文本(playtest 断言若引用文本需同步登记)——
方案 a 完全绕开此风险。
### B6 · 引导 busy 窗口手势缓存(逻辑修复)
- **问题(证据)**:live 行 30-35:`g2_wall` 播放中(37.60-43.28)我滑了一下
(41.57),只有引擎 [10] 轻震——墙没响、G2 计数没走,**静默吞手势**。若玩家
信了这一下、只再补一滑,G2 永远不满足"滑两下",引导卡死(虽有 20s 看门狗
兜底推进,但那是防资产缺失的,不是交互语义)。P1 特别检查项"失败可否归因"
判半不过。
- **目标状态**:busy 窗口内的首个手势在窗口结束后被补判(或明确拒绝并给
反馈)。
- **方案**:`_guideBusy` 期间缓存首个非跳过分手势,推进语音播完(done())
后立即按新步骤判定:若缓存手势对新步骤合法则直接作答(玩家体验为"我的
输入没丢");不合法则按新步骤的答错路径处理(有纠正语音,可归因)。缓存
仅 1 个、只存活到本步骤解锁,杜绝跨步幽灵输入。
- **宪法条款**:00 §5.4「做了一个手势 3 秒内必须有反馈」(当前 busy 吞手势
违反此条,本条是合规修复);14 裁定 5(引导链结构不动)。
- **拥挤评估**:零资产、零新增语音;只改引导期手势时序。引导链被 playtest
断言冻结的部分(finishVoices(3)→PLAY 等)不受影响——缓存只在 busy 窗口
内生效,脚本化玩家的手势本就在 turn cue 后送达。
### B7 · 结算链单击守卫(逻辑修复)
- **问题(证据)**:live 行 253-275:`inspect_gate_ready` 被随后的逃脱双击
截断(行 256-257,属正常链式替换);更严重的是我的习惯性单击(369.22)在
结算链 escape 播到 10.7/15.3s 时直接触发重开——`time_band1`(用时)与
`again` 两句被永久吞掉(行 263-275)。P3 定为 P0:"结算是我唯一确认打完了/
打得如何的窗口,一碰就碎"。
- **目标状态**:结算链播完前,ENDING 单击不立即重开。
- **方案**:`_escape()` 起设 `_settleBusy=true`(结算链 onDone/30s 兜底处
复位);ENDING 单击在 `_settleBusy` 期间忽略(或在 full 档播一句既有
`again` 前句?—— 不新增语音,直接忽略+引擎 confirm 震已有)。`again` 句
本身已含操作指引,链播完后再单击重开的原语义不变。30s 资产缺失兜底保留
(防静默死锁)。
- **宪法条款**:00 §5.4(单击 3s 内反馈——忽略路径有引擎 confirm 震兜底,
合规);00 §3.2(结算整句分档的完整到达权);14 裁定 4(不顶句、不丢句)。
- **拥挤评估**:零资产;只影响结算后前 ~15s 的重开时序。结算链播完 → 单击
重开 → replay 的既有闭环在转写里完整存在(mock 行 144-149),本条只是
把闭环的起点从"escape 第一句"推迟到"again 播完"。
### B8 · 跳过引导后补播目标句(零资产)
- **问题(证据)**:`guide_skip` 路径(live 行 61-74)与 REOPEN 单击直进
路径都只播 `start_room`(手势总览)——**目标陈述 g7_goal 只在引导 G7 链尾
出现一次**,跳过的玩家(恰是最需要目标的一群,P1/P3 共识)永远听不到
"找到能用这把钥匙打开的东西"。且跳过后阶段=key、身上无物品,60s 内只有
意象级 L1(行 75-86)。
- **目标状态**:任何路径进入 PLAY 时,玩家都至少听过一次目标句。
- **方案**:`guide_skip` 确认句后、`_enterPlay` 前补播 `g7_goal`(资产现成,
链式 `_speak`);REOPEN 单击直进路径同样在 `start_room` 后接 `g7_goal`。
二周目老手可单击跳过该句(skippable 默认 true,符合 12 号文档——教学
义务句已随首局完成,此处是状态告知不是教学)。
- **宪法条款**:00 §3.2;14 H1 注(g7_goal 不接方位句的红线仅冻结于**引导
链内**——本方案接在跳过确认链后,不触碰被冻结的引导链断言);01 号 §2.1
设计意图(G7 目标陈述的全局可达)。
- **拥挤评估**:两句短链、每会话最多一次;二周目玩家 1 次单击即可跳过。
对节奏影响 ≤5s,且只发生在"信息最空白"的路径上。
### B9 · L2 台词顺带修订(不推荐本轮做,留档)
live 行 288-294 可见 L2/L3 句仍用"西南角/西北角/东南角"绝对方位词——对
从未建立坐标系的玩家,绝对角落词不如相对方向可用(L3 带路已经改用相对
滑动方向)。但 L2 明示句的改词会动 12 句资产与全部相关断言,且 L3 已兜底
带路,**收益/成本比低,列为后续轮候选**,不在本轮实施清单内。
## 4.2 盲区 → 方案对照表
| 盲区 | 方案 | 新语音资产 | 逻辑改动 | 证据 |
|---|---|---|---|---|
| S1/S1b 方位(撞墙/平时) | B1 | +4 句 | 撞墙链路接播+限频 | live 行 40-55;P1/P3 P0 |
| 相对方位半盲区(无目标名) | B2 | +3 句 | 三处接线点换 id | live 行 112-116/156-160/201-208 |
| S3 全局进度无渠道 | B3 | +3 句 | 新计时器+触发 | 用户反馈;live transit 段 |
| S4/S4b 目标重述/查询泄漏 | B4 + B8 | 0(复用) | 兜底换 id;跳过链补播 | live 行 61-74/161-178/230-243 |
| S5 空房不可区分 | B5a/b | 0 或 +3~4 句 | 首访描述接线 | live 行 90/182/120/212 |
| S6 busy 吞手势 | B6 | 0 | 缓存补判 | live 行 30-35 |
| S7 结算链截断 | B7 | 0 | 守卫 | live 行 253-275 |
## 4.3 实施清单(F 类编号,**仅登记,未实施**)
| # | 内容 | 资产管线步骤 | 断言登记建议 |
|---|---|---|---|
| F1(B1) | 撞墙方位句 `wall_dir_l/r/u/d` ×4,撞墙链路接播 + 同向 10s 限频 | `tools/gen_voice_dual.py --game escape --only wall_dir_l,wall_dir_r,wall_dir_u,wall_dir_d` → `--convert` → `check_all_assets.py` → `extract_voice_text.py` 刷新 | escape playtest:撞墙后方位句到达/限频生效/移动打断作废;guide.test 不受影(PLAY 期行为) |
| F2(B2) | `gd_cabinet_dir` / `gd_box_dir` / `gd_gate_dir` ×3,替换三处 gd_room_* 接播(原句保留) | 同 F1 管线,3 句 | playtest:三处进展链尾新句到达且方位正确;原 H1 断言(gd_room_* 映射表)保留不动 |
| F3(B3) | `gd_prog_1/2/3` ×3 + 无进展 150s 活跃计时器(≥5 手势闸门、每段限 1 次、队列语义) | 同 F1 管线,3 句 | playtest(guide=off):计时器不误触(挂机不触、进展后清零、限 1 次);guide.test:队列不顶句、作废语义 |
| F4(B4) | 双击兜底第二句 stuck_*_2 → stuck_*_1 | **零资产** | playtest:兜底链内容变更需逐条同步(现有断言若期望 stuck_*_2 必改);L2 计时器触发路径断言不动 |
| F5(B5a) | 空房首访补播 empty_*(零资产先行版) | 零资产 | playtest:首访到达/重访不重复/单击检查语义不变 |
| F6(B5b,可分期) | empty_* 改词带方位特征 ×3~4 | 同 F1 管线;**改词前逐间核对房间图方位特征(归因真话)** | 文本断言同步;双耳一致性 |
| F7(B6) | 引导 busy 手势缓存补判 | 零资产 | guide.test:busy 窗口手势缓存→解锁后补判/答错路径;既有引导推进断言零改动红线 |
| F8(B7) | 结算链单击守卫 `_settleBusy` | 零资产 | playtest:结算链完整播完(time_band/again 到达)后才可重开;30s 兜底仍生效 |
| F9(B8) | 跳过引导/REOPEN 直进补播 g7_goal | 零资产 | playtest:两条路径 start_room 后 g7_goal 到达;二周目单击可跳过 |
统一收尾(每轮 F 实施完必做):`check_all_assets.py` 全绿 → 6 playtest
(guide=off)断言对账(escape 现有 103 条,F4/F5/F7/F9 会改其中若干,
须逐条登记变更原因)→ `guide.test.mjs` 增补 → live 复跑一局验证新句在
真实混音中可闻(B2/B3 短句尤其要做 ASR 层判定)。
---
# 阶段四 · 实施勾账(escape-state round2,2026-09-15)
> §4.3 清单已从"仅登记,未实施"转为**已实施**(除 F6 按方案"可分期"留档,
> 见实施裁定 1)。验证档案:`research/escape-state/round2/`(mock 转写 +
> live 真机一局 38 段 OK 37 / SFX_BGM_ONLY 19 / 零截断,S1-S7 逐条核对)。
> 新断言全部登记在 `web/escape/test/playtest.mjs`(103 → 235 条全绿)与
> `web/harness/guide.test.mjs`(120 → 132 条全绿);既有断言变更 3 处,
> 逐条登记于下表"断言变更"列。
| # | 落地 | 断言登记(场景.条数) | 断言变更 | live 证据(round2/live-transcript.md 行号) |
|---|---|---|---|---|
| F1(B1) | ✅ `wall_dir_l/r/u/d` ×4 双性别已生成落盘;`_move` 撞墙链路 `wall` 播完接播、同向 10s 限频、被打断作废(game.js `_move`) | playtest G×13(引导期不播/接播顺序/打断作废/限频与恢复)+ 资产校验 F 段扩 wall_dir 族 | 无 | 行 141-143 wall_dir_u、行 166-168 wall_dir_l、199.5s 限频 |
| F2(B2) | ✅ `gd_cabinet_dir/gd_box_dir/gd_gate_dir` ×3 替换三处接播;`_playGoalDir(target, namedId, expectedId)` 异常路径回落 `gd_room_*`(原句保留) | playtest H×12(三处到达+顺序+不再播 gd_room_*+回落+同房间不播);guide.test ⑩c 换线 + 回落断言 | guide.test ⑩c 三处接线断言由 gd_room_* 改为 gd_*_dir(映射表单元断言保留不动) | 行 231-233 gd_box_dir、行 306-312 gd_gate_dir |
| F3(B3) | ✅ `gd_prog_1/2/3` ×3 + `_checkStuck` 平行通道(150s 无进展 ∧ ≥5 手势 ∧ stage≠key,每段限 1 次,队列/作废语义同卡关提示) | playtest I×11(key 不走/闸门/触发/限 1 次/进展复位/box 段再触/入队不顶句/作废计入不重发) | 无 | 行 265-268 gd_prog_2(活跃 154s 触发,期间 L1-L3 零误触) |
| F4(B4) | ✅ `_useItem` 兜底第二句 `stuck_*_2`→`stuck_*_1`,L1 播了即记账沿用 `_stuckShown` 去重 | playtest J×8(背包+L1/不直通 L2/记账/再兜底不重复/60s 不重复 L1/120s L2 仍到达) | playtest 场景 A 兜底断言 stuck_key_2→stuck_key_1(+不播 _2 反向断言) | 行 180-182 use_fail+stuck_cabinet_1 |
| F5(B5a) | ✅ 空房首访补播 `empty_*`((1,0)→1/(0,1)→2/(1,2)→3/(2,0)→3 复用),`_freshEmpty` 实现"本房间内单击不重复首访句";不算进展事件 | playtest K×9(首访到达/非进展事件/房内单击不重复/重访零语音/重访单击照常/四房映射) | 无(单击检查语义不变) | 行 115-118/189-192/199-202 三间首访 |
| F6(B5b) | ⏸ **留档**(实施裁定 1) | — | — | — |
| F7(B6) | ✅ `_guidePending` 缓存首个非跳过分手势,`_advance` done() 解锁后按新步骤补判(fromCache 绕过下滑跳过检查);跳过分手势不缓存;G6 链尾离 GUIDE 缓存作废 | playtest L×15(不当场判定/补判作答/答错路径/不触发跳过/仅 1 个/跳过分手势不缓存/引导链完整);guide.test ⑩d×7(full 档同款) | 无(既有引导推进断言零改动) | live-events 82.3s 缓存→87.9s 补判 |
| F8(B7) | ✅ `_settleBusy` 守卫:`_escape` 挂载,`_startEndingBgm`(链完/30s 兜底汇合点)解除;ENDING 单击守卫期忽略 | playtest M×10(守卫期不重开/链完整 time_band+again/解除后重开/again 缺失 30s 兜底) | 无 | 行 336-347 习惯性单击被守卫挡下,time_band3+again 完整到达 |
| F9(B8) | ✅ `_skipGuide` 链 guide_skip→g7_goal→_enterPlay;REOPEN 单击 `_enterPlay(true, true)` start_room→g7_goal;`_skipGuide` 期间 guideStep=7(裁定 4) | playtest N×6(直进顺序/可单击跳过)+ A2/B 场景跳过链更新(g7_goal 到达、顺序、两句后再进 PLAY) | playtest A2/B 跳过断言由"一句后 PLAY"改为"guide_skip→g7_goal 两句后 PLAY" | g7_goal OK sim=1(行 149.5s) |
统一收尾执行记录:`check_all_assets.py` 全项目 15 游戏全绿(escape voice 各 90
id 双性别一致);escape playtest 235/0、guide.test 132/0、其余 14 款 playtest
回归全绿、engine-voice 19/0 与 engine-input 72/0 无连带;live 复跑一局
(端口 8145,打完杀净)S1-S7 每个时刻现在都有落点(round2/README.md 对照表)。
## 实施裁定记录(编号自本轮起,接续追加不改旧文)
1. **F6(B5b)分期留档**:B5b 草案台词未定稿——`empty_2` 东侧内墙方位特征
原文标注"需与地图核对后定稿"、`empty_3a/3b` 无台词草案,无法按"台词与
草案逐字一致"的管线铁律生成;且 B5 原文即"建议 a 先行"、F6 标注"可分期"。
本轮只落地 F5(B5a 零资产版),B5b 连同 B9(L2 角落词改词)一并列为
后续轮候选。
2. **B4 去重双向化**:方案只规定"已在 `_stuckShown` 记过 L1 的进展段不重复
播";实施把兜底播出 L1 也**记账**(播了即加入 `_stuckShown`),否则 60s
计时器会把玩家刚听过的同一句意象暗示再播一遍(去重只做单向等于没做)。
附带效果:L1 整级(含 `stuck_ask`)跳过,L2/L3 阶梯与触发时点不变
(playtest J 冻结)。
3. **B3 作废计入已播**:待播 `gd_prog` 在排队期间被玩家操作作废后仍计入
"每进展段限 1 次"(触发即记账)。方案"每进展段限 1 次"若按播出记账,
5s 轮询会在条件持续成立时立即重发,形成骚扰循环;按触发记账与该条
"全局限 3 句"的拥挤评估一致(playtest I⑧ 冻结)。
4. **B8 跳过链期间 guideStep=7**:`_skipGuide` 的两句链(guide_skip→g7_goal)
播放期间相位仍是 GUIDE,原 guideStep 下单击/滑动会被当成引导作答(既有
竞态:skip 链中单击可误推 G1)。落地把 guideStep 置 7(G7 等待语义:
手势全忽略),链中输入 robustness 与 G7 自然链尾对齐;语音缺失兜底计时
5s→9s(覆盖两句链)。
5. **B5a 两个二选一**:(2,0) 选"复用 empty_3"(方案原文认可"两间'远处水滴'
空房对称分布也是合法的记忆点");"单击时不再重复首访句"以 `_freshEmpty`
实现——仅首访句刚播过的那个房间内抑制同句,离开即恢复单击播句,
重访后单击检查语义完全不变(playtest K 冻结)。
6. **B6 缓存手势绕过下滑跳过**:方案"不合法则按新步骤的答错路径处理"
意味着缓存的下滑在可跳过步骤上也不再触发 `_skipGuide`(fromCache 绕过
skip 检查)——这正是 live round1 S6 事故(答案被吞后下滑被当成跳过)的
修复点;实时注入的下滑跳过语义不变(playtest L⑤/guide.test ⑩d 冻结)。
7. **B1 限频记账点**:同向 10s 限频按**方位句实际开播时刻**记账;仅调度
(wall 接播注册)未播出即被打断的不占额度(playtest G③"被作废的方向
补撞后正常接播"冻结)。
## 盲区清单核销(§3.2 → round2)
S1/S1b(B1)、相对方位半盲区(B2)、S2(B5a)、S3(B3)、S4/S4b(B4+B8)、
S5(B5a,B5b 留档)、S6(B6)、S7(B7)——live round2 逐条核对证据见
`research/escape-state/round2/README.md`,每个盲区时刻现在都有落点。