- radio:R3 放行(4/4 人设满分档 5/5、愿意玩、P0=0;live 四修坐实+SA3 真声入局) - dungeon:R1 0/4→R3 4/4 翻案达成;H1 死亡结算链截断根修(真凶=双击窗幽灵 tap 死后回调路由进 SETTLE,800ms 手势死窗;7 断言含旧代码反证)+ 定向 live 复验 PASS(两局含 R3 同款幽灵场景,结算链 7 句逐句完整) - 章程账本更新:批次 1 验收 ☑(R4-R6 经用户裁定暂停,视上手反馈续;v2 首条=漫游迷路抢救盲区) - 断言账面:radio 246 / dungeon 1649 / 9 套件全绿
715 lines
62 KiB
Markdown
715 lines
62 KiB
Markdown
# 16 · 《回声地窟》完整设计(dungeon / Echo Cavern)
|
||
|
||
> **给开发者的实现要点摘要**
|
||
> 1. 听声辨位 Roguelike:网格地窟 + 面朝转向。**单击=声波脉冲**(回声"看"地形:正前距离 4 档×材质 4 种,左右侧 墙/开口 2 档,预 pan 立体声资产),**按住=前进、左右滑=转身**(豁免登记见 §2),双击=重发脉冲(免费重听)、上滑=状态、下滑=菜单(三件套不豁免)。战斗是 rpg 式回合制(前摇双击格挡),Boss 战加入绕位(脚步声报方位、转身对准才打得到)。
|
||
> 2. 方位全程降维 ≤4:探索报前/左/右三向(身后不报,转身即查),战斗相对方位只有面前/左/右/背后。回声=静态地形、脚步=动态怪物、播报=绝对方位——三套信息由同一 `grid` 单源生成,机制上不可能互相矛盾。
|
||
> 3. 单局=一命一局,7 层满通约 24 分钟;死了重开但金币进元进度("遗产"5 项永久强化,死亡不清档)。学习期零惩罚:L1 怪 ATK=0、L2 ATK=1,学期内不可能死;药水自动喝;撞墙无伤;无任何倒计时。
|
||
> 4. 随机性全走种子 PRNG(mulberry32,`opts.seed` 可注入,playtest 传固定 seed 复现);回声/脚步全部用「预生成 sfx 资产 + setTimeout 调度 + volume」实现,不用引擎外实时节点(playtest 虚拟时钟可断言)。
|
||
> 5. 台词表 [16-dungeon-voice-ids.md](16-dungeon-voice-ids.md)(125 句 ×2 性别,无 NPC——怪物不说话);本作向导句 gd_* 需在开发轮次登记进 14 号文档增补(§6 纪律:queue:true、idle-only、onceKey)。
|
||
> 6. 规模对标 escape+rpg 折中:单文件 game.js 预计 900-1100 行,bgm 4 + sfx ~34 + voice 125×2。
|
||
|
||
---
|
||
|
||
## 1. 概述
|
||
|
||
- **一句话**:你是盲眼探窟者,单击发出声波脉冲、用回声"看"每次都不同的地窟,听脚步辨怪,逐层下潜,死了带着金币重来、越死越强。
|
||
- **题材**:黑暗洞窟探险。核心命题「伸手不见五指,声音就是你的眼睛」——题材与纯音频互为 justification,与《剑与声》的"耳即眼"呼应但玩法完全不同:rpg 是选项列表 RPG,本作是**空间探索型 roguelike**(方法论 §4.1:状态层重点=方位参数映射+撞墙归因+目标方向信标)。
|
||
- **为什么适合纯音频**:回声定位本来就是"用声音看空间"——视觉游戏的 minimap 在这里天然就是一段回声;黑暗=无画面是叙事设定而非技术妥协。移动/转身/距离全部可离散化(≤4 方位×4 距离档),不需要连续角度(文献结论:方位降维 ≤4)。
|
||
- **单局**:一命一局,出生到死亡/通关。7 层,满通约 24 分钟,普通玩家死于 4-6 层(约 14-19 分钟)。死亡结算 ≤15s 必答归因三问,金币入元进度,**死亡不清档位进度**。
|
||
- **与现有 6 游戏的关系**:空间感承接 escape(网格+带路提示),回合战斗承接 rpg(前摇格挡),演练教学承接 space(D 步逐项实操),元进度为本作新增系统(8 款新模式探索点的核心增量)。
|
||
|
||
## 2. 手势映射与豁免登记
|
||
|
||
### 2.1 探索态(EXPLORE / DRILL / DESCEND)
|
||
|
||
| 手势 | 作用 | 反馈 |
|
||
|---|---|---|
|
||
| 单击 | **发出声波脉冲**(核心动词,1.2s 冷却防连打糊声流) | sfx/pulse + 调度回声组(§3) |
|
||
| 按住(longpress,450ms 起) | **前进**:按住朝面朝方向连续走(每格 0.5s),松开(longpressend)即停;撞墙自动停 | 每格 step_own + swipe 震;撞墙 bump + 停,**无伤** |
|
||
| 左滑 | 左转 90°(面朝逆时针 N→W→S→E) | step_turn + swipe 震 |
|
||
| 右滑 | 右转 90° | step_turn + swipe 震 |
|
||
| 双击 | **重发脉冲**(免费重听回声,不消耗任何资源、无冷却惩罚) | 同单击(宪法"双击=重听"的本作落地:线索=回声) |
|
||
| 上滑 | 状态播报链:层 → 血量档 → 金币档(元进度总账)→ 面朝 → 周围威胁 | 5 段链播 |
|
||
| 下滑 | 模态菜单(§2.4) | menu earcon |
|
||
|
||
### 2.2 战斗态(BATTLE)
|
||
|
||
| 手势 | 作用 | 反馈 |
|
||
|---|---|---|
|
||
| 单击 | 挥击:怪在你面前才命中;不在(Boss/绕位怪换位后)= 挥空 | atk_swing→命中 atk_hit / 挥空 + `btl_swish`(归因真话) |
|
||
| 双击 | **格挡**:仅敌方前摇音窗口内(1.2s)有效;面对攻击来源减伤 80%,未面对 0%(背后挨刀) | parry / hurt + hit 震 |
|
||
| 左滑/右滑 | 转身(只对绕位怪/Boss 有意义;缠斗怪时无效) | step_turn / 纠错(§10) |
|
||
| 按住 | **尝试逃跑**:普通怪必成、但撤退挨一下(真实掉血);Boss 战拒绝 | `btl_flee` / `btl_flee_boss` |
|
||
| 上滑 | 战斗状态:我方血量档 → 敌方血量档 →(Boss/绕位战)它的当前方位 | 3 段链播 |
|
||
| 下滑 | 模态菜单(战斗暂停,回合制无实时流,无需 count-in) | menu earcon |
|
||
|
||
### 2.3 标题态(TITLE)/ 结算态(SETTLE)
|
||
|
||
| 手势 | TITLE | SETTLE |
|
||
|---|---|---|
|
||
| 单击 | 继续(有存档)/ 开始探窟(无存档) | 再来一局(新周目,重掷 seed) |
|
||
| 右滑 | 放弃当前周目,重掷新周目(元进度保留) | —(无效,纠错) |
|
||
| 双击 | 重听 continue_ask / 元进度播报 | 重听结算链 |
|
||
| 上滑 | 元进度播报:金币档 → 最深层数档 → 遗产清单(已解锁各一句)→ `unlock_none`(不够时) | 重听元进度段 |
|
||
| 下滑 | 菜单(帮助/返回两项) | 菜单 |
|
||
| 按住/左右滑 | 无效(error 震 + `gd_invalid`) | 同左 |
|
||
|
||
### 2.4 模态菜单(遵守 00 文档 §5.2 全球约定,无豁免)
|
||
|
||
下滑呼出 → 单击循环切换(每切播项名)→ 双击确认 → 上滑=帮助 → 下滑=关闭。打开即暂停(战斗回合制天然可暂停)。选项:返回探索(战斗态同)→ 保存并回大厅 → 重新听帮助 → 重新开始本周目。
|
||
|
||
### 2.5 豁免登记(必须显式)
|
||
|
||
| # | 豁免条目 | 理由(先例) |
|
||
|---|---|---|
|
||
| E1 | 单击=脉冲(非"确认/继续") | 核心动词必须是最快手势;脉冲是"检查"类非即时操作,300ms tap 延迟无碍(escape 单击=检查同款) |
|
||
| E2 | 左右滑=转身、按住=前进(探索态四向滑+长按被玩法占用) | 网格移动玩法(escape 四向滑=移动先例);上下滑让位给不可豁免的状态/菜单三件套,前进改绑长按——"按住就走"零认知负荷(P4),单手免视(P3) |
|
||
| E3 | 对局期 `audio.setRateBoostEnabled(false)`(探索/战斗/教学),菜单/结算恢复 true | 长按=移动是潜在玩法输入,长按快进必须豁免(rhythm/space 对局期豁免先例) |
|
||
| E4 | 战斗态下滑=菜单保留,但菜单在战斗内语义为暂停 | 回合制,菜单即暂停,无并发压力;不沿用 rpg"战斗态下滑=逃跑"豁免(本作逃跑绑长按,更贴"转身就跑"直觉) |
|
||
|
||
教学(DRILL)中逐条讲清 E1/E2/E4 的偏离(tut_* 句),符合 §5.4"偏离必须在开场交互教学中讲清"。longpressend 仅作停走信号,不承载第二语义;不开启 immediateTap(无 tapcancel 路径)。
|
||
|
||
## 3. 核心动词:声波脉冲与回声定位
|
||
|
||
### 3.1 声学编码(全在引擎能力内)
|
||
|
||
| 维度 | 编码 | 实现 |
|
||
|---|---|---|
|
||
| 正前距离 | 4 档:贴身(1格)/近(2格)/中(3-4格)/远(5+格) | 脉冲后 setTimeout 调度 `echo_1..4`,延迟 0.25/0.50/0.80/1.10s,volume 0.9/0.7/0.5/0.35 |
|
||
| 正前材质 | 4 种:石墙/木门/铁梯(楼梯)/水 | 回声到达后 +120ms 播材质尾音 `mat_stone/door/stair/water`(0.2s 短音色) |
|
||
| 左右侧 | 2 档:墙(闷)/开口(空旷回灵) | 脉冲后 +0.35s 同时播 `side_wall_l/r` 或 `side_open_l/r`(**预 pan 立体声资产**:能量只在一个声道,引擎 play() 无实时 pan,space whoosh_left/right 同工艺) |
|
||
| 身后 | **不报** | 刻意降维:回声只照面前和两侧,转身即查(教学句 tut_side_rule 讲明规则;不报≠信息矛盾) |
|
||
|
||
- 节点说明:**不用引擎外实时 WebAudio 节点**(playtest mock 只 mock bufferSource/gain)。全部=预渲染 sfx 资产 + `play()` volume + setTimeout 调度——虚拟时钟下回声调度可精确断言。
|
||
- 降级方案:若 SA3 立体声分离度不达标,侧向回声改 SYN 程序合成(直接写立体声 AudioBuffer 的 L/R 通道数据,管线能力内);材质尾音全部 SYN 可兜底。
|
||
- 频带分离防糊:己方脚步/怪物脚步=低频(100-300Hz),脉冲+回声=中频(800Hz-2kHz),BGM 垫底——探索期并发声流 ≤2(你的动作反馈 + 怪物脚步)恒成立。
|
||
- 脉冲冷却 1.2s:防连打;双击重发不受额外惩罚(P1"回声可重听且免费")。冷却期单击/双击 → 仅 error 震(无语音惩罚)。
|
||
|
||
### 3.2 三套信息自洽(P1 专项,机制级保证)
|
||
|
||
| 信息 | 声源 | 内容 | 坐标系 |
|
||
|---|---|---|---|
|
||
| 回声 | 脉冲触发 | 静态地形(墙/门/楼梯/水 + 距离) | 相对(前/左/右) |
|
||
| 怪物脚步 | 怪物移动即播(每步,间隔 1.5-2.5s) | 动态威胁(左右 pan + 距离档音量;背后=中央音量减半,转身后恢复=方位反馈) | 相对(左/前/右/后) |
|
||
| 播报 | 上滑/事件 | 层数/血量/金币/面朝(绝对)/威胁(相对左右) | 面朝=绝对,其余=相对 |
|
||
|
||
三者全部由同一 `game._floor.grid` 与怪物位置**单源查询**生成,无独立状态;模板句固定(方位句 4 句、威胁句 4 句)。回声永不报怪物、脚步永不报地形——语义域零重叠。威胁播报只报左右不报背后(同回声降维,规则在教学讲明)。
|
||
|
||
## 4. 状态机
|
||
|
||
### 4.1 状态列表
|
||
|
||
`TITLE / DRILL / EXPLORE / BATTLE / MENU(overlay) / DESCEND / SETTLE`。MENU 是挂起 overlay(记录返回态),其余为正交主态。
|
||
|
||
### 4.2 转移图
|
||
|
||
```
|
||
start ──> TITLE ──单击(有档)──> EXPLORE(恢复存档+局面复述)
|
||
│ │单击(无档) │
|
||
│ ├─────> DRILL(仅首次) ──完成/下滑跳过──> EXPLORE(L1)
|
||
│ └右滑=重掷新周目 ──────────────┘(已学过教学,直达)
|
||
v
|
||
EXPLORE <──────────────┐
|
||
│ 走进怪格/怪走进格 │ 胜利(原地)
|
||
v │
|
||
BATTLE ────────────────┘
|
||
│ HP≤0
|
||
v
|
||
SETTLE(死亡) <── EXPLORE(第7层终Boss胜=通关SETTLE)
|
||
│单击
|
||
v
|
||
TITLE(元进度入账后)
|
||
|
||
EXPLORE ──踏上楼梯格+单击──> DESCEND ──> EXPLORE(floor+1)
|
||
EXPLORE/BATTLE ──下滑──> MENU ──返回──> 原状态
|
||
```
|
||
|
||
DESCEND 演出:stairs_down 轰隆 + `evt_descend` + 下一层生成 + `st_floor_N`,约 5s。
|
||
|
||
### 4.3 每状态手势映射总表
|
||
|
||
| 手势 | TITLE | DRILL | EXPLORE | BATTLE | MENU | SETTLE |
|
||
|---|---|---|---|---|---|---|
|
||
| 单击 | 继续/开始 | 步骤作答 | 脉冲 | 挥击 | 循环切项 | 再来一局 |
|
||
| 双击 | 重听 | 步骤作答(D6) | 重发脉冲 | 格挡(前摇窗口) | 确认执行 | 重听结算 |
|
||
| 左滑 | error | 步骤作答(D5) | 左转 | 转身(绕位怪有效) | 上一项 | error |
|
||
| 右滑 | 重掷新周目 | 步骤作答(D5) | 右转 | 转身(绕位怪有效) | 下一项 | error |
|
||
| 上滑 | 元进度 | 状态(教学后可用) | 状态链 | 战斗状态链 | 帮助 | 重听元进度段 |
|
||
| 下滑 | 菜单 | 跳过教学 | 菜单 | 菜单(暂停) | 关闭 | 菜单 |
|
||
| 长按 | error | 步骤作答(D4) | 前进 | 尝试逃跑 | — | error |
|
||
| 长按松开 | — | 停 | 停 | — | — | — |
|
||
|
||
无效手势一律 error 震;full 档语音纠错 `gd_invalid`(onceKey 同局限 1 次,之后仅 error 震)。idle-only:纠错/看门狗语音在播时跳过(引擎 guide 已实现)。
|
||
|
||
## 5. 教学编排(DRILL,首局内嵌于第 1 层)
|
||
|
||
每步=一句短旁白→一次实操→反馈;做错重教同一步(**新短句**,不逐字重播)≤2 次后自动演示通过(不卡流程,space 先例)。教学寄生局内:DRILL 就在 L1 地图起点房内进行,D8 的教学怪是 L1 两只怪之一。全程可下滑跳过(`tut_skip`),跳过标志存进度,二周目不再进 DRILL。
|
||
|
||
| 步 | 旁白 | 等待动作 | 通过条件/反馈 |
|
||
|---|---|---|---|
|
||
| D0 | intro_1+intro_2+tut_d0 | 单击 | confirm 震 |
|
||
| D1 | — | 单击 | pulse+回声组 + `tut_echo_come`。**首输入≤60s 达成点(≈t+15s)** |
|
||
| D2 | tut_front_rule | 单击继续 | 纯听规则句 |
|
||
| D3 | tut_side_rule | 单击继续 | 纯听规则句 |
|
||
| D4 | tut_walk | 按住走 ≥2 格 | 脚步声(撞墙也算过,bump+`那边是墙`由既有 bump 路径播) |
|
||
| D5 | tut_turn | 左滑+右滑各 1 | 转身声 |
|
||
| D6 | tut_replay | 双击 | echo 重播 + `tut_echo_come` 复用 |
|
||
| D7 | tut_status + tut_menu | 上滑(状态链播)→ 下滑开菜单→再关 | 两键各一次 |
|
||
| D8 | tut_mob → 走向教学怪触发战斗(盲眼蝠 ATK=0)→ tut_parry →(前摇)双击 | 格挡成/败都继续:成功 `tut_parry_ok`,失败 `tut_parry_miss` | 然后 tut_attack → 单击挥击至击杀 `tut_kill`。**首正反馈≤3min 达成点** |
|
||
| D9 | tut_stairs | 脉冲听到铁器回声后自行走到楼梯 | 踏上楼梯格 `tut_stairs_on` → 单击下潜 → EXPLORE |
|
||
|
||
D9 后不再教学;上滑=状态链随时可查。生成约束(教学局 L1,优先于随机):起点房前方 2 格内保证有墙(D1 回声演示)、起点房门口固定一只盲眼蝠(D8)、房内 1 处金币(拾取体验)。
|
||
|
||
FTUE 核对:遮罩点按→D0 三句(~8s)→D1 首次脉冲 ≈ t+15s(<60s ✓);首个正反馈=教学战胜利 ≈ t+2min(≤3min ✓);学习期 L1-2 怪 ATK 0/1(死亡不可能 ✓)。
|
||
|
||
## 6. 地窟生成
|
||
|
||
### 6.1 seed 用法
|
||
|
||
```
|
||
rng(floor) = mulberry32((seed ^ (floor * 0x9E3779B9)) >>> 0) // 每层独立子种子
|
||
```
|
||
|
||
- `opts.seed` 可注入(playtest 传 42);正式局 `seed = Math.floor(Math.random()*1e9)`(开局掷一次,存入 run)。同 seed → L1..L7 全部可复现。
|
||
- 生成步骤(每层):① 尺寸定表(§6.2)→ ② 放 3-6 个矩形房间(2×2~3×4,不重叠)→ ③ L 型走廊按 MST 顺序连通全房间 → ④ 起点房=第 1 房中心,楼梯房=BFS 距起点最远房间中心,楼梯格=其中心 → ⑤ 宝物撒入房间格 → ⑥ 怪物放在 BFS 距起点 ≥3 格的格位(开局安全区)。
|
||
- 复现断言口径:同 seed 两次构造 → grid/怪物/宝物/楼梯逐格相等(测试读 `game._floor` 重算不变式,**不写魔法坐标**)。
|
||
|
||
### 6.2 层规格与难度曲线
|
||
|
||
| 层 | 尺寸 | 怪物(型×数) | 宝物 | 目标用时 | 累计 |
|
||
|---|---|---|---|---|---|
|
||
| 1 教学 | 7×7 | 盲眼蝠×2(ATK0) | 金币×1 | 3.0 | 3.0 |
|
||
| 2 | 7×7 | 窟鼠×3 | 金币×1+药水×1 | 3.0 | 6.0 |
|
||
| 3 窟主 | 9×9 | 蘑菇怪×3 + 窟主石甲蟹 | 金币×1+短刃 | 3.5 | 9.5 |
|
||
| 4 | 9×9 | 绕行者×2+洞蛛×2 | 金币×1+皮甲 | 3.5 | 13.0 |
|
||
| 5 | 11×11 | 洞蛛×3+绕行者×2 | 金币×1+药水×1 | 3.5 | 16.5 |
|
||
| 6 窟主 | 11×11 | 洞蛛×2+绕行者×2 + 窟主腐沼蟒 | 金币×1+阔刃 | 3.5 | 20.0 |
|
||
| 7 终局 | 11×11 | 石像卫×3 + 窟中之王 | 金币×1 | 4.0 | 24.0 |
|
||
|
||
- 窟主守楼梯格:未击败时踏上楼梯格播 `evt_stairs_lock`(楼梯不可用);击败后解锁。终 Boss 击败=通关。
|
||
- 敌型见 §7.2 表。每层开场氛围:`st_floor_N` + 层 BGM 不变(探索曲统一),深度感靠 `evt_descend` 与怪物构成。
|
||
|
||
## 7. 遭遇与战斗
|
||
|
||
### 7.1 遭遇
|
||
|
||
- 全**明雷**:怪物脚步声持续可闻(rpg 暗雷教训——纯音频里突袭没有信息缓冲)。你走进怪格或怪走进你格 → `btl_start_<mob>` + battle earcon + BGM 切换。
|
||
- 遭遇瞬间自动"面对"怪物(播报名即=它在面前)。怪移动的脚步:每步播 `mob_step_<class>_<l|c|r>`,pan 按相对方位(左/前/右),**背后用中央资产音量减半**;距离档音量 ≤2 格 0.9 / 3-4 格 0.6 / 5-8 格 0.35 / 更远不播。距离 ≤2 格加 heartbeat 震(威胁前置预警窗,方法论 L2)。
|
||
|
||
### 7.2 回合时序(实现契约)
|
||
|
||
```
|
||
进 BATTLE → 玩家回合(guide.turn) → 单击挥击/长按逃跑/转身 → 结算
|
||
→ 若怪活:怪行动(绕位怪先移动:脚步报新方位;缠斗怪原地)→ 攻击前摇 1.2s(tel_* 音)
|
||
→ 前摇内双击=格挡判定(面对攻击源:×0.2 / 未面对:×1.0)→ 受击/格挡音+震
|
||
→ 玩家 HP≤25% 且腰带有药 → 自动喝药(potion 音+播报)
|
||
→ 回到玩家回合。HP≤0 → 死亡归因链 → SETTLE
|
||
```
|
||
|
||
- 缠斗怪(蝠/鼠/菇/蛛):不绕位,恒正面,左右滑无效(`btl_no_turn` 纠错,每场 1 次)。
|
||
- 绕位怪(绕行者/石像卫):每 2 回合移动到 左/右/背后 之一(脚步 pan 报位),攻击前摇随其方位(tel 派生 pan 变体);`btl_behind` 在背后挨刀时播(归因真话)。
|
||
- Boss(石甲蟹/腐沼蟒/窟中之王):绕位+高血,半血播 `btl_half_*` + ATK+1(王二阶段)。Boss 战长按逃跑拒绝(`btl_flee_boss`)。
|
||
- 格挡判定与 rpg 一致绑双击(规避 tap 300ms 延迟);窗口外双击=error 震不生效。
|
||
|
||
### 7.3 逃跑
|
||
|
||
长按 = 尝试逃跑:普通怪/绕位怪**必成**(无 RNG,P4 友好),撤退挨一下(掉血=怪 ATK,真实账目),落回探索态、怪留在原格;拉开距离后脚步声自然远去。Boss 拒绝。逃跑无次数限制(容错拉满,代价=掉血+丢进攻回合)。
|
||
|
||
### 7.4 掉落与元进度
|
||
|
||
- 战斗胜利:金币入账 + `btl_loot_1..3`(按单场所得 3 档)+ 概率掉药水/装备(§6.2 宝物表为拾取点,怪掉落仅金币)。
|
||
- 每击杀 4 只 atkLv+1,播 `btl_lvup`("这层我变强了"的即时成长,P2 钩子)。
|
||
- **元进度(遗产)**:金币永续累计(meta.gold),死亡/通关结算入账,不清档。5 项解锁(下一局开场自动生效+播报):
|
||
|
||
| 遗产 | 价格(金币) | 效果 |
|
||
|---|---|---|
|
||
| unlock_potion 行囊药 | 90 | 开局自带 1 瓶腰带药 |
|
||
| unlock_vigor 体魄 | 150 | 起始 maxHp 20→26 |
|
||
| unlock_veil 护符 | 220 | 每层首次受击减半 |
|
||
| unlock_echo 回声强化 | 300 | 侧向回声加报距离档(墙闷/开口各 2 档→按远近换 2 组资产音量) |
|
||
| unlock_blade 武器 | 450 | 开局自带短刃(atk+1) |
|
||
|
||
- 经济核算:一局 40-120 金(怪 3-15/只 + 堆 10-30 + 窟主 50-80)→ 约 2-5 局解锁一项,死亡有可见成长(P2「下周目你会更强」)。
|
||
|
||
## 8. 数值表
|
||
|
||
### 8.1 玩家与敌人
|
||
|
||
```
|
||
maxHp = 20 (+vigor 6);atk = 2 + atkLv(0..4, 每4杀+1) + 武器(0..2)
|
||
dmg = max(0, atk - 敌def) ;敌打你 = max(0, 敌atk - 甲) × 格挡系数(0.2/1.0)
|
||
格挡钳制:擦伤 min 0;L1-2 怪 ATK 0-1 → 学期零惩罚
|
||
```
|
||
|
||
| 敌型 | 层 | HP | ATK | 行为 | 脚步音色类 | 掉金币 |
|
||
|---|---|---|---|---|---|---|
|
||
| 盲眼蝠 | 1 | 4 | 0 | 缠斗 | small | 3 |
|
||
| 窟鼠 | 2 | 6 | 1 | 缠斗 | small | 5 |
|
||
| 蘑菇怪 | 3 | 8 | 2 | 缠斗 | bug | 8 |
|
||
| 窟主·石甲蟹 | 3 | 20 | 3 | Boss 绕位 | heavy | 50 |
|
||
| 绕行者 | 4 | 10 | 2 | 绕位 | bug | 10 |
|
||
| 洞蛛 | 5 | 12 | 3 | 缠斗 | bug | 12 |
|
||
| 窟主·腐沼蟒 | 6 | 26 | 4 | Boss 绕位 | heavy | 80 |
|
||
| 石像卫 | 7 | 16 | 4 | 绕位 | heavy | 15 |
|
||
| 窟中之王 | 7 | 36 | 5→6 | Boss 绕位 | heavy | 通关 |
|
||
|
||
装备(拾取即自动装备,仅播报不管理):短刃 atk+1(L3 起)、阔刃 atk+2(L6 起)、皮甲 敌伤-1(L4 起)。药水:腰带限 1 瓶,HP≤25% 自动喝(回 40% maxHp,向上取整)。
|
||
|
||
平衡校验:L1 atk2 打蝠 2 刀;L3 atk3+短刃=4 打蟹 5 刀、蟹打裸装 3/下 vs 20HP+药 → 不格挡危险、会格挡稳过;L7 atk2+3+2=7 打王 6 刀、王打 5-6/下 vs 26HP → 必须格挡+药,Boss 战有真实技巧收益。
|
||
|
||
## 9. 音频三层
|
||
|
||
### 9.1 BGM(4 首,Mureka,-20 LUFS,ogg+m4a,无缝循环)
|
||
|
||
| id | 状态映射 | Mureka prompt(英文) |
|
||
|---|---|---|
|
||
| bgm/menu | TITLE / MENU / SETTLE(结算 volume 0.5) | "Dark cavern ambient pad, deep sub drone with distant water drips, calm and mysterious, no drums, no vocals, seamless loop" |
|
||
| bgm/explore | DRILL / EXPLORE / DESCEND | "Dark cave exploration ambient, low ominous drone, sparse echoing sonar-like pings, slow dripping rhythm, tense but calm, no vocals, seamless loop" |
|
||
| bgm/battle | BATTLE 普通怪 | "Tense dungeon battle music, pounding tribal drums 95 bpm, dissonant low stabs, claustrophobic urgency, no vocals, seamless loop" |
|
||
| bgm/boss | BATTLE 窟主/终 Boss | "Epic dark cavern boss theme, heavy drums 115 bpm, low brass swells, rising dread, no vocals, seamless loop" |
|
||
|
||
切换全部走引擎 crossfade;语音 duck 20% 引擎自动。danger 不切 BGM(guide danger earcon + heartbeat 震 + st_hp_4 承担)。
|
||
|
||
### 9.2 SFX(~34 个;SA3=Stable Audio 3,SYN=程序合成;全部双格式、真峰值 ≤-3dBFS)
|
||
|
||
| 类别 | id | 方式 | prompt 草稿 / 说明 |
|
||
|---|---|---|---|
|
||
| 脉冲 | `pulse` | SYN | 180→60Hz 正弦扫频+噪声瞬态,0.15s,中频突出 |
|
||
| 正前回声 | `echo_1..4` | SA3 | "close stone wall echo slap-back, bright, short, dry"(2:"mid distance cave echo, moderate tail" 3:"far cave echo, darker longer" 4:"very far cavern echo, long dark tail, huge space") |
|
||
| 材质尾音 | `mat_stone` `mat_door` `mat_stair` `mat_water` | SYN | 闷石敲/空心木咚/金属当啷/水滴湿滑,各 0.2-0.25s |
|
||
| 侧向回声 | `side_wall_l/r` `side_open_l/r` | SYN | 立体声文件:能量只在 L 或 R 声道;闷哒 vs 空旷回灵(SA3 不达标时程序写 L/R 通道兜底) |
|
||
| 移动 | `step_own` `step_turn` `bump` | SYN | 单步/拖步转身/撞墙闷响 |
|
||
| 拾取 | `coin` `potion` `gear` | SYN | 金币叮当/喝药咕咚/换装金属轻响 |
|
||
| 转层 | `stairs_down` | SA3 | "descending old metal stairs, deep cavern reverb" |
|
||
| 怪物脚步 | `mob_step_small_l/c/r` `mob_step_bug_l/c/r` `mob_step_heavy_l/c/r` | SA3 ×3 + ffmpeg pan 派生 ×6 | "small beast skittering steps, dry";"insect chitin crawling clicks";"heavy armored thudding steps"——各生成 1 条单声道,派生左/中/右三份 |
|
||
| 攻击前摇 | `tel_small` `tel_bug` `tel_heavy`(+派生 l/r) | SA3 | 小怪扑击嘶声/虫肢节摩擦/重怪蓄力重踏低吼 |
|
||
| 战斗 | `atk_swing` `atk_hit` `parry` `hurt` `enemy_die` | SYN/SA3 | 挥拳风声(SYN)/击中甲壳钝响/格挡亮叮(SYN)/受击闷击(SYN)/倒地(SA3) |
|
||
| 升档 | `levelup` | SYN | 上行钟声琶音(内容音,允许) |
|
||
|
||
monsters 脚步 pan 派生工艺:`ffmpeg -i in.ogg -af "stereotools=mlev=0.7:sbal=-.8" out_l.ogg` 等三向派生,省 SA3 任务数(SA3 实际 ~14 个)。
|
||
|
||
### 9.3 voice
|
||
|
||
125 句 ×2 性别(Serena / Uncle_Fu),无 NPC(怪物不说话,遭遇靠 `btl_start_*` 报名)。分组、台词、触发全表见 [16-dungeon-voice-ids.md](16-dungeon-voice-ids.md)。数字全分档整句(层 7 句、血量 4、金币 4、面朝 4、威胁 4、战利 3、死因 3、元进度 3),无运行时拼接。
|
||
|
||
## 10. 向导层接入(engine/guide.js,本作不新增 earcon 词表)
|
||
|
||
### 10.1 状态签名(进入状态时播)
|
||
|
||
| 状态 | guide.state() | 时点 |
|
||
|---|---|---|
|
||
| TITLE / MENU | `menu` | 进标题、开菜单 |
|
||
| DRILL / EXPLORE / DESCEND | `explore` | 进教学、进探索、下楼完成 |
|
||
| BATTLE | `battle` | 遭遇即播(与 btl_start_* 同点) |
|
||
| SETTLE | `settle` | 死亡/通关结算进入 |
|
||
| danger | `danger` | 血量首次 ≤25%(每层 1 次)+ Boss 半血 |
|
||
|
||
choice 签名本作不使用(无抉择态;菜单是 menu)。菜单回绕不播 wrap 句(菜单 ≤4 项,循环成本低,省台词预算)。
|
||
|
||
### 10.2 turn cue 与看门狗
|
||
|
||
| 等待点 | arm | at | say(full 档;L2 级一律纯 nudge,绝不逐字重播) |
|
||
|---|---|---|---|
|
||
| BATTLE 玩家回合(每回合) | guide.turn | — | turn earcon + confirm 震 |
|
||
| BATTLE 玩家回合无操作 | watchdog | [15s, 30s] | `gd_turn` / null |
|
||
| EXPLORE 静止 | watchdog | [10s, 25s] | `gd_pulse` / null |
|
||
| DRILL 每步等待 | watchdog | [8s] | 无语音,纯 nudge(教学句不重播) |
|
||
|
||
### 10.3 卡关三级提示(EXPLORE,escape 先例;"无操作时长"计时,任何有效手势重置)
|
||
|
||
进展事件=拾取/战斗胜利/踏上楼梯格/击败窟主/下潜。无进展 60/120/180s →:
|
||
|
||
| 级 | 内容 | 附带 |
|
||
|---|---|---|
|
||
| L1 暗示 | 楼梯未找到 → `stair_hint_a`;窟主未清 → `stair_hint_b` | warning 震 |
|
||
| L2 明示 | `stair_dir_e/s/w/n`(绝对方位;与上滑面朝句同坐标系,自洽) | warning 震 |
|
||
| L3 带路 | 进入导航模式:每步播 `nav_e/s/w/n`,抵达播 `nav_done`;此后每 60s 重复直至进展 | warning 震 |
|
||
|
||
提示到点遇语音在播 → 入待播队列(escape round2 礼仪),玩家操作即作废未播级别。纠错句 idle-only + 过期丢弃(引擎已实现)。
|
||
|
||
## 11. 存档设计
|
||
|
||
```json
|
||
{
|
||
"schema": 1, "game": "dungeon", "updatedAt": 0,
|
||
"summary": "第3层 · 伤势不轻 · 金币268 · 遗产2/5",
|
||
"meta": { "gold": 268, "unlocks": ["potion", "vigor"], "bestFloor": 5, "runs": 7, "kills": 23 },
|
||
"run": {
|
||
"seed": 42, "floor": 3, "pos": [5, 2], "face": "E",
|
||
"hp": 11, "atkLv": 2, "weapon": 1, "armor": 0, "potion": 1,
|
||
"veilUsedFloor": false, "killsRun": 9, "looted": [], "cleared": [], "parries": 4
|
||
}
|
||
}
|
||
```
|
||
|
||
- **summary 生成规则**:`第{floor}层 · {血量档名} · 金币{meta.gold} · 遗产{unlocks.length}/5`;死亡后 run=null,summary=`深处归来 · 金币{gold} · 遗产{n}/5`。
|
||
- 保存时机:拾取/战斗结算/下潜/菜单"保存并回大厅"/死亡结算。localStorage + PUT 服务器(00 文档 §4)。
|
||
- 读取:有 run → TITLE 播 `sys_continue_ask`,单击继续 → 恢复 `run` + 重放该层 seed 生成 + **局面复述链**(st_floor+st_hp+st_face+st_threat,≤5s 可操作);右滑 = 重掷新周目(run 清空重掷 seed,meta 保留)。无 run 单击 = 新周目(meta.unlocks 生效播报后进 DRILL/EXPLORE)。
|
||
- 归因真话依据:`parries`(全程格挡数)、lastHit 记录(击杀者类型/是否背刺/是否未格挡)在死亡结算选 `die_cause_*` 与 `die_adv_*`,句必须与真实行为对应。
|
||
|
||
## 12. playtest 断言清单(登记 13 号文档新增 §dungeon;mock 机制对齐 web/rpg/test/playtest.mjs)
|
||
|
||
**关键路径**(seed=42,首周目 L1→L3 窟主,≤260 手势):
|
||
1. 启动 → TITLE(无存档)→ 单击 → DRILL:D0..D9 逐步断言 voice 序(tut_d0→…→tut_done)与 drillStep 递进;D1 断言 pulse 与 echo 播出;D4 长按断言 pos 前进 ≥2;D8 乱按/不格挡断言 HP 不降(教学怪 ATK=0);下滑跳过教学路径断言(二周目不再进 DRILL)。
|
||
2. **seed 复现**:`opts.seed=42` 两次构造 → `_floor.grid`、楼梯/怪物/宝物坐标逐格相等;L1 尺寸 ===7;起点安全区(怪 BFS 距离 ≥3)。
|
||
3. **脉冲回声信息正确**:起点面朝 'N',按 grid 计算正前墙距 → 断言 echo_N 于脉冲后对应延迟±50ms 播出、volume 档正确、材质尾音 id 正确;左右侧向 side_* id 与 grid 一致;遍历 seed=42 的 L1 全网格 ×3 面向抽查 ≥30 组(回声编码 === grid 真值);**双击重发**断言资源零消耗且回声 id 与上次一致(未移动时)。
|
||
4. 拾取:走到金币格 → coin 音 + meta.gold 增加且 st_gold 档位相应跳档;药水满腰带 → evt_potion_full 且药留在原地。
|
||
5. 战斗路径(L2 窟鼠):进入 BATTLE → 回合流转(玩家回合 turn→敌前摇→结算循环);前摇内双击 → 敌伤 ×0.2(HP 差值断言);窗口外双击 → error 震、全额伤;长按逃跑 → 必成且 HP 真实减 ATK、怪留原格;击杀 → loot 档句 + meta.gold 入账。
|
||
6. 下潜:踏上楼梯格 → sys 提示句 → 单击 → DESCEND → floor=2 且 grid 由 floorSeed 重生成(再断言复现)。
|
||
7. **死亡与元进度**:L3 窟主战挂机至死 → 归因链序 `die_intro → die_cause_*(与 lastHit 真实来源一致,Boss 重击 → die_cause_2)→ die_adv_*(按 parries=0 → die_adv_1)→ st_floor_3`;断言 meta.gold = 死亡前+本局所得、`bestFloor` 更新;单击 → TITLE → 新周目 seed≠42、meta.unlocks 按金币阈值播 unlock_* 且起始 maxHp 生效(vigor)。
|
||
8. 菜单/帮助:探索下滑 → 模态菜单(单击循环播项名、双击确认、上滑=help、下滑=关闭);"保存并回大厅" → PUT 断言 data.summary 命中生成规则。
|
||
9. **中断复述**:存档重进 → sys_continue_ask → 单击继续 → 落在存档点 → 自动局面复述链(st_floor→st_hp→st_face→st_threat 顺序断言);上滑同链可重放。
|
||
|
||
**边界断言**:
|
||
- 学习期零惩罚:L1 全怪 ATK===0、L2 ≤1(读表断言);L1 全程乱按 HP 不变。
|
||
- 撞墙:长按顶墙 → bump + pos 不变 + HP 不变。
|
||
- 方位自洽:任意格转身后重脉冲 → 左右 side_* 与 grid 随面朝交换;身后方向从不产生回声资产。
|
||
- 怪物脚步:怪每步播 mob_step_<class>_<pan>,pan/音量档与相对方位、距离一致(含背后=中央减半口径)。
|
||
- 绕位怪背刺:未转身挨打 → 全额伤 + `btl_behind`;转身后格挡 → ×0.2。
|
||
- 缠斗怪左右滑 → `btl_no_turn`(每场 1 次)+ error 震。
|
||
- 自动喝药:HP≤25% 且有药 → HP 回升档 + potion 音;无药时不播不炸。
|
||
- 卡关三级:无操作快进 60/120/180s → stair_hint_* → stair_dir_* → nav_* 链;任意进展事件后重置分级;提示在语音在播时排队不顶句。
|
||
- 看门狗:探索 10s/25s、战斗回合 15s/30s 逐级触发且第 2 级纯 nudge 无语音。
|
||
- 脉冲冷却 1.2s:冷却期单击/双击 → error 震、无 echo。
|
||
- 屏蔽任意 voice 资产:流程不卡死(看门狗兜底推进)。
|
||
- guide=off:全静、零定时器(全局既定契约)。
|
||
- 资产齐套:voice 双性别目录 id 集合一致、sfx/bgm 双格式(assetExists 口径)。
|
||
|
||
**失败即不通过**:死亡归因句与真实死因不符(归因假话);同 seed 两次生成不一致;教学怪造成 HP 损失;双击重发扣资源;回声编码与 grid 矛盾。
|
||
|
||
## 13. 节奏曲线与单局核算
|
||
|
||
- 每层节奏:进层签名句(5s) → 探索(脉冲-移动循环,2-3 分钟)→ 1-3 场战斗(每场 40-60s,窟主 90s)→ 拾取 1-3 处 → 楼梯。每 30-60s 一个奖励点(金币/击杀升档/装备),满足方法论 L5。
|
||
- 满通 24.0 分钟(§6.2 累计列);普通玩家死于 L4-6 → 单局约 14-19 分钟;二周目起(遗产加持)更深。首局若速死(L2-3,~8 分钟)由元进度钩子拉重开——roguelike 死亡结算 ≤15s 且必带「下周目更强」,重开入口单击 ≤2s(A4/L5)。
|
||
- 无任何倒计时(P4);高手速度感=长按连走+免听完整回声直走(P2),脉冲冷却不限制移动。
|
||
|
||
## 14. 自检清单
|
||
|
||
| # | 硬约束 | 自检 |
|
||
|---|---|---|
|
||
| 1 | FTUE 首输入 <60s | D1 首次脉冲 ≈t+15s ✓(§5) |
|
||
| 2 | 首个正反馈 ≤3min | D8 教学战胜利 ≈t+2min;L1 首金币 ≈t+2.5min ✓ |
|
||
| 3 | 学习期零惩罚 | L1 怪 ATK=0、L2 ≤1,学期内死亡不可能;撞墙无伤;格挡失败无额外惩罚 ✓ |
|
||
| 4 | 失败三问归因 + 真话律 | 死亡链 die_cause_*/die_adv_* 按 lastHit 与 parries 真实选择;挥空/背刺/逃跑挨打均有真话句 ✓(§12 失败即不通过项锁定) |
|
||
| 5 | 手势宪法三件套不豁免 | 上滑=状态、下滑=菜单、双击=重听(重发脉冲) 全状态保留 ✓;移动/脉冲偏离已登记 E1-E4 且教学讲清 ✓ |
|
||
| 6 | 向导层 | 签名 5 态(不新增 earcon);turn cue 每回合;看门狗 [10/25][15/30]s 第 2 级纯 nudge;卡关 60/120/180 三级不报答案(带路只给方向);纠错 idle-only ✓(§10) |
|
||
| 7 | 方法论反模式 | 提示永不逐字重播(L2 级纯音频);成功后只给新短句肯定;单次播报 ≤3 选项(菜单逐项);并发声流 ≤2(频带分离+冷却);语义靠语音、earcon 用引擎统一词表 ✓ |
|
||
| 8 | 方位降维 ≤4 | 回声前/左/右(身后不报);战斗面前/左/右/背后;无连续角度 ✓ |
|
||
| 9 | 台词规范 | 一句一文件整句;全部 ≤25 字(<40 上限);数字全分档整句;"重听"写"重新听"、"重发"写"再发一次";同游戏同 instruct 常量;无语速词 ✓(voice-ids 注记) |
|
||
| 10 | P1 视障评委 | 双击免费重发脉冲;status key 全状态可用;方位/威胁模板句固定;归因真话;可打断/长按快进/2.2 档语速引擎既有;绝不当白痴(不简化深度:绕位/Boss/元进度保留)✓ |
|
||
| 11 | P3 通勤 | 单手免视全手势可达;上滑一句话复述局面(层/血量/面朝/威胁);任意时刻菜单存档退出;死亡不清 meta ✓ |
|
||
| 12 | P4 工人 | 教学逐字跟练+失败 2 次自动过;容错高(ATK0 怪/自动喝药/必成逃跑);无倒计时;零新手势(复用 swipe/tap/longpress)✓ |
|
||
| 13 | P2 大学生 | 元进度结算「下周目你会更强」;击杀升档+装备即时成长;长按连走速度感;死亡重开入口 ≤2s ✓ |
|
||
| 14 | seed 可复现 | floorSeed 派生公式 + opts.seed 注入 + 复现断言(§6.1/§12.2),与 playtest 种子 PRNG/强制队列/虚拟时钟机制兼容 ✓ |
|
||
| 15 | 三套信息自洽 | 单源 grid 查询;回声=静态地形/脚步=动态怪物/播报=绝对面朝,模板句固定,语义域零重叠 ✓(§3.2) |
|
||
| 16 | 引擎能力边界 | 无实时 pan(预 pan 资产);无引擎外节点(setTimeout+volume 调度);tap 300ms 延迟规避(格挡绑双击);30MB 预算内 ✓ |
|
||
|
||
---
|
||
|
||
## 实现核对(测试 agent,2026-09-02)
|
||
|
||
> 测试载体:`web/dungeon/test/playtest.mjs`(mock 层+虚拟时钟照 `web/rpg/test/playtest.mjs`,
|
||
> 1277 断言全绿,`tools/node/bin/node web/dungeon/test/playtest.mjs` 退出码 0);
|
||
> 会话剧本 `web/harness/sessions/dungeon.mjs`(`session` 主线弧线 + `sessionStress` 乱按压力,
|
||
> 经 `web/harness/transcribe.mjs dungeon [--session sessionStress]` 跑通,全程零资产缺失、零死锁)。
|
||
|
||
### §12 关键路径
|
||
|
||
| # | 断言 | 结果 |
|
||
|---|---|---|
|
||
| 1 | 启动→TITLE→DRILL:D0..D9 逐步 voice 序(intro_1/2+tut_d0→…→tut_done)与 drillStep 递进;D1 pulse+echo;D4 长按 pos ≥2;D8 乱按 HP 不降;下滑跳过+二周目不再进 DRILL | ✅(场景 2;D2 错手势重教、D3 连错 2 次自动通过、D8 乱按 80 手势 HP=20 一并覆盖) |
|
||
| 2 | seed=42 两次构造 grid/怪/宝/楼梯逐格相等;L1 尺寸 7;起点安全区 BFS≥3(教学局非门怪 ≥4) | ✅(场景 1:7 层全查 + 实流 `_floor` 与纯函数一致) |
|
||
| 3 | 回声编码 === grid 真值:全地板格 ×4 面向扫描(≥30 组),echo_N 延迟/音量、材质尾音 +120ms、侧向 +350ms 预 pan 资产 L/R;双击重发零消耗且编码一致 | ✅(场景 2:104 组合全扫,代表组精确到 0ms;身后不报=恰两路侧向) |
|
||
| 4 | 拾取:coin 音+meta.gold+金币档跳档(45+堆≥55→st_gold_2);药水满腰带 evt_potion_full 且药留原地 | ✅(场景 4) |
|
||
| 5 | 战斗:单击命中、缠斗 btl_no_turn 每场 1 次、前摇内双击 ×0.2(钩子 atk=10 → 伤 2)、窗口外/不格挡全额、长按逃跑必成挨一下怪留原格、击杀 loot 档+meta.gold | ✅(场景 5) |
|
||
| 6 | 下潜:踏上楼梯 sys 提示→单击→DESCEND→floor=2 且 grid 重生成复现 | ✅(场景 2) |
|
||
| 7 | 死亡与元进度:归因链序+真话、meta.gold 入账、bestFloor/runs、遗产购置播报、新周目 seed 重掷+继承(vigor 26/potion 1) | ✅(场景 6a/6b/6c 三种死因矩阵) |
|
||
| 8 | 菜单:单击循环/双击确认/上滑帮助/下滑关闭;「保存并回大厅」→ sys_saved → `'../'`;summary 生成规则 | ✅(场景 7 + 场景 2 D7) |
|
||
| 9 | 中断复述:sys_continue_ask→单击→st_floor→st_hp→st_face→st_threat 有序;上滑同链可重放 | ✅(场景 8) |
|
||
|
||
### §12 边界断言
|
||
|
||
| 断言 | 结果 |
|
||
|---|---|
|
||
| 学习期零惩罚:L1 全怪 ATK=0、L2 ≤1(读 `_battle.atk`);L1 乱按 HP 不变 | ✅ |
|
||
| 撞墙:bump + pos 不变 + HP 不变 | ✅ |
|
||
| 方位自洽:转身后重脉冲左右 side 随面朝交换(含全扫描 4 面向) | ✅ |
|
||
| 怪物脚步:pan/音量档(0.9/0.6/0.35)、背后中央减半(1 格 0.45、6 格 0.175)、>8 格不播、≤2 格 heartbeat | ✅ |
|
||
| 绕位怪:每 2 回合换位(右→后→左确定性轮换)、脚步报位、背刺 btl_behind+全额、转身追击命中、转身后格挡 ×0.2 | ✅ |
|
||
| 缠斗怪左右滑 → btl_no_turn(每场 1 次)+ error 震 | ✅ |
|
||
| 自动喝药:HP≤25% 有药 → 回 40% maxHp(ceil)+ potion 音+档播报;Boss 半血 ATK+1 + btl_half_*;Boss 逃跑拒绝 btl_flee_boss | ✅ |
|
||
| 卡关三级 60/120/180s:stair_hint_a→stair_dir_*→nav_* + 导航每步播方向 + nav_done;进展重置分级(重置后从 L1 重新触发);语音在播入队不顶句、操作作废、未播级别可重新触发 | ✅(场景 9) |
|
||
| 看门狗:探索 10s gd_pulse、战斗 15s gd_turn(guide=full);DRILL 8s 纯 nudge 无语音 | ✅(场景 10) |
|
||
| 脉冲冷却 1.2s:冷却期单击仅 error 震、无第二次回声 | ✅ |
|
||
| 屏蔽任意 voice:遭遇报名/下潜旁白被屏蔽 → guard 兜底推进不卡死 | ✅(场景 11) |
|
||
| guide=off:30s 空闲零播放、零定时器 | ✅ |
|
||
| 资产齐套:voice 双性别 125 句集合一致、sfx/bgm 双格式、game.js 静态引用展开全存在、运行期零缺失 | ✅ |
|
||
| E3:对局期 setRateBoostEnabled(false)(DRILL/EXPLORE/BATTLE/DESCEND)、TITLE/MENU/SETTLE 恢复 true | ✅(rateLog 断言) |
|
||
| E4:菜单=暂停——探索游走冻结、战斗前摇剩余窗口冻结、恢复恰结算一次(无重复回合) | ✅(场景 7;前摇内下滑原本不可达,见 bug ⑧) |
|
||
|
||
**§12 失败即不通过项**:死亡归因真话(cause/adv 与 `_deathInfo.lastHit` 一致,三种死因矩阵验证)、同 seed 两次生成一致、教学怪零伤害、双击重发零消耗、回声编码与 grid 一致——全部锁定 ✅。
|
||
|
||
### 与设计文档的偏差(记录不大改)
|
||
|
||
1. **归因句粒度**:台词表死因仅 3 档(小怪拖垮/Boss 重击/背刺),无「指认具体怪 A」整句资产;真话律落地为 `_deathInfo.lastHit.type` 与 cause 档位一致(playtest 断言 `lastHit.type==='crab'/'rat'/behind` ↔ die_cause_2/1/3)。资产表内不存在更细句子,不算偏差。
|
||
2. **战斗上滑第 2 段**:§2.2「敌方血量档」以 `btl_half_*`(半血阶段句)近似——台词表无 foe_hp_* 分档资产。
|
||
3. **§5 D4「撞墙也算过」**:实现额外把 D4 自动面向东侧 2 格走道+边界,保证 ≥2 格可达成(生成约束注释已写明)。
|
||
4. **L1 安全区**:非门怪 BFS≥4(严于规格 ≥3),避免 D4/D8 演练路径撞上第二只怪,注释已写明。
|
||
5. **die_adv 兜底**:挡过且跑过(parries>0 && flees>0)死于非背刺 → die_adv_1(建议句非过去事实句,规格未覆盖该象限,取最无风险句)。
|
||
6. **格挡成功播 `btl_parried`**:规格表 B 组 trigger=「格挡成功(减伤 80%)」,原实现该资产死引用,已接线(见 bug ②)。
|
||
|
||
### 修过的 bug(web/dungeon/game.js)
|
||
|
||
1. **归因真话律**:`run.parries++` 原在双击瞬间计(背对双击也计),歪曲 die_adv 选句依据——改为仅结算判成「面对且格挡成功」时计数。
|
||
2. **`voice/btl_parried` 死资产**:格挡成功(减伤 80%)从不播报——接线进 `_enemyResolve` 反馈链。
|
||
3. **卡关提示「未播级别计入 shown」**:入待播队列即标已提示,被玩家操作作废后该级永不重触发(违反 §10.3/escape round2 礼仪)——改为真正播出才计入,另加待播去重。
|
||
4. **终局层 L3 带路死路**:`_navRouteVoices`/`_navSpeakStep` 只查 `stairs`,L7 无楼梯致导航哑火——回退 `stairs || throne`(与 L2 明示同口径)。
|
||
5. **SETTLE 双击=重听结算链**:原只重播 `settle_again` 尾句——存 `_settleChain` 整链重放(§2.3)。
|
||
6. **DRILL D1 无看门狗**:`_drillEnter(1)` 无分支——补 `_drillWatchdog(1)`(§10.2 教学每步 [8s])。
|
||
7. **DRILL D8 上滑=做错**:§4.3 状态键「教学后可用」(D7 已教)——D8 补 up→状态链。
|
||
8. **E4 豁免半实现**:战斗前摇(telegraph)内下滑走 error 分支、菜单打不开,`_menuPause` 冻结机制不可达——前摇内补 `down→_openMenu()`,战斗任意时刻可暂停。
|
||
|
||
### 遗留 TODO
|
||
|
||
- 教学期 L1 游走静默:DRILL 内非教学怪不游走(`_startMobs` 仅 EXPLORE 调用),设计未明确,维持现状(D9 后下潜即恢复)。
|
||
- 木门/水材质(mat_door/mat_water):生成器不产出,资产与编码路径已备(`_echoScan` 注释),待未来层型。
|
||
- `meta.runs` 不统计 TITLE 右滑放弃的周目(仅死亡/通关计入);规格未明确。
|
||
- 会话剧本主线弧在随机图极小概率把窟主打死(玩家成长超预期时)——剧本有「没死就继续走」兜底分支,不影响零死锁承诺。
|
||
|
||
### R2 修复回归(2026-09-02,D0-1..D0-3 + D1-1..D1-6 结构修复轮)
|
||
|
||
> 证据源:research/new8/dungeon/round1/(synthesis + live-player + p1-blind)。本轮只修结构不加新内容;
|
||
> 引擎侧按产品裁定允许改动(web/engine/audio.js、gesture.js)。回归:8 套 playtest + guide.test 全绿
|
||
> (dungeon 1277→**1610** 断言;escape 140 / radio 221 / rhythm 141 / rpg 188 / simon 132 / space 155 /
|
||
> storyline 82 / guide 122,0 失败,radio 在并行修复中仍全绿)。
|
||
|
||
#### D0-1 语音抢占死循环(P0)
|
||
|
||
- **根因(引擎级竞态,非 DRILL 泄漏本身)**:`WebAudioEngine._playVoice` 的「顶掉当前对白」检查在
|
||
`await this._load(id)` **之前**,而 `this._voice = token` 在其后无条件赋值——同一 tick 双调度语音
|
||
(教学 D1:反馈句 `tut_echo_come` 裸 `_speak` + `_drillPass` 立即 `_speakGuarded` 下一句指令)在加载
|
||
竞速后,输家的 token 被静默顶号:**ScriptProcessorNode 永远无人 disconnect(僵尸节点空转抽 buffer)**,
|
||
其 endTimer 亦因 `_finishVoice` 的 token 校验提前 return 而永不清理。R1 live 证据完全吻合:t=130.2s
|
||
D1 首脉冲后 tut_echo_come 僵尸节点持续抽读,探针按「voice buffer 被抽读」记段 → 71 分钟 35,745 次
|
||
「播放」99.7% 被「掐死」、nav_n↔tut_echo_come 互掐 33,297 次;跳教学二周目 0 抢占(无 D1 双调度)。
|
||
- **修**:① `audio.js _playVoice` 在 await 之后、赋值之前补杀当前 token(顶掉语义在加载完成后复核,
|
||
后到者胜、败者必被杀);② 游戏 D1/D6 反馈句并入 `_speakGuarded` 推进链(消灭同 tick 双 play 的源头),
|
||
`_drillDone`/`_drillSkip` 收尾显式 `guide.disarm()`;③ **全线风暴保险丝**(`audio.js play()` voice 入口):
|
||
同一 voiceId 3s 窗口内被调度 ≥5 次 → 丢弃后续并 console.error(每 id 一次)。
|
||
- **断言**:`保险丝:同 id 3s 窗口内前 4 次调度放行 / 第 5 次起丢弃 / 其他 id 不受累 / 窗口滑出恢复 /
|
||
只作用于 voice 层`(真实 WebAudioEngine 单元断言,场景 12A);`D1: 反馈句与下一步指令句串行`(场景 2);
|
||
`纯探索 60s 零操作语音调度有上界(≤3 句;R1 风暴同期 >2000 句)`(场景 12C)。
|
||
|
||
#### D0-2 回声信息真实性(P0)
|
||
|
||
- **根因**:① 侧向 `_sideScan` 把「整条侧射线到边界全通」才判开口——房间/走廊地型恒为假 → side_open
|
||
两局 40+ 脉冲 0 次、能走的方向报墙;② 站在楼梯格上脉冲走射线扫描(楼梯在脚下不在面前)→ mat_stair
|
||
0 兑现,教学「铁器=楼梯」成谎言(P1 专项:165 脉冲 0 铁器,含站在楼梯上 t+142.2)。
|
||
- **修**:回声与 grid 单源重接——侧向第一格地板=开口(§3.1「墙=闷/开口=空旷」2 档口径,墙保留射线距离);
|
||
`_echoScan` 站楼梯格 → 贴身(dist1)铁器回声。
|
||
- **断言**:场景 2 D9 全扫描(全地板格 × 4 面向真脉冲)truthScan 同步更新;场景 12B **全网格一致性**:
|
||
seed=42 L1..L7 每格 × 4 面向 `_echoScan`/`_sideScan` 与测试侧独立真值逐格相等(≥200 格)+
|
||
`全网格存在侧向开口(R1 全程 0 次 → 现扫到 N 格次)` + 各层站楼梯=贴身铁器。转写复核:主线局
|
||
mat_stair/side_open 均稳定出现。
|
||
|
||
#### D0-3 探索内容真空(P0)
|
||
|
||
- **① 密度下限**:`_fillMainPathDensity`——start→楼梯(终局=王座)最短路(BFS 距离梯度回溯,确定性)
|
||
每 ≤12 格必有内容遭遇,超限段中点补本层已有普通怪型(不加新怪种,≤8 只,安全区口径不变)。
|
||
断言:场景 1 各层 `主路径内容密度(最长真空 ≤12 格)` + 构成 ⊇ 规格表。
|
||
- **② 探索呼吸层**:新 SYN 资产 `sfx/amb_cave`(gen_sfx.py:45-320Hz 带通空气声 + 4 整周期/10s 呼吸
|
||
LFO + 两粒远处水滴;10s 无缝循环源:去直流、环回交叉淡化、**不首尾淡化**,解码回读 ogg -4.20dBFS /
|
||
m4a -4.00dBFS、|直流|≤0.001、循环接缝 -25dB(rel peak);20 文件 ×2 格式全过 QC)。接线:EXPLORE 静默
|
||
≥20s(无手势无人声)播 amb_cave(volume 0.35,20s 节奏不连播)。guide=off 契约不变(环境垫属内容层,
|
||
场景 4 的零播放断言已按此口径更新并加 amb 专项断言)。
|
||
- **③ 下潜演出**:游戏代码本身完好(playtest 场景 2 一直断言 stairs_down→evt_descend→st_floor_N 通过);
|
||
R1 转写零演出为**剧本伪影**:(a) navigate 按 1 格/按建模,但 harness `waitVirtual(120)` 在虚拟时钟会
|
||
越过 +500ms 步进计时器(advance 至少推进到下一到期定时器)致每按多走 1+ 格、永远踩不准楼梯格;
|
||
(b) D9 用 `pulse:false` 违反「玩家靠耳朵」红线且无到站重试。修:navigate 立即松开(恰 1 格)+ `until:
|
||
'stairs'` 到站口径 + 逐层验证的下潜循环(tapDescend 只在真踩楼梯时出手,DRILL D9 同样受理)。
|
||
复核:主线转写两次运行弧线完全一致(两次下潜全套演出:stairs_down→evt_descend→st_floor_N)。
|
||
- **④ 卡关计时器**:计时与分级 R1 live 已证完好(64s/120s/180s 准时);转写 350s 未触发系剧本持续手势
|
||
重置 lastAct(§10.3 口径「任何有效手势重置」)+ 上句的导航伪影。断言:场景 12C `卡关 L1 暗示 60s 自然
|
||
触发(纯探索、无 hook)`。
|
||
|
||
#### D1 级修复
|
||
|
||
- **D1-1 菜单关闭无回执**:live 复核实为「关了但零反馈,下次下滑又重开」——路由本身无恙。修:`_closeMenu`
|
||
补状态签名 earcon(回 EXPLORE/DRILL=explore、BATTLE=battle、SETTLE=settle、TITLE=menu)。断言:场景 10
|
||
`菜单关闭播回程状态签名(menu → explore)`。
|
||
- **D1-2 D9 看门狗兜底**:`_drillWatchdog9()`(8s nudge、30s 升级播 stair_hint_a——自寻路步必须有救援);
|
||
D9 每次手势后重挂(onGesture 的 disarm 会清掉它,而 D9 的脉冲/转身不推进步骤)。断言:场景 10 两级触发
|
||
+ 升级句。
|
||
- **D1-3 导航指墙 + 蘑菇软封锁**:① 带路改为**最短路第一步方向**(`_navFirstStepDir`:从目标反向 BFS 取
|
||
梯度下降邻格),逐步校正;同向 ≥1.5s 才提醒(改向必播)——既不指墙也不撞全线保险丝(导航句同 id 连发
|
||
会计熔断)。断言:场景 9 `带路方向与 grid 最短路一致 + 第一步必可通行 + 节流单测(同向连呼 3 次只播 1)`。
|
||
② 蘑菇/所有游走怪**相邻即扑击接战**(`_mobStep` 移动后与玩家正交相邻 → `_encounter`),杜绝 R1 L3
|
||
「贴身绕圈、堵格不开战」35 分钟软封锁。断言:场景 12C 相邻怪必然开战。
|
||
- **D1-4 战斗结束 BGM**:教学战胜利/逃跑回 DRILL 的两条路径原先不切回 bgm/explore(只有菜单关闭路径恢复
|
||
——R1 转写 t=35.8 起战斗鼓点泡全程的组成因素之一)。两条路径补 `bgm/explore` 淡入。断言:场景 5 胜利/
|
||
逃跑后 `lastBgmEntry === bgm/explore`。
|
||
- **D1-5 金币档**:SETTLE(`_die`/`_settleWin`)原用 run 金币自设阈值(<20/<60),TITLE 用
|
||
`_goldBand(meta.gold)`(<50/≤200/≤600)——同一句 id 两套口径(live:SETTLE meta_gold_3 vs TITLE
|
||
meta_gold_2)。统一为同一分档函数同一账本(遗产购置后的 meta.gold)。断言:场景 6a `SETTLE 金币档 =
|
||
_goldBand(meta.gold)`。
|
||
- **D1-6 三件**:① 长按 boost 闪跳——E3 豁免期快进本就不生效,但 gesture.js 仍外发
|
||
`soundgame:longpress(+end)` 窗口事件(仪器记为 boost on→off 闪跳):`setRateBoostEnabled` 现同步经
|
||
`setLongpressBoostEventsEnabled` 抑制窗口事件(游戏手势回调不受影响)。② D7「菜单被系统自动收」经 live
|
||
事件流复核为风暴伪影(菜单开场链 sys_menu→首项名被僵尸节点反复重放,被听成自动执行 sys_menu_resume)
|
||
——D0-1 修复即愈,无额外代码;playtest 场景 2 已有「菜单保持打开直到玩家下滑关闭」断言。③ 战斗回合
|
||
15s 无操作播合成回执 `sfx/turn_tick`(880Hz 木鱼族,gen_sfx.py,-3.87dBFS)——standard 档无 gd_turn
|
||
语音也有「还到你」的确认。断言:场景 10 turn_tick + 引擎豁免事件抑制(rateLog 既有断言不受影响)。
|
||
|
||
#### 伪影修正(web/harness/sessions/dungeon.mjs)
|
||
|
||
- 「元进度不兑现」关案依据 + 三处剧本脱轨根因:① `waitVirtual(120)` 虚拟时钟越表多走(见 D0-3③);
|
||
② tapDescend 不受理 DRILL(D9 最后一击点不响);③ 单发 navigate+tapDescend 无重试,任何被怪打断即脱轨
|
||
——逐层验证下潜循环修复。另修 navigate 中「走位途中被扑→长按按到战斗里=瞬间逃跑」(R1 live btl_flee×9
|
||
的剧本同款)。复核:主线转写 2 次弧线逐句一致(含归因链与金币档);压力转写 2 次均零死锁走到死亡-重开。
|
||
|
||
#### R2 新增/变更断言口径登记
|
||
|
||
- 场景 1:怪物构成由「逐字相等」放宽为「⊇ 规格表 + 补怪限本层怪种 ≤8」(密度下限所需)。
|
||
- 场景 4:`guide=off 30s 零播放`改为「零语音/零向导播放,唯 amb_cave 按呼吸层口径除外」(内容层 vs
|
||
向导层切分)+ 呼吸层节奏/重置断言。
|
||
- 场景 9:L3 带路从「整段先东西后南北 + 每步必播」改为「最短路首步 + 改向必播/同向 1.5s 节流」
|
||
(§10.3 L3 口径的听感与保险丝友好修订)。
|
||
- 台词/资产表:无新增 voice;新增 sfx `amb_cave`、`turn_tick`(SYN,双格式,QC 数字见上)。
|
||
|
||
### R3 修复回归(2026-09-02,E0-1/E0-2/E1-1/E1-2 收口轮)
|
||
|
||
> 证据源:research/new8/dungeon/round2/(synthesis 裁定 + live-player + p1/p2/p3/p4)。
|
||
> 本轮**引擎零改动**(E0-1 看门狗改造全部在游戏层);radio 并行改动中,回归时全绿。
|
||
> 回归:8 套 playtest + guide.test 全绿,dungeon 1610 → **1642** 断言(escape 140 / radio 246 /
|
||
> rhythm 141 / rpg 188 / simon 132 / space 155 / storyline 82 / guide 122,0 失败)。
|
||
|
||
#### E0-1 看门狗饿死(P0,四票共识)
|
||
|
||
- **根因**:`_stuck.lastAct` 按 §10.3「任何有效手势重置」口径单轨计时——「一直在动手的迷路者」
|
||
(乱按/无效手势)永远攒不满 60s,暗示→明示→带路三级链零触发。live 实证:压力局 253 对 earcon、
|
||
最长 2223s 无语音窗、0 句提示;P1 结论「『每次手势后重挂』的口径恰好被乱按永久饿死」。
|
||
- **修(双轨制)**:
|
||
- ① **无操作轨**(§10.3 原样保留,仅探索态生效):任何有效手势重置,60/120/180s。
|
||
- ② **无进展轨**(新增):只由**状态推进**重置(楼层/格位置/战斗回合/拾取,`_progressTrack()`,
|
||
挂点在 `_walkStep` 移动、`_playerTurn` 回合推进、`_progress()` 事件与各状态进入点),30/60/90s
|
||
三级升级:**earcon**(复用 `sfx/turn_tick`,不占台词通道、不排队)→ **短句**(`stair_hint_a/b`)→
|
||
**导航接管**(`_navRouteVoices()` 最短路首步方向 + 楼梯指向,进入导航模式每步校正、60s 周期重报)。
|
||
- 两轨短句内容同级(无操作轨 L1 = 无进展轨 L2)交叉登记防重复播;明示级 `stair_dir_*` 只由无操作轨
|
||
兑现登记。站立不动(两轨同时走时)经无进展轨 30/60/90s 即升级——比 §10.3 的 180s 更早拿到带路,
|
||
两轨并行取先到;§10.3 的 60/120/180 口径在无操作轨原样保留。
|
||
- **断言(场景 13A/13B)**:`E0-1: 乱按 96s 全程零位移(无操作轨持续被手势重置)`、
|
||
`E0-1: 无进展 30s 级 → earcon(复用合成回执音,乱按饿不死)`、`E0-1: 无进展 60s 级 → 短句 stair_hint_a`、
|
||
`E0-1: 无进展 90s 级 → 导航接管(播最短路首步方向)`、`E0-1: 接管后进入导航模式`、
|
||
`E0-1: 接管首步方向必可通行(楼梯指向,不指墙)`、`E0-1: 乱按 96s 语音总量有上界(≤4)`、
|
||
`E0-1: 有真实进展(格位置持续推进)零卡关语音零 earcon(不误伤正常玩家)`。
|
||
|
||
#### E0-2 跳教学路径密度回归(P0,P3 抓获)
|
||
|
||
- **根因**:压力局 t=52.5 起困在教学 D9(tap=脉冲,零位移零步骤推进)39 分钟——D9 看门狗 30s 升级句
|
||
同样被「每次手势重挂」饿死;skip 后再 79 分钟零遭遇零指向。R1 乱按局 75s 即滚出教学的逃生口在 R2
|
||
形同虚设(2403s)。
|
||
- **修**:① 教学态共享无进展轨(`_stuckArm` 5s 轮询扩到 DRILL;`_drillEnter` 步骤推进/格位置/战斗回合
|
||
均重置):30s earcon → 60s 短句(仅 D9 给 `stair_hint_a`——教学句不重播 §10.2,非寻路步骤无方向话
|
||
可给、短句级跳过登记)→ **90s 教学接管 = `_drillSkip()` 自动滚出教学**(播 `tut_skip`、drillDone 入账、
|
||
落回探索态由导航链接手),恢复 R1「乱按 ~75s 出教学」口径并落在 P3 要求的 90-120s 内;做对了
|
||
(步骤推进/走动)永不误触发。② 密度保底对跳教学局本就全路径生效(`_fillMainPathDensity` 在
|
||
`_tryGenFloor` 内无条件执行),本轮补上「跳教学种子局实测 `_floor`」的直证断言;skip 后进探索即享
|
||
E0-1 导航接管。
|
||
- **断言(场景 13C/13D)**:`E0-2: 跳教学种子局主路径密度不变式(最长真空 ≤12 格)`、
|
||
`E0-2: 跳教学后 Ns 内必有指向语音(≤120s;R2 压力局 skip 后 79 分钟零指向)`、
|
||
`E0-2: 教学乱按零进展 ≤95s 内自动滚出教学(R2 压力局 2403s)`、`E0-2: 教学乱按 30s 级 earcon 先到`、
|
||
`E0-2: D9 乱按 60s 级给方向话 stair_hint_a(看门狗长嘴)`、`E0-2: 接管播 tut_skip`。
|
||
|
||
#### E1-1 到达即报(P1,live)
|
||
|
||
- **根因**:站楼梯格 tap 被下潜模态截走(补测局 t=1288.7 实演),「铁器=楼梯」贴身回声只能靠相邻格
|
||
脉冲兑现,教学 D9 承诺在输入层不可达(本局两次到站都靠 tut_stairs_on 而非铁器音找到楼梯)。
|
||
- **修(产品裁定「到达即报」)**:`_onStairCell` 踏格瞬间自动播贴身铁器回声(`echo_1` 即刻 +
|
||
`mat_stair` +120ms,编码同脉冲贴身档)+ 既有提示句(每层首达 `tut_stairs_on` / 窟主未清
|
||
`evt_stairs_lock`)。**不占输入通道**:单击仍是下潜确认,脉冲/其他手势语义零变化。
|
||
- **断言**:场景 2 `R3 E1-1 到达即报:踩上楼梯格瞬间自动播贴身铁器回声(echo_1+mat_stair,全程零玩家脉冲)`
|
||
+ `R3 E1-1: 到达即报不占输入通道——tap 仍是下潜确认(照常下潜)`;场景 13E
|
||
`E1-1: 踏上(被锁的)楼梯格自动播贴身铁器回声` + `E1-1: 到达置位 _onStairs` +
|
||
`E1-1: 窟主未清 → 到达即报铁器 + evt_stairs_lock`。
|
||
|
||
#### E1-2 band1 档位账目(P1,P2/P4 共识)
|
||
|
||
- **根因**:`_goldBand` 把 0<金<50 与 =0 同播「口袋空空」/「这趟没带回什么」——教学拾取(蝠 3 金、
|
||
堆 10-30 金)后即谎报(P2:49 秒自打脸,其后 6600s 里 27 次「口袋空空」)。
|
||
- **修**:新录 band1 真话句 `gold_some`「带回来一点,不多。」(8 字,多音字自查无风险);**=0 保留
|
||
`st_gold_1`/`meta_gold_1` 空手措辞(band0 口径不变)**。新增 `_stGoldVoice()`/`_metaGoldVoice()`
|
||
统一出口:上滑状态链 / SETTLE / TITLE 三处共用同一条(管线单条、账本仍同一 `_goldBand`)。
|
||
- **管线**:16-dungeon-voice-ids.md 与 gen_voice_dual.py 两表一致 → `source env.d/tts.sh` +
|
||
`flock .gpu.lock .venv-tts/bin/python tools/gen_voice_dual.py --game dungeon --gender both
|
||
--only gold_some,tut_flee_hold,st_best_2` 单批重录(gold_some m 1.84s / f 2.08s)→ `--convert`
|
||
(dungeon 254 文件 ogg+m4a)→ `tools/extract_voice_text.py` 刷新 voice_text.json(8 游戏 706 句)→
|
||
`tools/check_all_assets.py` 全过(dungeon 307+307 双格式、双性别各 127、零解码失败)。
|
||
- **断言**:场景 4 `上滑链前 4 段有序(…gold_some band1 真话…)`(教学蝠 3 金实证)、场景 13E
|
||
`E1-2: 上滑金币档 0<金<50 播 gold_some(不再谎报「口袋空空」)`、
|
||
`E1-2: SETTLE band1(0<金<50)同步播 gold_some`、`E1-2: 金=0 保留 meta_gold_1 空手措辞`;
|
||
场景 6a 死亡链序第 5 段与金币档断言改 gold_some。
|
||
|
||
#### 打磨(P2/P3 顺手带)
|
||
|
||
- **逃跑长按 ≥900ms**:`_fleeArm()`/`_battleFleeCancel()`——战斗态 longpress(引擎 450ms 判定)先武装,
|
||
按满 `FLEE_HOLD_MS=900` 才真正 `_flee()`;未满松手 = 取消 + error 震 + 每场 1 句区分提示
|
||
`tut_flee_hold`「想逃跑,按住别松手。」(新录)。探索态 450ms 走步不变;<450ms tap 优先判定不变
|
||
(引擎层)。断言:场景 5 `战斗长按 ~650ms(<900ms)松手 = 不逃(R3 打磨)`、
|
||
`短长按取消伴随 error 震`、`取消播区分提示 tut_flee_hold(每场 1 次)`;原「长按逃跑必成挨一下」
|
||
「Boss 拒绝」断言原样通过(按住不放的路径不变,harness 短步进 longpress 配对不受影响)。
|
||
- **st_best_2 措辞**:「你到过中层。」→「你最远到过中层。」(live P3:bestFloor 是前世纪录,防误读为
|
||
「本局到过」;同 id 重录)。st_floor 序列经复核无缺陷(settle 链报深度正是 P3 表扬项),不动。
|
||
- **台词账目**:125 → **127 句**(+`gold_some` +`tut_flee_hold`;`st_best_2` 重录不加句);
|
||
playtest 资产校验同步改 127。
|
||
|
||
#### R3 老断言口径变更登记
|
||
|
||
- 场景 4 上滑链第 3 段:`st_gold_1` → `gold_some`(教学蝠 3 金落 band1 真话档)。
|
||
- 场景 6a 死亡链序与金币档断言:`meta_gold_1` → `gold_some`(settle 金 40 = band1 且 >0)。
|
||
- 资产校验:voice 台词总数 125 → 127。
|
||
- 其余全部原样通过:卡关三级 60/120/180(场景 9 全链)、导航逐步校正/节流/nav_done、进展重置分级、
|
||
队列礼仪、探索 60s 语音上界、呼吸层 20s 节奏、guide=off 30s 静默(earcon 属 sfx 内容层不违约)、
|
||
E3/E4、屏蔽兜底、D0-D9 教学全链。
|
||
|
||
#### 遗留(本轮未动,沿用 R3 synthesis 悬置项)
|
||
|
||
- 8s nudge「earcon+首句短词」半可验改造(synthesis §2 P2/P3,未列入本轮修复清单)与 amb_cave 听感
|
||
人测(roadmap §4)。
|
||
- 教学跳过标志跨会话不一致、探针通道隔离:harness/仪器侧。
|
||
- P3 建议「上滑加第 6 句最近铁器方位」「撞墙归因句」:未列入本轮清单,留终审裁定。
|
||
|
||
### H1 热修(dev,2026-09-02):死亡结算链被「在途手势」截杀——结算入口手势死窗
|
||
|
||
- **现象(R3 live A 局 2787.057,唯一阻塞交付的 P1)**:Boss 重击致死后结算链只播了
|
||
`die_intro` 前 0.18s(vstart 2786.88 → vend kill 2787.057),`die_cause_2`/`die_adv_*`/
|
||
`st_floor_3`/金币档/`settle_stronger`/`settle_again` 后 6 句零 fetch 零 vstart,结算屏
|
||
静默 76s,直到玩家下一次单击——而那次单击开出的是 `sys_new_run`(新周目),不是结算重听。
|
||
- **根因(events.jsonl 逐事件推演,非 live 报告猜想的语音链调度竞态)**:
|
||
① 引擎 gesture.js 非即时模式下单击要等双击窗(`doubleTapWindow=300ms`)关闭才回调
|
||
`onGesture('tap')`(gesture.js `pendingTapTimer`);② 致命前摇 resolve(t=2786.78
|
||
`_enemyResolve` → `_die`)落地时,玩家上一记 tap 还压在双击窗里;③ 2787.057 tap 回调时
|
||
`onGesture` 按「当时」状态路由——state 已是 SETTLE → `_onSettleGesture('tap')` →
|
||
**`_enterTitle()`**:`_session++` 作废结算会话 + `audio.stopVoice()` 无回调掐死
|
||
`die_intro`(引擎约定「打断即作废整链」,onComplete 不触发 → 后 6 句永不接续),
|
||
且 run 已清 → TITLE 空链全静默。证据闭环:vend 时刻恰有 gesture.js 的 confirm 震
|
||
([20],2787.057),三次 tap 的「记录→回调」间隔均为 ~360ms(touchend+300ms),
|
||
「单击开出新周目」正是 TITLE 态 tap 语义。虚拟时钟 playtest 未抓到:旧 mock 直通
|
||
`onGesture`,从不在死亡后补派在途手势。
|
||
- **修法(web/dungeon/game.js,最小面)**:新增 `SETTLE_GRACE_MS=800` 手势死窗——
|
||
`_die()`/`_settleWin()` 进结算时盖 `this._settleGraceUntil` 戳;`onGesture` 在 SETTLE 态、
|
||
非菜单、戳内的一律吞掉(ghost 手势不算玩家输入,不碰 `_stuck.pending`)。窗口上界取
|
||
手势最迟回调时刻 touchstart+450ms(长按判定)+300ms(双击窗)≈750ms,800ms 全盖;
|
||
死窗后结算手势照常(tap 回标题 / 双击重听链 / 下滑菜单),设计出口不受影响。
|
||
战斗收尾链不需 abort:死亡时战斗语音链早已自然播完(swish onComplete 才会进前摇),
|
||
死亡链独占通道的前提本就成立——本次截杀者是 stopVoice 而非排队顶号,与 R2
|
||
「`_finishVoice` token 校验/加载竞态」语义无涉,引擎零改动。
|
||
- **断言(playtest 场景 6d,7 条)**:mock 构造 live 同款序列(挥空 → btl_swish 收尾链 →
|
||
敌前摇 → 致命 resolve),死亡后同拍与 +300ms 各补派一枚在途 tap:①两枚 tap 均被吞
|
||
(仍在 SETTLE);②结算链 7 句完整有序播出(结算屏有内容);③`die_intro` 恰播一次
|
||
(未被 `_enterTitle`/stopVoice 截杀);④死窗后 tap 正常 → TITLE(出口不受死窗影响)。
|
||
**旧代码验证**:stash 修复后同场景 5/7 断言即挂(tap 跳 TITLE、链断),与 live 签名一致。
|
||
- **回归**:dungeon 1642→**1649 全绿**;escape 140 / radio / rhythm 141 / rpg 188 /
|
||
simon 132 / space 155 / storyline 八套件全 PASS。
|
||
- **遗留**:真机 live 定向复验待下一轮(「挥空瞬间被打死 + 死亡瞬间按着屏幕」场景);
|
||
`_settleWin`(终局通关结算)同栅已顺手盖住,但通关路径无 live 负样本。
|