round2: 看门狗礼仪F1(不重播/单句升级/idle-skip) + 分档归因F2 + 交接count-in/answer F3 + 教学看表现F4-F5 + 警报去叠F6 + 回绕F7 + 开场瘦身F8 + 10句新语音 + 测试全绿(6playtest零改动+guide52断言+资产终检)
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
/**
|
||||
* 《盲航》新手会话剧本:首局完整演练 D1-D4(第二颗故意超时,听当场重教)→
|
||||
* 正式对局躲过保护期(20s/45s 的加速/护盾引入都照做)→ 保护期结束后故意漏一颗
|
||||
* (触发归因句 + 向导「同向滑动就能拨开」指令)→ 再正常躲几颗收工。
|
||||
* 正式对局躲过保护期(20s/45s 的加速/护盾引入都照做)→ 保护期结束后故意漏两颗
|
||||
* (第 1 次听归因+向导「同向滑动就能拨开」,第 2 次听 gd_last_life「最后一次机会」
|
||||
* ——round2 F2.2:受击经济透明)→ 之后全部躲开,保护期结束后再撑 20 秒以上收工
|
||||
* (不再像 round1 那样在保护期后 8 秒就撤,也没有第三次被撞)。
|
||||
*/
|
||||
export function describe(game) {
|
||||
const DIR = { left: '左边', right: '右边', up: '上面', down: '下面' };
|
||||
@@ -45,46 +47,55 @@ export async function session(ctx, mock) {
|
||||
}
|
||||
ctx.status('演练做完了,要出发了');
|
||||
|
||||
// 正式对局:保护期 60s + 结束后再躲一小段就收工(不再模拟,避免挂机撞毁)。
|
||||
// 每颗陨石生成(呼啸声起)就立即同向拨开——比盯着 1.5s 判定窗口更稳。
|
||||
// 正式对局:保护期 60s + 结束后再撑 20s 以上收工。
|
||||
// 每颗陨石生成(呼啸声起)就在判定窗口将开时同向拨开——比盯着 1.5s 判定窗口更稳。
|
||||
//(刷怪链是自驱动的定时器链——禁止裸 waitIdle,等待一律挂条件)
|
||||
await ctx.waitCond(() => game.phase === 'playing');
|
||||
ctx.status('出发。引擎声起来了,保护期里先练手');
|
||||
const dodged = new Set();
|
||||
let victim = null; // 保护期结束后故意不躲的那颗
|
||||
let eaten = false;
|
||||
const victims = []; // 保护期结束后故意不躲的(前两颗:听归因 + gd_last_life)
|
||||
let eaten = 0;
|
||||
while ((game.phase === 'playing' || game.phase === 'feature')
|
||||
&& performance.now() < game.protectionUntil + 8000) {
|
||||
&& performance.now() < game.protectionUntil + 21000) {
|
||||
if (game.phase === 'feature') {
|
||||
ctx.status('游戏停下来教新功能');
|
||||
await ctx.waitIdle(); // 引入句听完(feature 态无刷怪链,可安全等待空闲)
|
||||
// 引入句听完就照做(不裸 waitIdle——feature 态之后的刷怪链是自驱动定时器,
|
||||
// 裸等会把整局对局连本带利烧穿到坠毁)
|
||||
await ctx.waitCond(() => !ctx.voiceBusy());
|
||||
ctx.note(game.featureKind === 'boost' ? '照它说的单击加速' : '照它说的双击开护盾');
|
||||
await ctx.gesture(game.featureKind === 'boost' ? 'tap' : 'doubletap');
|
||||
continue;
|
||||
}
|
||||
if (victim && !game.obstacles.has(victim)) {
|
||||
ctx.note('被撞了。先听到方位归因,又听到「同向滑动就能拨开」');
|
||||
victim = null;
|
||||
// 被撞的那颗消失 → 归因链听完了,继续
|
||||
const eatenIdx = victims.findIndex((v) => !game.obstacles.has(v));
|
||||
if (eatenIdx >= 0) {
|
||||
victims.splice(eatenIdx, 1);
|
||||
ctx.note(eaten === 2 && victims.length === 0
|
||||
? '第二次被撞:先听方位归因,又听到「这是最后一次机会,撑住」——这就是全部家底了'
|
||||
: '被撞了。先听到方位归因,又听到「同向滑动就能拨开」');
|
||||
continue;
|
||||
}
|
||||
// 只在判定窗口将开未开时出手(警报第一响 = 撞击前 1s,窗口内)
|
||||
const target = [...game.obstacles].find((o) => !dodged.has(o)
|
||||
&& o.impactAt - performance.now() < 1300);
|
||||
if (target) {
|
||||
if (!eaten && performance.now() >= game.protectionUntil) {
|
||||
eaten = true;
|
||||
victim = target;
|
||||
if (eaten < 2 && performance.now() >= game.protectionUntil) {
|
||||
eaten++;
|
||||
victims.push(target);
|
||||
dodged.add(target); // 标记 = 故意不躲
|
||||
ctx.status('保护期结束了,这颗我故意没动');
|
||||
ctx.status(eaten === 1
|
||||
? '保护期结束了,这颗我故意没动'
|
||||
: '又有一颗,也故意不躲——想听听第二次被撞会说什么');
|
||||
continue;
|
||||
}
|
||||
dodged.add(target);
|
||||
await ctx.gesture(target.dir);
|
||||
continue;
|
||||
}
|
||||
await ctx.waitCond(() => (game.phase !== 'playing' && game.phase !== 'feature')
|
||||
|| (victim && !game.obstacles.has(victim))
|
||||
await ctx.waitCond(() => game.phase !== 'playing'
|
||||
|| victims.some((v) => !game.obstacles.has(v))
|
||||
|| [...game.obstacles].some((o) => !dodged.has(o) && o.impactAt - performance.now() < 1300));
|
||||
}
|
||||
ctx.status('躲过保护期、挨过一次撞,心跳有点快,先到这');
|
||||
const aliveSec = Math.round((performance.now() - game.runStart) / 1000);
|
||||
ctx.status(`保护期结束后撑了 ${aliveSec - 60} 秒、挨了两下还活着(警报也只起一波了),先到这`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user