Files
SoundGame/web/harness/sessions/simon.mjs
T

88 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 《音序回响》新手会话剧本:教学六音一步一步学(中间故意做错一次听纠错)、
* 收尾听完机会经济句(免费重听两次、用完扣机会);第 1 轮直接答对;第 2 轮开局
* 忘序列僵住 10 秒,听到向导「双击可以重新听」提示后 5 秒内照做——用掉一次免费
* 重听(round2:25s 升级已不再自动重播序列,坐着等答案重念的路子废了);第 3 轮
* 凭记忆答对,在**整轮边界**收工(round2 剧本修正:不在轮中退出——round1 转写
* 「第 4 轮报数后再没人说话」与「干晾 85 秒」都是剧本失真,不是游戏失真)。
*/
export function describe(game) {
const GN = { up: '上滑', down: '下滑', left: '左滑', right: '右滑', tap: '单击', doubletap: '双击' };
const GESTURES = ['up', 'down', 'left', 'right', 'tap', 'doubletap'];
switch (game.phase) {
case 'tutorial':
return game.teachAwait
? `教学:该做「${GN[GESTURES[game.teachIdx]]}」了`
: '教学旁白在播,先听着';
case 'announce': return `第 ${game.round} 轮报数呢,听好了`;
case 'playback': return `第 ${game.round} 轮序列演奏中(${game.sequence.length} 个音),记住顺序,先别动手`;
case 'input': return `第 ${game.round} 轮轮到你了:已对上 ${game.inputIdx}/${game.sequence.length},照原顺序做手势`;
case 'roundend': return '刚答完一轮,播报中';
case 'gameover': return `游戏结束,完成 ${game.round - 1} 轮。单击再来一局,下滑开菜单`;
case 'menu': return '菜单里:单击切选项,双击确认,上滑听规则';
default: return '开场,稍等一下';
}
}
export async function session(ctx, mock) {
const { game } = ctx;
const G = ['up', 'down', 'left', 'right', 'tap', 'doubletap'];
// 教学 T0-T6:教一个练一个
for (let i = 0; i < G.length; i++) {
await ctx.waitCond(() => game.phase === 'tutorial' && game.teachAwait);
ctx.status(`教学第 ${i + 1}/6 个音色`);
if (i === 2) {
ctx.note('把「左滑」记混了,先来了个右滑');
await ctx.gesture('right'); // 听纠错语音(t_wrong_left)
await ctx.waitCond(() => game.phase === 'tutorial' && game.teachAwait);
}
await ctx.gesture(G[i]); // 照指令做对
if (i === G.length - 1) {
// 收尾链(t7 两句 + 机会经济句)播完直达对局——一到 input 就接手,
// 不裸 waitIdle(会把 input 的看门狗窗口整段空烧掉,round1 剧本失真根源)
await ctx.waitCond(() => game.phase === 'playback' || game.phase === 'input'
|| game.phase === 'gameover');
} else {
await ctx.waitIdle();
}
}
ctx.status('教学讲完了,机会经济也交代清楚了:免费重听两次,用完要扣机会');
// 正式对局:完整打完第 3 轮,在轮边界收工
let relistenDone = false;
while (mock.now < 12 * 60 * 1000 && game.phase !== 'gameover') {
if (game.phase === 'input') {
// 第 2 轮先失忆僵住 10 秒:听向导「双击可以重新听一遍序列」,5 秒内照做。
//(期望输入恰为双击时先答这一下,下一步再演示重听——双击语境判定)
if (game.round === 2 && !relistenDone
&& game.inputIdx <= 1 && game.sequence[game.inputIdx] !== 'doubletap') {
relistenDone = true;
ctx.status('序列有点长,先僵一会儿');
ctx.silence(10, '想不起来序列了');
await ctx.waitVirtual(10600); // 10s 看门狗:nudge + gd_relisten
ctx.status('听到提示:需要的话,双击可以重新听一遍序列');
ctx.note('照提示双击,用掉一次免费重听');
await ctx.gesture('doubletap');
await ctx.waitCond(() => game.phase === 'playback' || game.phase === 'input');
continue;
}
if (game.round === 3 && game.inputIdx === game.sequence.length - 1) {
ctx.status('最后一轮最后一个音,打完这轮就收');
} else {
ctx.status('轮到我复现了');
}
await ctx.gesture(game.sequence[game.inputIdx]);
continue;
}
// 整轮边界收尾:第 3 轮答完(roundend),不等第 4 轮报数
if (game.phase === 'roundend' && game.round === 3) break;
// announce/playback/roundend:等阶段推进即可,不裸 waitIdle(防吞掉 input 窗口)
const ph = game.phase;
const r = game.round;
await ctx.waitCond(() => game.phase !== ph || game.round !== r);
}
ctx.status(`收工:完整打完 ${game.round} 轮,在轮与轮的边界停手(免费重听剩 ${game.chances} 次)`);
}