Files
SoundGame/docs/design/16-dungeon.md
T
xsl f7509ad5d8 feat(new8/R3): 双游戏收口修复轮 + 验证档案(radio G1-G6 / dungeon E0-1..E1-2)
- radio:帮助链去重(同等待点二次=beat震+earcon,全量口令38→20)/rep_low警告震/空点轻震/状态链重排(状态在前)/「首播必完整、复播可即掐」显式规则登记/G3核查为设计使然转断言;221→246断言
- dungeon:看门狗双轨制(无操作轨+无进展轨,30/60/90s三级升级到导航接管)/跳教学90s自动滚出(2403s→95s内)/踩楼梯到达即报贴身铁器/band1新词「带回来一点,不多」全管线重录/逃跑长按900ms+提示句;1610→1642断言
- R3 验证(进行中已入库部分):全量回归 9/9 全绿;R3 转写×4;P2/P3/P4 终审×6份(radio 满分档首评/三审,dungeon 4/4.5/3-4 全票愿意玩,无P0);两处独立定址同一残余=漫游型迷路刷不响无进展轨(R4 第一条)
- 8套件+guide全绿;Mureka 累计 $0.87/$2.00
2026-09-02 23:08:35 +08:00

59 KiB
Raw Blame History

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(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。数字全分档整句(层 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. 存档设计

{
  "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__,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 句最近铁器方位」「撞墙归因句」:未列入本轮清单,留终审裁定。