From 45124a1b9bf14932d9cc6648bbc669d71d600d93 Mon Sep 17 00:00:00 2001 From: xiangsilian Date: Thu, 3 Sep 2026 03:01:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(new8):=20=E6=89=B9=E6=AC=A11=E6=94=B6?= =?UTF-8?q?=E5=8F=A3=E2=80=94=E2=80=94radio/dungeon=20=E5=8F=8C=E5=8F=8C?= =?UTF-8?q?=E6=A0=87=E8=AE=B0=E5=8F=AF=E4=BA=A4=E4=BB=98=EF=BC=88R3=20synt?= =?UTF-8?q?hesis=C3=972=20+=20H1=20=E7=83=AD=E4=BF=AE=20+=20=E5=AE=9A?= =?UTF-8?q?=E5=90=91live=E5=A4=8D=E9=AA=8CPASS=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 套件全绿 --- docs/design/16-dungeon.md | 35 ++++++ research/new8/00-program.md | 4 +- .../new8/dungeon/round3/h1-live-verify.md | 95 +++++++++++++++++ research/new8/dungeon/round3/live-player.md | 72 +++++++++++++ research/new8/dungeon/round3/p1-blind.md | 24 +++++ research/new8/dungeon/round3/synthesis.md | 26 +++++ research/new8/radio/round3/live-player.md | 100 ++++++++++++++++++ research/new8/radio/round3/p1-blind.md | 22 ++++ research/new8/radio/round3/synthesis.md | 19 ++++ web/dungeon/game.js | 18 +++- web/dungeon/test/playtest.mjs | 52 +++++++++ 11 files changed, 464 insertions(+), 3 deletions(-) create mode 100644 research/new8/dungeon/round3/h1-live-verify.md create mode 100644 research/new8/dungeon/round3/live-player.md create mode 100644 research/new8/dungeon/round3/p1-blind.md create mode 100644 research/new8/dungeon/round3/synthesis.md create mode 100644 research/new8/radio/round3/live-player.md create mode 100644 research/new8/radio/round3/p1-blind.md create mode 100644 research/new8/radio/round3/synthesis.md diff --git a/docs/design/16-dungeon.md b/docs/design/16-dungeon.md index 3d00f0bf..387409e3 100644 --- a/docs/design/16-dungeon.md +++ b/docs/design/16-dungeon.md @@ -677,3 +677,38 @@ choice 签名本作不使用(无抉择态;菜单是 menu)。菜单回绕 人测(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 负样本。 diff --git a/research/new8/00-program.md b/research/new8/00-program.md index b0771705..617de060 100644 --- a/research/new8/00-program.md +++ b/research/new8/00-program.md @@ -73,8 +73,8 @@ | 批次 | 游戏 | 设计 | 资产 | 开发 | R1..R6 | 验收 | |---|---|---|---|---|---|---| -| 1 | radio | ☑ | ☑ | ☑ | R1 ☑(5/5 愿意玩,P0=0)→ R2 修复中 | ☐ | -| 1 | dungeon | ☑ | ☑ | ☑ | R1 ☑(0/4 拒绝,语音风暴 P0)→ R2 结构修复中 | ☐ | +| 1 | radio | ☑ | ☑ | ☑ | **R1-R3 ☑(+R2 五修+R3 六打磨;4/4 人设满分档愿意玩,P0=0)→ 可交付 ✅**(R4-R6 经用户裁定暂停,视上手反馈续) | ☑ | +| 1 | dungeon | ☑ | ☑ | ☑ | **R1-R3 ☑+H1 热修(0/4→4/4 翻案;live 六连验+H1 定向复验 PASS)→ 可交付 ✅**(v2 首条=漫游迷路抢救盲区) | ☑ | | 2 | abyss | ☐ | ☐ | ☐ | ☐ | ☐ | | 2 | detective | ☐ | ☐ | ☐ | ☐ | ☐ | | 3 | cipher | ☐ | ☐ | ☐ | ☐ | ☐ | diff --git a/research/new8/dungeon/round3/h1-live-verify.md b/research/new8/dungeon/round3/h1-live-verify.md new file mode 100644 index 00000000..8c60f0f3 --- /dev/null +++ b/research/new8/dungeon/round3/h1-live-verify.md @@ -0,0 +1,95 @@ +# H1 热修定向 live 复验 —《回声地窟》(dungeon) 死亡结算链 + +> H1(死亡结算链同 tick 竞态根修 + SETTLE 800ms 手势死窗)交付后的定向 live 复验。红线遵守:未改任何游戏代码/资产/harness 代码。验证目标唯一:**死亡后结算链(die_intro 起约 7 句)完整有序播出,不再被幽灵手势截断;结算屏不再静默 76 秒;死亡瞬间的在途手势不再跳 TITLE。** + +## 结论:**PASS(两局双确认)** + +R3 P1 的两个触发面都复现了,旧 bug 都没有复发: + +1. **主验证(局 1)**:挥空链在途时被窟主石甲蟹打死 → 结算链 7 句逐句完整播出(事件层 vend 全部 = 自然播完时刻,ASR 6 句 OK + 1 句已知短句误识),无静默。 +2. **幽灵手势直接命中(局 2)**:致命前摇 1.2s 窗口内发出的 tap,其回调在死亡后 ~0.49s 才派发(正好落进 H1 的 800ms 结算死窗)→ 被吞掉,**结算链一句没少、没有被带去 TITLE**——这正是 R3 A 局 die_intro 播 0.18s 即死的同款输入,热修后不再复发。 + +--- + +## 基本信息与产物 + +- 会话(`web/harness/live/out/dungeon-20260902163908/`,四件套齐全:events.jsonl + verdicts.json + transcript.md + mix.webm;413 段语音 / 1271 手势 / audioDur 8285s,两局同一会话): + - settings:male / normal / normal / full;第一动作 tap 解锁。 + - **局 1**:跳教学 → L1(踩楼梯下潜,到达即报正常)→ L2(清 3 鼠,导航接管带路)→ L3 → 清 3 蘑菇 → **被窟主石甲蟹(站在楼梯格)打死**。死亡时刻 ta=2395.05(会话内秒,下同)。 + - **局 2**(交叉确认):结算 → TITLE → 新周目 → 重爬 L1/L2/L3 → 再战窟主 → **挥空链在途 + 致命前摇中发 tap(幽灵手势场景)** 被打死。死亡时刻 ta=8203.965。 +- 报错:asset404=0、console.error=0、pageerror/unhandledrejection=0;**vend reason=preempt = 0**(413 段全部 reason=kill,引擎对"播完"的通用标注,R3 P3-1 已知仪器口径)。 + +## 主验证一:结算链句子清单(两局对照,t=会话内秒) + +**局 1**(死亡 2395.05:被石甲蟹绕到背后一记重击打死 → 归因真话 die_cause_3 + die_adv_3): + +| # | 句子 | vstart → vend | 时长 | ASR | +|---|---|---|---|---| +| 1 | die_intro 你倒下了。 | 2395.16 → 2396.62 | 1.44s(**满**) | OK sim 0.86 | +| 2 | die_cause_3 你背后的偷袭,要了命。 | 2396.72 → 2400.19 | 3.44s | **OK sim 1.0** | +| 3 | die_adv_3 身后有动静,先转身。 | 2400.29 → 2402.80 | 2.48s | OK sim 0.80 | +| 4 | st_floor_3 这是第三层。 | 2402.89 → 2404.36 | 1.44s | OK sim 0.89 | +| 5 | meta_gold_2 这趟带回一些金币。 | 2404.46 → 2406.89 | 2.40s | OK sim 0.80 | +| 6 | settle_stronger 下个周目,你会更强。 | 2406.99 → 2410.45 | 3.44s | OK sim 0.88 | +| 7 | settle_again 单击,再探一次。 | 2410.55 → 2412.42 | 1.84s | MISMATCH sim 0.2* | + +*settle_again 为 harness 已知限制(≤6 字短句在 menu bgm 下的同音误识);事件层 vend 2412.42 = vstart+1.84s 自然播完,trunc=False。句间隔全部 ~0.10s,链条 2395.16→2412.42 连续 17.3s,无任何静默窗。**R3 现场(die_intro 只播 0.18s + 后 6 句零 fetch + 结算屏静默 76s)未复现。** + +**局 2**(死亡 8203.965:挥空链在途 + 被石甲蟹正面重击未格挡打死 → die_cause_2 + die_adv_1,归因真话): + +| # | 句子 | vstart → vend | 时长 | ASR | +|---|---|---|---|---| +| 1 | die_intro | 8204.06 → 8205.53 | 1.44s(满) | OK sim 0.86 | +| 2 | die_cause_2 你没挡住那记重击。 | 8205.63 → 8207.81 | 2.16s | OK sim 0.80 | +| 3 | die_adv_1 下次,前摇声响时就双击。 | 8207.92 → 8211.38 | 3.44s | OK sim 0.70 | +| 4 | st_floor_3 | 8211.48 → 8212.94 | 1.44s | OK sim 0.89 | +| 5 | meta_gold_2 | 8213.04 → 8215.46 | 2.40s | OK sim 0.80 | +| 6 | settle_stronger | 8215.56 → 8219.02 | 3.44s | OK sim 0.88 | +| 7 | settle_again | 8219.12 → 8220.98 | 1.84s | MISMATCH sim 0.2* | + +链条 8204.06→8220.98 连续 16.9s,句间隔 ~0.10s。两局金币位同报 meta_gold_2(meta.gold 同账本,与上滑/TITLE 同函数,E1-2 口径未回归)。 + +## 主验证二:幽灵手势场景(H1 死窗的直击复验,局 2) + +局 2 特意按 R3 触发条件编排(与 R3 A 局同款输入): + +``` +8199.398 GEST tap ← 挥空 tap(touchstart) +8199.861 btl_swish vstart ← 挥空链播出(2.9s,R3 同款"挥空链在途") +8202.764 btl_swish vend +8202.764 tel_heavy_r ← 致命前摇(1.2s 格挡窗) +8203.965 hurt ×2 + bgm/menu ← 致命一击落地,hp≤0,进 SETTLE +8204.061 die_intro vstart ← 结算链开播 +8204.115 GEST tap ← ★幽灵手势 touchstart(在死亡瞬间发出) +8204.478 vibrate [20] ← 手势回调派发:死亡后 ~0.49s,落在 800ms 死窗内 → 被吞 +8205.526 die_intro vend ← 1.44s 自然播满(R3 此处是 0.18s 即死) +8205.630 die_cause_2 vstart ← 后续 6 句照常逐句播出 + ... +8220.983 settle_again vend ← 链条完整收尾 +``` + +幽灵手势的回调派发(8204.478,死亡后 ~0.49s)落在 `SETTLE_GRACE_MS=800ms` 死窗内被吞掉:die_intro 照常播满、结算链没有作废、**没有跳 TITLE、没有开出新周目**。死窗后结算手势恢复正常(见出口验证)。这正是 H1 注释里描述的"非即时模式单击要等双击窗(300ms)关闭才回调"的竞态现场,热修后行为正确。 + +## 出口验证(两局) + +- **局 1**:结算链播完(2412.42)后单击 → 2584.23 tap → 仅手势震动、无任何语音、无 bgm 切换(settle→TITLE 本就同曲)、无 sys_new_run = 静默进 TITLE ✓;随后再 tap → 2599.60 `sys_new_run` 正常开出新周目(drillDone 生效不再进教学,与 R2/R3 口径一致)✓。 +- **局 2**:结算链播完(8220.98)后单击 → 8368.12 tap → 同样静默进 TITLE,无任何异常播报 ✓。 +- 死亡 800ms 死窗之后 tap 恢复正常路由("离开结算"),死窗只吞在途手势,不影响玩家后续输入 ✓。 + +## 顺带回归观察(非本轮范围,顺路确认) + +- **零抢占零死锁**:413 段 vend reason 全 =kill,preempt=0(R1 35,611 → R2 0 → R3 0 → 本次 0,四轮干净);全事件空窗 >25s 的 12 处逐一归因,全部是玩家侧(本 agent)分析停顿/有意静置(含局 2 L3 找路的长静置),期间看门狗(gd_turn / 导航接管 / 环境垫)按设计照常说话,无一处游戏卡死。 +- **30/60/90 无进展阶梯**又双_samples:局 1 L2(1487.8 earcon → 1517.9 stair_hint_a → 1547.9 nav_s)与局 2 L3(5288.7 → 5318.8 stair_hint_b → 5348.8 nav_s)两级实测,严丝合缝;导航接管每步重报正常。 +- **到达即报(E1-1)**:局 1 L1/L2 楼梯(ta 904.4 / 1726.1)与局 2 L2 楼梯(1726 对应局 2 的 3723.7)踩格自动 echo_1+mat_stair+「楼梯就在脚下」均正常。 +- **归因真话律**:两局死因句与实际死因一致(局 1 背刺 → die_cause_3/adv_3;局 2 Boss 未格挡重击 → die_cause_2/adv_1),R2 D1-5/E1-2 的死亡链金币句(meta_gold_2)两局都拿到了现场收据(R3 时因 P1 丢失)。 +- 唯一 trunc 标记:`btl_no_turn`(局 1 L3,1841.1)——玩家在纠错句播出中立刻做了下一个手势导致的让位,教学向短句、非结算链,与 H1 无关。 + +## 仪器与过程备注(透明度) + +- 局 2 找窟主时,因逐格推算在 9×9 层反复漂移,用**第二 CDP 客户端只读**了一次 localStorage 存档(seed/pos/face/hp——与 R3"第二 CDP 客户端"同类的仪器手段,未写入任何状态),并把 game.js 的楼层生成算法原样转录到本地脚本算出楼梯格(窟主就站在楼梯格上),再通过正常手势流走到位。全程未改代码/资产,游戏侧只收到标准手势事件。 +- harness 长按固定 ~600ms、单击回调带 300ms 双击窗延迟——正是 R3/H1 的竞态土壤,本次在局 2 中被直接用作测试注入。 +- speed 枚举仍只接受 1/0.3/0(R3 P3-2 未修,非阻塞)。 + +## 一句话裁决 + +**H1 热修定向复验 PASS:两局死亡(含"挥空链在途 + 致命前摇中发 tap"的 R3 同款幽灵手势现场)结算链 7 句全部完整有序播出、句间隔 ~0.1s 无静默、在途 tap 落进 800ms 死窗被吞、出口单击干净进 TITLE/再来一局——R3 唯一阻塞项销案,dungeon 可交付。** diff --git a/research/new8/dungeon/round3/live-player.md b/research/new8/dungeon/round3/live-player.md new file mode 100644 index 00000000..16febca4 --- /dev/null +++ b/research/new8/dungeon/round3/live-player.md @@ -0,0 +1,72 @@ +# 《回声地窟》R3 live 确认局报告 — 老陈(认真局)+ 急躁压力局(收口验证) + +> R3 轮次协议第 3 步:dev 交付 E0-1/E0-2/E1-1/E1-2 + 打磨项后的 1 局 live 确认。红线遵守:未改任何游戏代码/资产/harness 代码;乱按局抢救触发时刻均给 t=(录音时基秒)。 + +## 基本信息 + +- 身份:老陈,R2 同款细心中年玩家,认真局走完「新手弧线」;另一局换急性子表弟小飞代打乱按压力局。 +- 设置与会话(`web/harness/live/out/` 落盘,events.jsonl + verdicts.json + mix.webm + transcript.md 四件套齐全): + - **局 A0**(意外收获局)`dungeon-20260902150137`:male/normal/normal/full。本想在 D1 教学步停顿读一下说明书,静置 92 秒——被 E0-2 接管当场滚出教学。这个「事故」成了 E0-2 的第一份 live 证据,遂保留。 + - **局 A**(认真主局)`dungeon-20260902150633`:male/normal/normal/full。因 A0 的自动滚出把 `drillDone` 写进了存档,本局直接跳教开局(恰好覆盖「跳教学路径」);跳教 → L1 → L2 → L3 → Boss 战死亡 → 结算 → 新周目。206 手势,266 段语音事件。 + - **局 B**(乱按压力局)`dungeon-20260902155741`:female/full。**注**:settings 传 `"speed":"1.7"` 被引擎规整为 `1`(本 harness 的 speed 枚举是 1.0/0.3/0,R2 的 1.7x 语速通道本次不可复现——仪器侧,见 P3-2),故 B 局是「同速乱按局」而非 1.7x 局;E0 断言全部依赖事件层时基,不受影响。 +- 手势统计:A0 = 4;A = 206(tap/longpress/swipe×4/doubletap 全用,另用第二 CDP 客户端补了 2 次 1300ms 裸长触,仅仪器手段、未碰代码);B = 56(乱按:longpress/双滑/双击/上滑混撒,刻意不含下滑=跳教学,把「滚出教学」留给看门狗)。 +- 报错:三会话 `asset404=0、pageerror/unhandledrejection=0、console.error=0`;**`vend reason=preempt` = 0**(R1:71 分钟 35,611 次;R2:0;R3:0,连续两轮干净)。 + +--- + +## R3 修复逐条现场验证 + +| # | R3 修复 | 结论 | 现场证据(t=会话内秒) | +|---|---|---|---| +| **E0-1** | 看门狗双轨(无进展轨 30/60/90s:earcon→短句→导航接管;只由状态推进重置) | **修好(两局双确认,阶梯逐级实锤)** | **A 局**:最后进展=1010.5(走格)→ earcon(confirm 震+tick)**1034.8**(+24s)→ `stair_hint_a` **1064.9**(+54s)→ **`nav_s` 1094.9**(+84s,=90s 档 + 5s 轮询粒度);期间脉冲/手势不断但零进展,无操作轨被喂饱照样升级——「乱按不再饿死」直证。**B 局**复现:杀怪进展 ≈486 → earcon **517.6** → `stair_hint_a` **547.7** → **`nav_e` 577.7**,30/60/90 三档严丝合缝;导航模式下每走一步重报(**2494.1** step→`nav_e` 随步重指)+60s 周期重报(1094.9→1154.9→…→2438.5,严格 60s)与 R2 L3 带路口径一致。「有真实进展不误触发」:A 局 1094.9 接管后我一路走格/打怪推进,全程无 earcon/短句误发;B 局 697-718 走格+撞墙期间同样零误报,只有随步 nav 重指。** +| **E0-2** | 跳教学路径:教学态同享无进展轨,90s 接管=自动滚出教学;跳教后 120s 内必有遭遇或指向语音 | **修好(两条退出路径都验到,且跳教后 6 秒即有遭遇)** | **路径①(A0 局,静置型)**:D1 教学步零输入静置,最后进展 80.7 → **`tut_skip` 172.4(+91.7s)** 自动滚出教学进探索——「R2 的 39 分钟」vs R3 的 92 秒。**路径②(B 局,乱按型)**:小飞从 TITLE 起乱撒非下滑手势,教学被「两次做错自动演示通过」快速推进(`tut_front_rule` 59.7 → `tut_side_rule` 65.9 → `tut_walk` 71.7 → `tut_turn` 80.9 → `tut_replay` 102.8 → `tut_status` 111.3 → `tut_mob` 117.8),卡在 D9 寻路步后:`stair_hint_a` **171.6** 与 **194.4** 两次短句 → **`tut_skip` 224.4(最后进展 129.6 走格 + 94.8s)** 滚出教学。**跳教后密度**:`tut_skip` 228.7 播完 → **230.3 蝙蝠扑击接战(+5.9s)**,远优于「120s 内必有」断言(A 局跳教开局 62.7s 也已有 mob_step 逼近、71.4s 接战)。** +| **E1-1** | 踩上楼梯格「到达即报」:自动贴身铁器回声 + 下行提示,不靠玩家脉冲;tap 下潜照常 | **修好(两个干净样本 + 脉冲射线报铁器也活着)** | **样本1(A 局 L1,误打误撞全程录到)**:519.27 longpress 踏上楼梯格 → **同一拍自动 `echo_1`(519.72)+ `mat_stair`(519.84)+ `tut_stairs_on`(519.82,「楼梯就在脚下。单击,下潜。」)**,零玩家脉冲输入;523.78 tap → `stairs_down`+`evt_descend`(3.12s)+`st_floor_2`,下潜演出照常。**样本2(A 局 L2,刻意复现)**:1371.66 脉冲先听到 `echo_1`+`mat_stair`(正前射线铁器回声,R2 D0-2 的「相邻格兑现」同时活着);1377.47 踏格 → **自动 `echo_1`+`tut_stairs_on`+`mat_stair` 三连**;1382.04 tap → 下潜 `st_floor_3`。教学 D9「听到铁器回声再走过去」的承诺第一次在输入层无死锁地兑现(R2 P1-1 当场销案)。** +| **E1-2** | band1(0<金<50)新句 `gold_some`「带回来一点,不多」,上滑/结算/TITLE 同账本同分档 | **修好(含 0 金对照,全表面零谎报)** | **A 局上滑**:杀怪入账后 meta.gold=6/11/21 三次上滑状态链金币位全部播 **`gold_some`**(250.9,ASR「带回来一点不」sim 0.92;642.5 附近 B 局亦同);金到 ≥50 后同一条链换 **`st_gold_2`「有些积蓄」**(2545.9)——同一分档函数随账自动升档。**0 金对照(B 局开局)**:129.6/144.5 金币位播 `st_gold_1`「口袋空空」——0 金说空手、少量说「带回来一点」、攒多说「有些积蓄」,三档句句真话,R2/P2「捡到金币→口袋空空」的谎报结构已消除。**结算/TITLE 表面**:A 局死亡结算链因新 P1(见下)在 `die_intro` 即断,`_metaGoldVoice` 的死亡链样本未能采到;TITLE 面亦未访问——但状态链已实证同一 `_goldBand/_stGoldVoice` 出口(与 SETTLE/TITLE 共函数),代码面单出口 + live 面三档真话,判修好,仅欠一张死亡链的现场收据。** +| **打磨** | 战斗逃跑长按需按满 900ms;短松(<900ms)=取消+区分提示「想逃跑,按住别松手」 | **修好(两半都实测)** | **短松取消**(harness 长按固定 600ms<900ms):A 局 570.4 → error 震 `[60,40,60]` + **`tut_flee_hold`**(「想逃跑,按住别松手」,ASR「抢逃跑,按住别怂」sim 0.67)✓;且每场战斗只播一次(同场第二次 528.2/1528.8 只震不播,1519.7 新战斗重播——按设计);B 局 2011.7 第三次独立战斗同款复现。**按满真逃**:第二 CDP 客户端裸触 1300ms(仪器手段)→ A 局 682.5 **`btl_flee`**「必成挨一下」(hurt 音随行)+ BGM 战→explore 立即回切 ✓。**Boss 禁逃**:L3 石甲蟹战裸触 1300ms → 2715.3 **`btl_flee_boss`「退路塌了,跑不掉!」sim=1.0** + 纠错震 ✓。** +| **整体** | 教学→2-3 层→战斗→死亡→结算→新周目,零死锁零抢占 | **基本通过(一处 P1 见下)** | 三会话合计教学 D0-D9 全步骤台词齐(A0+ B 覆盖);A 局 L1→L2→L3 三层全走(两跳楼梯全是「到达即报」接「单击下潜」);战斗 9 场(蝠×4 鼠×3 蘑菇×3 石甲蟹×1,含格挡成功 `btl_parried`、击杀升档 `btl_lvup`×2、自动侧移报位 `mob_step_heavy_r`、挥空真话 `btl_swish`「挥空了,他不在你面前」);死亡一场(Boss 重击)+ 结算 tap 直接开出**新周目**(`sys_new_run` 2864.1,且因 drillDone 不再进教学,与 R2 口径一致)。**零抢占**:三会话 vend reason 全部 =kill,preempt=0;**零死锁**:三会话最大事件空窗逐一归因——A 局 92.7s/91.6s 均为本玩家读代码静置、76.0s 即下述 P1 结算中断;B 局 207.5s 为下述 P2 战斗静置——无一处是游戏卡死。** + +## 局 A 体验叙述(验收视角,第一人称) + +跳教开局后上滑第一件事就是听金币位——上一轮还嘴硬「口袋空空」,这局捡了 3 金就改口「带回来一点,不多」,账本子终于说人话。L1 的蝙蝠扑上来,我手一慢吃了下 ACK,反手两刀放倒;第二只扑进来把我上滑状态链拦腰打断——遭遇优先级最高,这个「没礼貌」是对的。真正的高潮在 519 秒:我随手一个长按,脚下突然自己响起贴身铁器回声+「楼梯就在脚下,单击,下潜」——**上一轮我用尽办法都听不到的这句教学承诺,这一轮是踏上去自动送来的**。L2 重复了一遍(这次我先脉冲听到了正前方的铁器尾音,再踏格、再听一遍到达即报),L3 卡了会儿路,干脆静坐——24 秒后 earcon、54 秒短句、84 秒导航接管准时点名「往东」,导航模式每走一步重报下一步,把我一路带到石甲蟹脸上。Boss 战里短按长按被「想逃跑,按住别松手」劝住,真按满一拍被「退路塌了,跑不掉!」拒绝,两刀换位脚步报位、挥空播「他不在你面前」,最后被重击放倒——**然后结算链只说了半句「你倒下了」就哑了**,结算屏静坐 76 秒直到我自己单击开出新周目。这是本局唯一一次游戏「说到一半闭嘴」。 + +## 局 B 体验叙述(小飞乱按:非下滑混撒) + +小飞不识字也不听课:教学 D0-D7 被他的胡乱输入用「两次做错自动演示」一路买通(每步 ~10 秒),卡在 D9 找楼梯时彻底躺平——17 秒后游戏开始喊「找找铁器的回声」,两遍之后一脚把他踹出教学(tut_skip),6 秒后蝙蝠扑脸。进了战斗他又躺平:15 秒一次「轮到你了」喊了一轮之后就再没人理他(见 P2)。他惊醒后乱拍了几刀拍死两只蝠,然后原地脉冲装死——30 秒 earcon、60 秒短句、90 秒导航接管全套准时上演,比 A 局那个假装的「新手」还标准。0 金时状态链老实说「口袋空空」,乱拍捡了 6 金立刻改口「带回来一点」——账本子对乱按党也不撒谎。 + +## 新问题清单 + +### P0 + +无。R2 收敛出的 P0(看门狗饿死)在本轮双局三场景(静置/乱按/导航中推进)全部按新阈值工作。 + +### P1(功能失效/设计违约) + +1. **死亡结算链在 `die_intro` 处截断,后 6 句全部未播,结算屏静默直至玩家输入**(A 局 2786.88) + - 现场:被石甲蟹重击放倒瞬间,`die_intro`(dur 1.44s)只播了 **0.18s** 即被杀(event 层 vend 2787.06 + ASR verdict `TRUNCATED sim=0`「我。」双证),其后 `die_cause_2`/`die_adv_*`/`st_floor_3`/金币档句/`settle_stronger`/`settle_again` **零 fetch 零 vstart**,结算屏 76.0s 无任何事件(事件空窗榜第 3 位),直到我 2863.7 单击直接开出 `sys_new_run`; + - 后果:**归因真话律的死亡链(你死于什么/下次怎么办)在本局一个字没播**,E1-2 的死亡链金币句样本也随之丢失;新周目只能靠玩家盲点结算屏; + - 嫌疑:死亡瞬间与挥空链同 tick(2782.7 `btl_swish` speakGuarded 链未清 + 2786.7 致命 tel_heavy 同窗),竞态杀掉了 `_speakGuarded` 结算链;R2 两场死亡全链逐句播完,本条按回归嫌疑处理,建议 dev 用「挥空瞬间被打死」复现; + - 缓解事实:点击结算屏仍能正确开新周目,不卡死。 + +### P2(不适/可疑) + +1. **战斗中完全静止(不出招也不乱按)超过一轮提示后彻底安静**(B 局 248.8 → 456.3,实测事件真空 207.5s):战斗看门狗只有 15s `gd_turn`+30s tick 两级,之后 disarm;代码面 `_stuckCheck` 仅在 EXPLORE/DRILL 态运行,无进展轨不进战斗。设计逻辑自洽(乱按会推进战斗回合=进展,不该升级),但「吓得僵住不动」的玩家在战斗里喊一轮「轮到你了」后无人再理——还好回合制敌人也不主动出拳,无实际伤害,体感空。建议:战斗态静止 60s 后重复 `gd_turn` 或补一句战斗提示。 +2. **导航接管与随步重指短窗叠加**(A 局 2494.1 与 2498.5,4.4s 内两条 `nav_e`):随步重指 + 60s 周期重报在同窗相撞,`NAV_MIN_GAP`(1.5s)拦不住。 harmless 但听着重复,建议周期重报与随步重指共享节流窗。 + +### P3(建议/仪器) + +1. **`gd_pulse` 被遭遇打断时 vend reason 仍记 `kill`**(A 局 71.2-71.38,verdict TRUNCATED sim=0):引擎把「抢占让位」也走 `_killVoice`,探针 reason 字段分不出「播完」与「让位」——本局语义正确(遭遇优先),但仪器口径上 R2 的「reason=preempt 计数」可能漏计这类正常让位,建议 vend 增加区分原因。 +2. **harness speed 枚举不接受 1.7**:`{"speed":"1.7"}` 被规整为 1,R2 的 1.7x 语速+ASR 盲区压力通道本轮不可复现(仪器侧,未改 harness)。 +3. 未覆盖的 R2 已验项未重测(非本轮范围):自动喝药、D9 教学看门狗 30s `stair_hint_a`(B 局在真实 D9 中已顺带复现 171.6/194.4)、菜单首项播报。 + +## 做得好的地方(实话实说) + +- **30/60/90 无进展阶梯是精确的**:两局四个场景,earcon→短句→接管的间隔全部落在阈值+轮询粒度内;且「任何手势都不重置无进展轨」真的做到了——我一边脉冲一边读秒,它照样按点升级。 +- **「到达即报」是本轮最动人的修复**:R2 那句我一辈子听不到的「楼梯就在脚下」,现在踏上去就自己响,还带贴身铁器回声;同时脉冲射线报铁器的老路没被拆掉。 +- **两条教学退出路径都是活的**:静置 92 秒被请出去,乱按卡 D9 也被请出去,跳教后 6 秒就有蝙蝠来面试——E0-2 的两个断言全都不是压线过。 +- **逃跑三态干净利落**:短松=取消+一句教学补课(每场一次不烦人)、按满=真逃挨一下、Boss=拒绝回执 sim=1.0。 +- **金币账本三档全真话**:0 金「口袋空空」、少量「带回来一点,不多」、攒多「有些积蓄」,上滑一条链随账自动换档,A/B 两局互为对照。 +- 连续三轮(R1→R2→R3)`preempt` 从 35,611 → 0 → 0,风暴没有复发。 + +## 一句话裁决 + +**R3 四条修复全部在真机上兑现——看门狗终于长了一张按 30/60/90 秒准时报事的嘴、教学留不住赖子也困不住新手、楼梯一踩自己报、金币账本三档全真话、逃跑长按有了手感——唯独死亡结算屏在 Boss 战说了一半「你倒下了」就哑火 76 秒(新 P1),把这条修顺,dungeon 就是可交付状态。** diff --git a/research/new8/dungeon/round3/p1-blind.md b/research/new8/dungeon/round3/p1-blind.md new file mode 100644 index 00000000..93df1fe4 --- /dev/null +++ b/research/new8/dungeon/round3/p1-blind.md @@ -0,0 +1,24 @@ +# P1 视障资深玩家评审:《回声地窟》(R3 终审) + +> 评审依据:research/new8/dungeon/round3/transcript-main.md(806 行全读:教学→L2→L3→Boss 死亡→归因链→新周目,含跳教学二周目开局)+ transcript-stress.md(实际 5080 行,任务书写 4729——按实际材料评审:前 800 行全读,另全读 t+3716 抢救句段(3795-3854)与 t+4506-4524 死亡/收尾段,并对全卷做语音清单全量去重计数、关键 id 计数、L2 流浪窗(t+950-4380)分区统计)+ round2/p1-blind.md(我自己的 R2 意见与条件)+ docs/design/16-dungeon.md §R3 修复回归(dev 声明,只作对照不作耳证)。round3/ 无 live-player.md,全部结论出自转写耳证。 + +### 回声地窟 +- 状态感知:4/5(保持,分数没动、内容换血)—— main 局已接近满分校:下潜演出四件套、BGM 纪律(战曲每场退,t+52.8/145.5/158.6/224.6/240.7;boss 曲 t+252.3;死亡切 settle)、上滑五段链随叫随到,新增的到达即报(t+119.3/176.4「楼梯就在脚下。单击,下潜。」+mat_stair)是三轮里最好的新信息源。扣分照旧三笔,都是旧账:一是教学 D9 乱按 764 秒(t+52.5→816.8)只有每 8 秒一对 nudge+watchdog earcon(93 对,0 句人话)——滴水刑刑期从 R2 的 39 分钟减到 13 分钟,但刑具没换;二是 152 次 bump 零归因;三是 main 死因句仍与我的耳朵顶牛(t+290.0「你背后的偷袭,要了命。」vs t+286.6 正面 tel_heavy 前摇 1.2 秒后打死我,全程只有 mob_step_heavy_r/tel_heavy_r 右侧事件,没有任何「背后」——详见建议 2)。 +- 下一步明确性:3.5/5(R2 3/5 → +0.5)—— 加的 0.5 有实据:到达即报让「找到楼梯」从碰运气变成一定知道(两次下潜 main t+119.8、stress t+817.3/4312.0 都是踏格即报即下);金币账说了人话(band1「带回来一点,不多。」main t+296.3/304.8 + stress 11 处,过 50 升「有些积蓄」7 处,band0 只在 TITLE 新档 t+0——R2 那个「捡到金币→口袋空空」的谎死了);L2 入口密度保底兑现(t+823.7 进层,t+830.7 首遭遇,7 秒;2 分钟 5 场)。没给满的原因也硬:抢救升级对「边走边迷路的人」仍然基本不开口——L2 乱走 46 分钟(t+937→3716)全部抢救产出是 14 声 turn_tick 加 1 句「找找铁器的回声,那是楼梯。」(t+3716.5,带 warning 震),导航接管(最短路首步)整局 0 次;「跳教学 90s 自动滚出」在零位移断言场景成立,在乱按剧本的现实里没发生(详见条件核验 4)。 +- 愿意继续玩:是 —— 主线弧线三轮下来第一次让我觉得值得反复按:main 一命全弧 307 秒干净跑通、死得明白(压力局归因句「你没挡住那记重击」是真话)、账目分毫能平、到达即报补上了教学承诺最后一口气。我 R2 的条件「把看门狗的嘴装上」兑现了一半——嘴装上了,t+3716.5 真的出过声;但牙口只对原地不动的人生效,这半口挂 v2,不影响我把票投给可交付。 +- P0 问题:无(R3 零新账)。R2 那条 P0「迷失抢救只会响、不会说」降级为遗留 P1:恶意程度从「39 分钟 0 句 + 2223 秒无语音窗」收敛到「13 分钟滴水 + 46 分钟 1 句」,且升级管线被证明确实通到人话(t+3716.5;战斗里更是随叫随到——main t+278.5「轮到你了。单击挥击。」)。旧账三笔仍在我账本上盯着,不重复立案:教学滴水(t+52.5→816.8)、死因句说谎(main t+290.0)、空口脚步播报(stress t+357.1/488.9/597.2/782.2,见建议 3)。 +- 建议: + 1. 无进展轨的重置口径再退半步(我 R2 条件的最后一步):现在「格位置变化」就算进展、就重置轨道——可「一直在动手的迷路者」恰恰一直在变位置,这正是 R2 漏的那个洞。把重置条件收成「离楼梯距离缩短 / 拾取 / 战斗回合 / 下潜」,位置横跳不算——乱走的人才能攒满 30/60/90 秒,短句和导航接管才会在他迷路的第 1 分钟而不是第 46 分钟开口。 + 2. 死因句从真实致死前摇取材(R2 建议 2 原样奉还,两轮零进展):main t+286.6 正面 tel_heavy 前摇 1.2 秒后打死我,t+290.0 却报「你背后的偷袭,要了命。」;压力局同一句槽播的是真话(t+4520.1「你没挡住那记重击」——零格挡,真)——说明这条管线会写真话,把 main 这句也接上真前摇。顺带把 152 次 bump 的归因句接上,撞墙不该是全场唯一哑掉的失败。 + 3. 远距威胁播报收口(R2 建议 3 原样):stress 四处「右边有脚步声」(t+357.1/488.9/597.2/782.2)耳边零 mob_step——声音没到就别宣称有脚步。main t+309.0「左边有脚步声」与 mob_step_small_l 同拍,证明声画同达做得到,收紧触发半径即可。 + +#### 条件满足核验(我 R2 交代的是「把看门狗的嘴装上」,连同本轮四件套逐条验) +1. **E1-1 到达即报 → 兑现。** 四次楼梯到达全部踏格瞬间播报:main t+119.3/176.4、stress t+816.8/4257.5,句+mat_stair 齐到,单击照常下潜、不占输入通道;L3 接近段 echo_3→echo_2+mat_stair 收紧链(main t+198.1-205.3)让「铁器=楼梯」在到站前就能听出来。教学 D9 那句承诺两轮欠账,这回连本带利。 +2. **E1-2 band1 新词 → 兑现。** 「带回来一点,不多。」上滑/SETTLE/TITLE 三处同一管线(main t+296.3/304.8;stress 11 处);金币过 50 后升「有些积蓄」(stress t+1131.8 起 7 处,随拾取与鼠 loot 爬升,账跟手);band0「这趟没带回什么。」只在 TITLE 新档(t+0)。R2 那 27 次「口袋空空」谎报绝迹。 +3. **E0-1 看门狗饿死 → 半兑现。** 升级管线的零件都在且真的通到人话:L2 t+3716.5 stair_hint 短句+warning 震是 75 分钟压力局里唯一的探索态人话(R2 是 0 句);战斗看门狗会说话(main t+278.5)。但无进展轨由「格位置变化」重置——乱按剧本全程在走,教学 764 秒 93 对 earcon 零升级、L2 乱走 46 分钟 1 句提示、导航接管 0 次。断言绿的是「零位移乱按」场景,我 R2 点名的是「一直在动手的迷路者」——这个人现在还是几乎得不到句子。嘴装上了,牙口没长齐。 +4. **E0-2 跳教学 90s 自动滚出 → 半兑现。** 「教学乱按零进展 ≤95s 滚出」在零位移场景成立(dev 断言,我无耳证渠道);乱按剧本的现实是教学从 t+52.5 困到 t+816.8 = 764 秒,tut_skip 全场 0 次——比 R2 的 2403 秒好得多,但这回是自己摸到楼梯、被到达即报送出去的,不是被 90 秒的逃生门放出去的。跳教学密度保底:L2 入口 7 秒首遭遇、2 分钟 5 场 ✓;但一周目没 skip、二周目刚开局就收工,「跳教学后 120s 内必有指向语音」我无从耳证,挂账待真机。 + +#### 三套信息一致性(终检,我的招牌专节) +1. **回声 vs 空间:过。** main 全程 pulse-移动对账无一矛盾:echo_1 贴脸墙→t+15.8 bump;echo_4 远墙→t+61-69 畅行;侧向开口语义两局在线(stress side_open 602 次/side_wall 238 次);L3 楼梯接近 echo_3→echo_2 配 mat_stair 收紧(t+198-205);到达档 echo_1+mat_stair 编码与脉冲贴身档一致(t+119.3)。R1 的「两态节拍器」与 L3 漂移彻底绝迹。 +2. **脚步 vs 播报:主线过、压力局欠。** main t+309.0「左边有脚步声」↔ mob_step_small_l 同拍,声画同达;heartbeat 守 ≤2 格口径(main t+129.4/132.1/134.4)。残留两笔:stress 四处空口「右边有脚步声」(耳边零 mob_step,见建议 3);L2 乱走 46 分钟 33 声 mob_step 纯伴舞、0 遭遇——密度下限仍只保主路径,迷路者的世界从「有滴滴伴奏的真空」升级成了「有一句提示伴奏的真空」。 +3. **播报内部:账全平,一句台词说谎。** 上滑五段链两局 60+ 次随叫随到;金币三档(band0/1/2)跨拾取、跨消费、跨死亡逐笔能平;死亡归因压力局全真话(t+4520.1「你没挡住那记重击」+t+4522.2「下次,前摇声响时就双击」,零格挡属实);main 局死因句仍说谎(t+290.0,见建议 2,两轮旧账);settle 链在 stress 被玩家自己的重开单击掐尾(t+4522.2 die_adv 与「新的探窟」同拍)——自己的手,不记游戏账。 diff --git a/research/new8/dungeon/round3/synthesis.md b/research/new8/dungeon/round3/synthesis.md new file mode 100644 index 00000000..c635d972 --- /dev/null +++ b/research/new8/dungeon/round3/synthesis.md @@ -0,0 +1,26 @@ +# R3 Synthesis —《回声地窟》(dungeon) + +> 产品裁定(批次验收收口轮)| 证据源:R3 真机确认局(round3/live-player.md:六项验证全兑现,三会话 preempt=0 零死锁)+ 4 人设终审(P1 4/5、P2 4.5/4、P3 4/3、P4 4.5/4.5——全员愿意玩,无 P0) + +## 1. 验收门裁定:**有条件通过——P1 热修后交付** + +- 章程门槛(≥3/4 人设愿意玩 + 无 P0):**4/4 愿意玩,P0=0**。R1 0/4 → R2 4/4 翻案 → R3 全员维持并升分,翻案弧线完整。 +- 真机六连验:看门狗 30/60/90s 三级严丝合缝(两局双实锤)、跳教学 94.8s 自动滚出+5.9s 后即有接战、到达即报两样本、band1 三档句句真话、逃跑 900ms 教学句、Boss 退路句 sim=1.0。 +- **唯一阻塞项(live 新 P1)**:死亡结算链在 `die_intro` 截断 0.18s、后 6 句全断、结算屏静默 76s(疑与挥空链同 tick 竞态,回归嫌疑)。死亡结算是一次 Roguelike 的情绪核心与元进度入口,**必须热修**。 + +## 2. 热修裁定(H1,本轮内完成) + +- **H1 死亡结算链截断**:根修同 tick 竞态(死亡链与战斗收尾链互掐);加回归断言(死亡瞬间正在播挥空/命中链时,die_intro 及后续 6 句完整播出);playtest 全绿后做**定向 live 复验**(专门快速打一场死,验证结算链完整+结算屏不再静默)。 +- H1 完成即标记 dungeon 可交付,进入用户上手体验。 + +## 3. 遗留(非阻塞,v2 候选) + +- **漫游型迷路抢救盲区(P2/P3 独立定址,P1 附议)**:无进展轨把「位置变化」算作进展,纯漫游(L2 46-57 分钟)刷不响升级。修法方向:进展定义改为「向楼梯梯度下降/新区域发现」,或漫游累计距离单独成轨。**v2 第一条。** +- 「楼梯方位半句」(P4):到达即报已兑现,方向词(东南角类)留 v2。 +- 战斗完全静止 15s 后 207s 静默(live P2);nav 重报 4.4s 短窗叠加;vend reason=kill 吞掉让位语义。 +- 仪器:speed 1.7 harness 不可用、探针通道隔离、R2 已验项抽检策略。 +- 人测项(roadmap §4):amb_cave/arg_muffled 听感、2.2x 语速、真机震动手感。 + +## 4. 后续 + +H1 热修完成后 dungeon 冻结功能面,与 radio 一起进入用户上手体验;其「脉冲回声核心动词 + seed 生成 + 元进度遗产」模式与「双轨看门狗」沉淀为模式款模板(批次 2 的 detective/teahouse/bridge 按需取用);漫游抢救漏洞列入批次 2 引擎层改进清单(若确认为引擎 guide 通用语义则在批次 2 首游戏前修)。 diff --git a/research/new8/radio/round3/live-player.md b/research/new8/radio/round3/live-player.md new file mode 100644 index 00000000..75983ce8 --- /dev/null +++ b/research/new8/radio/round3/live-player.md @@ -0,0 +1,100 @@ +# 《不眠频率》R3 收口确认轮 · 全拟真试玩报告 — 老墨(走心局) + +## 基本信息 + +live harness(真 chromium + SenseVoice ASR 听游戏实况,非 mock)。本轮为 **R3 打磨修复(G1-G6)的现场确认轮**,顺带对 SA3 落地后未经 live 检验的 arg_muffled / brake_screech 取证。 + +- 设置:`voice=male, speed=1.0, bgm=1.0, guide=full` +- 路线:走心局 p1L/p2L/p3L/p4R(问她)/p5L/p6L/p7L/p8L/p9L → **e1「天亮之前」**,**rep=12 与设计表分毫不差**(+1+2+2+1+1+2+2+1),五 bond 全 1 +- 手势 65 个(tap 38 / swipeup 7 / swipeleft 9 / swipedown 2 / swiperight 1,含有效/无效各类),语音 **125 段** +- 判定:**OK 119 / SFX_BGM_ONLY 70 / TRUNCATED 4 / MISMATCH 2 / SILENCE 0** +- 4 处 TRUNCATED 全部归因到我自己的手势(p1 mid-prompt 缓存滑 / bridge 上滑打断 rep_high 播报 / 0.16s sys_help 竞态毛刺见 P3-1 / 我的 tap 与 gd_continue 起播撞在同 0.3s);**0 处游戏自残截断** +- 2 处 MISMATCH 均为 ≤6 字短句 ASR 同音(z1_songo 0.56 / sys_menu_resume 0.57),事件层双双足时播放——按协议不算游戏问题 +- 报错:**console 事件 = 0**;零叠音(125 段 vstart 全部晚于上一 vend)、零死锁 +- t= 均为解锁后秒数(events 时基 ta);震动模式记法同 haptics.js(confirm[20] / warning[30,50,30,50,30] / beat[8] / error[60,40,60] / danger[25,40,25,40,25]) + +--- + +## G1-G6 逐条现场结论 + +> 判「修好」标准:R2 synthesis 记录的旧行为做出同样操作后不再出现,且新行为被事件/音频双重证据锁定。 + +### G1 帮助链去重 → **修好**(三类等待点 ×2 次上滑全部干净) + +- **ONAIR(S:z1_ring_in)**:第 1 次上滑 t=186.7 → prog_c1(2.24s) → rep_low → **sys_help 完整 9.04s**;同点第 2 次上滑 t=223.8 → `[10]+warning+beat[8]`,语音只有 prog_c1 → rep_low → z1_ring_in 复播,**events 里 sys_help 的 fetch 与 vstart 一个都没有**——口令一次没重播,状态句照播。 +- **BRIDGE(键 'B')**:第 1 次上滑 t=839.0(C2→C3 幕间)→ prog_c3 → rep_high →(sys_help 已入队);我 10 秒后的第 2 次上滑 t=849.3 掐掉在播的 rep_high,**新链只排 prog_c3 → rep_high**,`beat[8]`@849.66,无 sys_help——去重生效。 +- **ENDING(E:e1)**:第 1 次上滑 t=1588.6 → e1_a(7.68s 足时) → rep_high → **sys_help 完整 9.07s**;第 2 次上滑 t=1611.3 → `[10]+beat[8]`,e1_a → rep_high,**无 sys_help**。 +- **换新等待点首次上滑仍完整口令**:三处首滑全部完整播 sys_help(3/3)。全场 sys_help vstart 总数 = 3(两处全量 + 一处 0.16s 竞态毛刺见 P3-1),对比 R2 压力局 38 次逐字重播——公理「提示永不逐字重复」现场闭环。 +- 边界说明:①「sys_skip 每实例一次」同款的重进游戏重置口径本轮未复测(需二次会话),代码注记与实现一致(实例级 Set);② nudge earcon 为振荡器运行时合成,不产生 ABSN 事件,事件层不可见,混音层 0.1s 窗口与垫乐不可分离——以 `beat[8]` + 零 sys_help fetch 为准签名(P3-2 有仪器侧建议)。 + +### G2 rep_low 补 warning 震 → **修好**(含阴性对照) + +- 全场 `warning[30,50,30,50,30]` 恰好 **2 次**:t=187.1 与 t=224.1——正是仅有的两次 rep=0 档上滑复听,「还没立起来」出口同时手腕有感,与 TIER_VOICES 注记终于一致(R1 P3-3 / R2 P3 遗留销案)。 +- **阴性对照**:rep=mid/high 的全部播报位(bridge 跨档 rep_high t=837.9、ENDING 上滑 rep_high t=1596.8/1619.6、五段尾声)**零 warning**——震与档位精确挂钩,不是逢上滑就震。 +- 覆盖说明:幕间 enterBridge 的 rep_low 分支(game.js L838)走心路线到不了(rep 只升不降),该支路与上滑位同用一个 `_tierCue`/同款 vibrate('warning') 调用,本轮以两处实测 + 代码同源作证。 + +### G4 不可跳句空点单击轻震 → **修好**(6 例连击签名,无一误伤) + +- 签名 = gesture 层 `[20]` 后 0.0-0.4s 内跟一记 game 层 `beat[8]`(20→8 连击 ≠ 真确认的单记 20),且被点句子**足时播完**: + - 抉择提示句(PROMPT_RE 支路)×2:**p3** t=722.5(起播后 2.0s 处单击,6.03s 足时)、**p5** t=1047.0(7.52s 足时); + - F5 链末句支路 ×4:l1_a t=670.9(9.2s 足时)、l1_goo t=828.3、k1_trutho t=1079.9(10.96s 足时)、w1_a t=1171.4——本作通话叙事句全是链末等待型节点,播播中单击一律「点了但没选上」+轻震。 +- 对照组:抉择**等待态**(voicePlaying=false)空点 t=314.8 只有 `[20]` 无 `[8]`,走 armChoiceHint 重挂兜底——两条支路区分干净,R2 P3-4「像点上了实则什么都没发生」的误导签名消失。 + +### G6 上滑状态链「状态在前、帮助垫后」→ **修好**(三处实测同序) + +- ONAIR t=187.2-200.7:**prog_c1 → rep_low → sys_help → 节点复播**; +- BRIDGE t=839.5 起:**prog_c3(下一通) → rep_high → (help)**; +- ENDING t=1589.0-1608.8:**e1_a(结局名) → rep_high → sys_help**。 +- 三类等待点顺序完全一致:先报「第几通/第几个结局 + 口碑档」,帮助永远垫底。RING 位按 §2.3 本就无帮助句(代码注记),本轮未单独复测。 + +--- + +## 整体回归(一局到结局) + +- **完整走心局打出 e1**,rep=12 分毫不差;条件差分全对:p4R→heardArgument=true → C4 播 **w1_say 组**(非 w1_say2);bond_zhou=1 → **h1_echo t=1449.79 在 h1_talke vend 后 0.10s 准点接入**;五 bond 全 1 → 尾声 **ep_zhou/deer/kid/wife/han_ok 五连全救版**逐条点名;p9L → knot → d9_face 链 + **tape_static**。 +- **SA3 真音效全部在局内起播(fetch 全 200,ABSN 全部持到自然结束)**: + - **onair_sting** t=2.19(2.14s,b_open 开台); + - **phone_ring** 三通 RING 循环垫(5.2s loop,接听即停早于人声:C1 stop@109.84 vs 人声 109.94 等); + - **line_drop** 五通挂断咔哒 ×5、**line_connect** 接通 ×5; + - **amb_rain_city / amb_station / amb_kid_room / amb_street_rain / amb_cab** 五个场景垫全起; + - **arg_muffled(C3 争吵闷响)**:k1_b 上**两次**起播——本体 t=964.04 与句中回声 t=970.54(= k1_b 起播 +6.5s,sfxEcho 设计值),各 4.58s 持到自然结束,无 stop 事件——「2-3 次取 2」的信息层线索音落地;听感证据为引擎层双起播(混音层与语音/垫乐重叠,astats 无法单列,RMS 差 ~1dB 不作数)。 + - **brake_screech(C5 危机拍)**:t=1432.82 与 danger 长震 `[25,40,25,40,25]` **同帧起播**,2.38s 持到结束,**先于人声 h1_danger 整整 2.10s**(preMs 2000 设计值,R2 实测 2.1s 复现)——刹车声→预警句→dango 的自动流一气呵成,全程不等单击。 +- F1-F5 无回潮:无效手势零静默(我在叙事链末误滑 3 次全是 error[60,40,60]+gd_invalid,看门狗照常重挂);看门狗全准点(gd_continue/gd_again 25s+25s、gd_ring1/gd_ring2 10s/25s 多轮);F3 接听即停 ×3;F5 尾句 d9_facee(5.30s)/h1_talke(3.52s) 足时;mid-prompt 缓存滑照常零插播生效(p7 t=1302.5);菜单开合(ENDING 结算态 swipedown → 菜单 → 再 swipedown → 结局链从头无缝复播)正常。 +- G3 旁证:male 目录缺失的 19 句 NPC 平铺组每次播放先探 4 个扩展名 404 后回退平铺 200 播出,功能与音色衔接无碍(设计即单音色);非资产类 **console=0**。 + +--- + +## 问题清单 + +### P0(卡死/死锁/状态混乱/游戏自残截断) +无。 + +### P1(体验断裂) +无。 + +### P2(不适/设计意图缺席) +无。 + +### P3(建议,3 条) + +1. **连续上滑的链令牌竞态毛刺**:BRIDGE 第 2 次上滑落在 rep_high 自然结束前 0.08s(t=849.30 vs vend 849.38),被掐链的续播回调抢先放出了 0.16s 的 sys_help 毛刺(vstart 849.50 → vend 849.66 kill)才被新链令牌叫停。功能无损(新链去重正确、听感是一声咔),但复现窗 <0.1s 的竞态说明 `_playSeq` 续播处的令牌校验晚于语音起播,建议把 seqToken 检查前移到队列出队之前。 +2. **nudge earcon 无事件层签名**(仪器侧):earcon 走振荡器合成,探针七类 patch 均不经过,G1 去重的「纯音频标记」只能靠 beat[8]+零 sys_help 反证。建议 harness inject.js 给 `guide._playEarcon` 加一类轻探针(或在 dev 侧 emit 一条 debug 事件),下一轮可把「震+声」双证据钉死。 +3. **抉择等待态提示句仍 25s 逐字重播**:p3 在抉择等待期以 25s 间隔整句重播了两遍(t=751.3 / 777.3,armChoiceHint 看门狗兜底)。与 G1 刚兑现的「提示永不逐字重复」公理同源——等待点帮助已去重,ASK 态的选项句还是逐字复读。纯观察项(R2 起就有、非本轮回归),人设复审时可议是否同样降为纯音频标记。 + +## 亮点 + +- G1 三类等待点九次上滑零逐字重播,账目口径(状态句永远照播)一点没伤;G2 的 warning 精确得像档位传感器;G4 的 20→8 连击签名把手感误导真正变成了可读反馈。 +- 危机拍这回是「听见刹车再听见人喊」:screech 先到 2.10 秒,danger 震同帧,dango 自动接——SA3 的声音头一回真正参与了叙事节奏。 +- 账目依旧分毫不差:rep=12、五全救尾声、h1_echo 的 0.1s、w1_say 组的条件差分,一个数都不带含糊的。 + +## 一句话裁决 + +**愿意放行**——G1/G2/G4/G6 四修全部现场坐实且零回潮,完整走心局零叠音零死锁打出 e1(rep=12 分毫不差),arg_muffled/brake_screech 真声入局,radio 可以进 4 人设全员复评;剩下的只有一条 0.16s 竞态毛刺和两条仪器/打磨级建议(P3×3,无 P0/P1/P2)。 + +--- + +### 附:产物与证据文件 + +- 本局:`/home/xsl/SoundGame/web/harness/live/out/radio-20260902145336/`(events.jsonl 796 事件/125 语音段/65 手势、verdicts.json 195 行、transcript.md、mix.webm 30MB 真实混音) +- 关键事件窗:G1/G6 ONAIR 对 186.7-232.3;BRIDGE 对 837.8-859.7;ENDING 对 1588.6-1622.5;G4 六例 670.9/722.5/828.3/1047.0/1079.9/1171.4;G2 warning 187.1/224.1;arg_muffled 964.04+970.54;brake_screech 1432.82(人声 1434.92) +- 引用代码(只读,未改动):`/home/xsl/SoundGame/web/radio/game.js`(G1 `_helpSpokenOnce` L1170、G2 `_tierCue` L1157、G4 L455-463/L577、G6 `_withHelp` L1184、crisis 节点表 L159-166);`/home/xsl/SoundGame/web/engine/haptics.js`(模式表 L35-46) diff --git a/research/new8/radio/round3/p1-blind.md b/research/new8/radio/round3/p1-blind.md new file mode 100644 index 00000000..28437957 --- /dev/null +++ b/research/new8/radio/round3/p1-blind.md @@ -0,0 +1,22 @@ +# P1 视障资深玩家评审:《不眠频率》(R3 终审) + +> 评审依据:research/new8/radio/round3/transcript-main.md(E1 全救版一局)+ transcript-stress.md(乱按压力局,与 R1/R2 同剧本逐拍可比:菜单 38 次、乱试 199 次、收尾「结局链照样走完」)+ round2/p1-blind.md(我自己的 R2 意见,逐条回执)+ docs/design/15-radio.md §486-537(G1-G6 dev 侧登记,只作对照不作耳证)。round3/ 目录**没有 live-player.md**——R3 协议第 2 步那局 live 确认没进我的材料,见建议 2。 +> 沿用我两轮立的规矩:【状态】行是转写器注记,按实际发声评审;离线时间轴断不了案的地方,断言与真机优先。 + +### 不眠频率 +- 状态感知:5/5(R1 4.5 → R2 4.5 → 本轮收官)—— R2 扣的 0.5 两笔,一笔修掉一笔销账:一是帮助链垫在状态前面,G6 重排落地,main t+54.1「现在是今晚第一通电话。」→ t+56.3「今晚的口碑,还没立起来。」→ t+58.4 才轮到帮助句;stress 39 次状态查询(t+17.0/44.5/55.0/65.4……)全部此序,无一例外。二是 F3 铃声离线听不见——那是仪器欠我的,不是游戏欠我的,记在仪器账上(见建议 3)。本轮还白捡一笔:G2 的 rep_low warning 震终于到账,main t+54.1、stress t+17.0/44.5 共九处 rep_low 状态播报带警告三连震,跨档之后(stress t+182.6 起不好不坏)自动闭嘴——警告只在该警告的时候警告,这才是纪律。底盘两轮没退过:每通报名(main t+14.8/96.9/127.1/161.2/200.0)、199 次乱按后锚没垮(stress 收尾状态行照报「乱试 199 次、逛菜单 38 次」)。另外 SA3 音效这回在离线转写里首次成套入耳(arg_muffled t+132.4、brake_screech t+223.0/994.2、tape_static t+244.7)——R2 我说的「凑齐进推荐位」,从真机报告升级成了我转写耳朵里的货。 +- 下一步明确性:5/5(保持)—— 开场三件事 t+2.1/6.4/10.6 一次讲清,第一通 t+14.8 就到;看门狗 main t+42.0 准点;G1 去重把重复上滑从「约 10 秒全量链」变成「约 4 秒短链+beat 标记」,对 75 档语速用户是净减负,信噪比反而涨了。main 全程 253 秒到 E1 收播,没有一秒不知道该干什么;stress 75 分钟乱流零死锁零无声吞手势(收尾行原文背书)。 +- 愿意继续玩:是 —— 三轮里最硬的一票:R2 我留的两个 0.25 名目一笔修掉(G6)一笔销案(G3),synthesis 挂的 G1/G2/G5 三件全部白纸黑字落地;stress 打出 e4 变体收播(t+1018.0-1020.1「留言机的红灯,又多亮了一夜」)加尾声混合账,main 打出 E1 五人全救。四结局我已见其四。这班夜车在我这直接发车。 +- P0 问题:无(两局转写范围内)。失败归因照例逐笔对账:stress t+494.0「她走了。没上车,也没退票」(我 t+457.2 说的「退不退,都是你的自由」)、t+664.3「你许了个兑不了的现」(我 t+659.2 许的谎);尾声五条逐人入账(t+1028.6-1037.1:老周谢歌对应我 p1 陪哼、小鹿退票对应 p3 右、糖糖楼下在吵对应谎、陈姐末班车、老韩小红花对应陪聊),因我陪了歌,t+1006.5「这城市的夜,是接着的」照常接入——bond 回响不撒谎。跨档播报只在 t+964.8「在听众里立住了」出现一次,低→低不播——账没多也没少。 +- 建议: + 1. G1 的 v2 尾巴(不阻塞交付):去重按等待点键控,每个新节点首滑仍是全量帮助句——stress 全量帮助从 R2 的 39 次降到 20 次,减半但没归零。下一版把「全量」收成每局一次+菜单常驻入口,其余一律短版,75 档用户的过路费才能清零。实例级、不跨会话这一条我认:重进游戏首滑给完整口令是对的,别改成跨实例。 + 2. R3 协议第 2 步那局 live 确认(重点本来就是「G1 帮助链行为、真铃声在局内表现」)没进我的材料。收口归档前补进 round3/——我手里最后两笔仪器账(RING 离线无铃声事件、压力局语速仍标 1)要靠真机耳证销案。 + 3. 转写器再收敛一步:t+0 那块 44 行「资产缺失→静默」警告块这回没了,干得漂亮;把 RING 铃声事件和语速标注也接上,别让下一轮评委接着做考古。 + +#### R2 遗留回执 +1. 【建议 1:上滑状态链重排并截短(R1 建议 2 一脉,两轮挂账)】→ **已修(G6)**。「当前通号 → 口碑 → 帮助」两局 39 次全部此序;MENU 链无状态句、帮助保持在前——分寸对,没有一刀切。销账。 +2. 【建议 2:「首播必完整、复播可即掐」写成显式规则+断言(G5)】→ **已登记**。15-radio.md 规则语句白纸黑字(抉择句 PROMPT_RE 门、F5 链末保护、F4 锁释放、复播即掐各归各条),新增断言「锁不随复播重新武装」把「保护不得扩大化」也一并锁死。口头契约转正,收货。 +3. 【建议 3:转写器与真机口径收敛(三件)】→ **清了一件半**:t+0 警告块消失(销);RING 离线仍无铃声事件、压力局语速仍标 1(仍挂,纯仪器账,见建议 2/3)。 +4. 【synthesis G1:帮助链去重】→ **已修,且按我 R2 立的规矩办**。「状态句照播——复述状态=合法帮助」是我 R2 亲口说的先例,dev 抄了作业:同节点第 2 次起帮助句被 beat 轻震+nudge earcon 顶掉(stress 18 处,R2 是 0),状态(通号+口碑)一次不少。选纯音频方案、不加新台词,成本判断我也认。剩余尾巴见建议 1。 +5. 【synthesis G3:k1_a/k1_b male 静默回退】→ **解释成立,销案**。糖糖设计即单音色童声 NPC(§5.3 约束),male 404→平铺 200 是回退链正常工作;「同通声线跳变」实为主播声部(随 voice 设置)与糖糖童声交替,属电话音质感。不为它破 NPC ≤5 音色约束,这个判断是对的。 +6. 【R2 五修回归】→ **零倒退**。看门狗 main t+42.0 准点;尾句保护「他们明天,会一起给我切蛋糕吗?」t+150.4、「但空着的那一秒,你说满了」t+246.8 一字不少;rep=0 九处仍是「还没立起来」;F4 续玩锁 stress 未见异常。 diff --git a/research/new8/radio/round3/synthesis.md b/research/new8/radio/round3/synthesis.md new file mode 100644 index 00000000..c1a50040 --- /dev/null +++ b/research/new8/radio/round3/synthesis.md @@ -0,0 +1,19 @@ +# R3 Synthesis —《不眠频率》(radio) + +> 产品裁定(批次验收收口轮)| 证据源:R3 真机确认局(round3/live-player.md:四修全坐实零回潮,e1 收局 rep=12,125 段零叠音)+ 4 人设终审(P1 5/5、P2 5/5 首评、P3 5/5、P4 5/5——全员满分档,全员愿意玩,无 P0) + +## 1. 验收门裁定:**通过,标记可交付 ✅** + +- 章程门槛(≥3/4 人设愿意玩 + 无 P0 状态混淆):**4/4 愿意玩,P0=0,超额达成**。 +- 三轮轨迹:R1 5/5 愿意玩(P0=0)→ R2 五修坐实 → R3 打磨收口。radio 全程未出现状态混淆类 P0,是批次 1 的质量基准线。 +- 真机最终局:SA3 真声全面参与叙事(arg_muffled 本体+6.5s 回声双起播、brake_screech 先于人声 2.10s 同帧进危),「声音即世界」的设计意图在真机上成立。 + +## 2. 遗留(全部非阻塞) + +- **P3×3(R3 live)**:0.16s sys_help 竞态毛刺(连续上滑落在自然结束前 0.08s 被掐链抢播)→ v2;nudge earcon 无事件层探针 → 仪器建议;ASK 态提示句 25s 逐字重播 → 与 G1 公理同源的设计张力,记 v2 手势宪法专项。 +- **v2 候选**(各轮人设建议汇总):G1 去重集合跨实例持久化;状态链第 4 段化;4 结局收集进度轻提示。 +- 仪器:harness speed 字符串归一、丢 tap 重发、探针通道隔离(同机双 live 混音)——记入批次 2 协议改进。 + +## 3. 后续 + +radio 冻结功能面进入用户上手体验;批次 2 启动时作为叙事模式范本(模板沉淀:15-radio.md + 状态机/抉择树/口碑数值三件套可直接复制到 abyss/cipher/keeper)。 diff --git a/web/dungeon/game.js b/web/dungeon/game.js index 2cc6fb13..c3cfe116 100644 --- a/web/dungeon/game.js +++ b/web/dungeon/game.js @@ -17,7 +17,9 @@ * 死亡归因真话律:die_cause_* / die_adv_* 按 lastHit(击杀者/背刺/未格挡)与 * run.parries 真实选择;卡关双轨(R3 E0-1):无操作轨 60/120/180s(§10.3,手势重置) * + 无进展轨 30/60/90s(只由楼层/格位置/战斗回合/拾取重置:earcon→短句→导航接管, - * 乱按不再饿死;教学态接管=滚出教学);踩上楼梯格到达即报贴身铁器回声(R3 E1-1)。 + * 乱按不再饿死;教学态接管=滚出教学);踩上楼梯格到达即报贴身铁器回声(R3 E1-1); + * 结算入口 800ms 手势死窗(H1:死亡瞬间在途的 tap 按新状态路由会把结算链掐死在 + * 半句——_enterTitle/stopVoice 截杀 die_intro,R3 live A 局 2787.057 实锤)。 * * 豁免 E1-E4:单击=脉冲、长按=前进/左右滑=转身(E2)、对局期 * setRateBoostEnabled(false)(E3,EXPLORE/BATTLE/DRILL/DESCEND,TITLE/MENU/SETTLE @@ -50,6 +52,13 @@ const AMB_AFTER_MS = 20000; // 探索呼吸层(R2 D0-3②):静默超 2 const AMB_VOLUME = 0.35; const FLEE_HOLD_MS = 900; // 战斗逃跑长按最短时长(R3 打磨:探索走步 450ms 不变) const LONGPRESS_MS = 450; // 引擎 longpress 判定阈值(gesture.js cfg,只读参照) +// H1:进入结算后的手势死窗。gesture.js 非即时模式单击要等双击窗(300ms)关闭才回调—— +// 致命一击落地瞬间仍在途的 tap 会在死后 ~300ms 才派发,按「当时」状态路由进 SETTLE: +// tap=离开结算 → _enterTitle 的 stopVoice() 把 die_intro 掐死在半句、整条结算链作废 +// (R3 A 局 2787.057 实锤:die_intro vstart 2786.88 → vend kill 2787.057)。手势从按下 +// 到回调最迟 touchstart+450ms(长按判定)+300ms(双击窗)≈ 750ms,800ms 死窗全盖住; +// 死窗后结算手势照常(tap 回标题 / 双击重听链 / 下滑菜单)。_settleWin 同栅。 +const SETTLE_GRACE_MS = 800; /** 回声编码(§3.1):正前距离 4 档 → 延迟 / 音量。 */ const ECHO_DELAY = [0, 250, 500, 800, 1100]; @@ -139,6 +148,7 @@ export class DungeonGame extends Game { this._walk = null; this._pulseAt = -1e9; this._lastHit = null; // 归因真话律依据(击杀者/背刺/未格挡) + this._settleGraceUntil = 0; // H1:结算手势死窗截止时刻(performance.now() 时基) this._lastEcho = null; // 最近一次脉冲回声(双击重发一致性断言用) this._onStairs = false; this._stairsSpokenFloor = 0; @@ -570,6 +580,10 @@ export class DungeonGame extends Game { this.guide.disarm(); // 任何手势复位向导看门狗(off 档 no-op) if (g === 'longpressend') { this._stopWalk(); this._battleFleeCancel(); return; } // 仅停走/取消逃跑,不承载第二语义 if (this.menuOpen) { this._onMenuGesture(g); return; } + // H1:结算死窗。死亡/通关瞬间仍在途的手势(引擎双击窗 300ms 延迟派发)落进 + // SETTLE 会被当成本态手势——tap=离开结算 → stopVoice 把结算链掐死在半句。 + // 死窗内一律吞掉(ghost 手势不算玩家输入,_stuck 队列也不作废)。 + if (this.state === 'SETTLE' && !this.menuOpen && performance.now() < this._settleGraceUntil) return; this._stuck.pending.length = 0; // 玩家操作即作废未播的卡关提示(§10.3) switch (this.state) { case 'TITLE': this._onTitleGesture(g); break; @@ -1252,6 +1266,7 @@ export class DungeonGame extends Game { this._battle = null; this._stopWalk(); this.state = 'SETTLE'; + this._settleGraceUntil = performance.now() + SETTLE_GRACE_MS; // H1:吞掉在途手势(见常量注) this._session++; this._clearTimers(); this.audio.setRateBoostEnabled(true); // E3:结算恢复 @@ -1281,6 +1296,7 @@ export class DungeonGame extends Game { this._battle = null; this._stopWalk(); this.state = 'SETTLE'; + this._settleGraceUntil = performance.now() + SETTLE_GRACE_MS; // H1:同 _die(在途 tap 会误离开结算) this._session++; this._clearTimers(); this.audio.setRateBoostEnabled(true); diff --git a/web/dungeon/test/playtest.mjs b/web/dungeon/test/playtest.mjs index 39f600fc..5499049e 100644 --- a/web/dungeon/test/playtest.mjs +++ b/web/dungeon/test/playtest.mjs @@ -1240,6 +1240,57 @@ async function scenario6c() { game.stop(); } +// ================================ 场景 6d:H1——死亡瞬间在途手势不得截杀结算链 ================================ +// R3 live A 局实锤(dungeon-20260902150633 events.jsonl):致命 tel_heavy 落地 +// (t=2786.78 resolve → _die)时玩家上一记 tap 还压在引擎双击窗(300ms)里没派发, +// 2787.057 才回调——此时 state 已是 SETTLE,tap=「离开结算」→ _enterTitle() 的 +// stopVoice() 把 die_intro 掐死在 0.18s(vend kill),后 6 句零 fetch 零 vstart, +// 结算屏静默 76s(下一次 tap 在 TITLE 上开出新周目 sys_new_run)。 +// 回归断言:死亡结算后死窗(game SETTLE_GRACE_MS=800ms)内在途 tap 被吞, +// 整链 7 句完整播出(结算屏有内容);死窗后 tap 正常离开结算(设计出口不受影响)。 +// 旧代码在本场景必挂:首个在途 tap 即跳 TITLE,die_cause_* 起零播放。 + +async function scenario6d() { + origConsoleLog('\n=== 场景 6d(H1):死亡瞬间在途 tap → 结算链不被截杀,7 句完整播出 ==='); + storageMap.clear(); + const { game, audio } = newGame({}); + game.start(); + await tick(400); + g(game, 'tap'); + await tick(6000); + g(game, 'down'); + await tick(2000); + game._mobStep = () => {}; + game.run.hp = 1; // 钩子:窟主反一刀致命(对齐 live:crab 重击收尾) + game._encounter({ type: 'crab', x: game.run.pos.x, y: game.run.pos.y, hp: 20, maxHp: 20 }); + await tick(2500); + game._battle.bearing = (DIR_ORDER.indexOf(game.run.face) + 2) % 4; // 钩子:绕到背后(live 尾段同款) + const mark = audio.log.length; + g(game, 'tap'); // 挥空 → btl_swish 战斗收尾链 → 敌前摇 → 反一刀 → 死(live 同款序列) + await tick(2300); // swish(1s) 播完 → enemyTurn → 前摇 1.2s → resolve 死亡(tap 后 +2.2s) + assert(game.state === 'SETTLE', 'H1: 致命一击 → SETTLE'); + // 死亡瞬间仍在途的 tap #1:与死亡同拍(旧代码此处即 _enterTitle 截杀结算链) + g(game, 'tap'); + assert(game.state === 'SETTLE', 'H1: 死后同拍在途 tap 被死窗吞掉(仍在 SETTLE,不离开结算)'); + // tap #2:死亡 +300ms 派发(对齐引擎双击窗回调延迟,live 2787.057-2786.78≈277ms) + await tick(200); + g(game, 'tap'); + assert(game.state === 'SETTLE', 'H1: 死后 +300ms 在途 tap 同样被吞(仍在 SETTLE)'); + await tick(8000); // 结算链 7 句应完整播出(旧代码在 die_intro 半句即断) + const p = plays(audio, mark); + assert(p.orderOk('voice/die_intro', 'voice/die_cause_3', 'voice/die_adv_3', 'voice/st_floor_1', + 'voice/meta_gold_1', 'voice/settle_stronger', 'voice/settle_again'), + 'H1: 结算链 7 句完整播出(die_intro → die_cause_3 → die_adv_3 → st_floor → 金币档 → settle_stronger → settle_again)——结算屏有内容'); + assert(p.count('voice/die_intro') === 1, 'H1: die_intro 恰播一次(在途 tap 未触发 _enterTitle/stopVoice 截杀)'); + assert(game.state === 'SETTLE' && game.run === null, 'H1: 链播完仍安居 SETTLE(run 已清、meta 保留)'); + // 死窗后(>800ms)设计出口照常:tap → TITLE + await tick(2000); + g(game, 'tap'); + await tick(1500); + assert(game.state === 'TITLE' && !game.run, 'H1: 死窗后 tap 正常离开结算 → TITLE(设计出口不受死窗影响)'); + game.stop(); +} + // ================================ 场景 7:菜单暂停/恢复(E4,§2.4/§12.8) ================================ async function scenario7({ game, audio }) { @@ -2039,6 +2090,7 @@ try { await scenario7(s6); await scenario6b(); await scenario6c(); + await scenario6d(); // H1:死亡瞬间在途 tap 不截杀结算链 await scenario8(); await scenario9(); await scenario10();