feat(new8): 批次1收口——radio/dungeon 双双标记可交付(R3 synthesis×2 + H1 热修 + 定向live复验PASS)
- 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 套件全绿
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user