105 lines
4.8 KiB
JavaScript
105 lines
4.8 KiB
JavaScript
/**
|
||
* 《声命线》新手会话剧本:像真人一样等语音播完再动、偶尔走神(触发看门狗,
|
||
* 听到轻响后 5 秒内回应——round2 修正剧本失真:round1 的「干晾 85 秒」是剧本
|
||
* 问题不是游戏问题)、在非抉择节点试一次无效的左滑(触发向导纠错)、中途演示
|
||
* 一次语速 2.2 切换(F9:设置下一句即时生效,转写状态行可见语速档)、菜单逛
|
||
* 一圈再出来、抉择点一律选左,一路打到结局。
|
||
*/
|
||
export function describe(game) {
|
||
if (game.mode === 'MENU') return '菜单里:左滑重开、右滑或单击返回剧情、上滑听帮助';
|
||
if (game.awaitingRestart) return '结局播完了。单击屏幕,可以回到故事开始的地方';
|
||
if (game.choiceArmed) return `「请选择」念完了(节点 ${game.nodeId}):左滑或右滑,做出你的决定`;
|
||
if (game.voicePlaying) return '旁白正在播。单击跳过本句,双击重听整个节点';
|
||
return `剧情等着你(节点 ${game.nodeId}):单击继续,双击重听`;
|
||
}
|
||
|
||
export async function session(ctx, mock) {
|
||
const { game } = ctx;
|
||
let invalidTried = false;
|
||
let menuDone = false;
|
||
let speedDemoDone = false;
|
||
let speedBackDone = false;
|
||
|
||
// 开场:gd_brief 先给操作口令(round2 F8),n00 全听完,再愣一会儿——看门狗 25s 才说话
|
||
await ctx.waitCond(() => !game.voicePlaying); // 只等链播完(不空烧看门狗窗口)
|
||
ctx.status('开场口令和旁白播完了,屏幕黑着,走神想剧情去了');
|
||
ctx.silence(25, '在入戏,不是迷路(round2:剧情类等待默认低频)');
|
||
await ctx.waitVirtual(25600); // 触发看门狗:nudge + gd_continue
|
||
ctx.status('听到一声轻响和「轻触屏幕,继续」,马上点一下');
|
||
await ctx.gesture('tap'); // 听到提示 ≤5s 内反应
|
||
|
||
for (let i = 0; i < 60 && !game.awaitingRestart && mock.now < 20 * 60 * 1000; i++) {
|
||
// 只等语音链播完(真人不会抢话)。不能裸 waitIdle——会把等待态看门狗的两级
|
||
// 提醒整段空烧完才动手(round1 剧本失真根源),链一停就该轮到玩家了
|
||
await ctx.waitCond(() => !game.voicePlaying);
|
||
|
||
if (game.awaitingRestart) break;
|
||
|
||
// 中途进一次菜单,逛一圈再出来
|
||
if (!menuDone && game.nodeId === 'n04') {
|
||
menuDone = true;
|
||
ctx.note('想看看还有什么可按的,下滑呼出菜单');
|
||
await ctx.gesture('down');
|
||
await ctx.waitIdle();
|
||
ctx.status('菜单里');
|
||
await ctx.gesture('up'); // 听帮助 + 进度
|
||
await ctx.waitIdle();
|
||
await ctx.gesture('doubletap'); // 重听菜单项
|
||
await ctx.waitIdle();
|
||
ctx.note('听懂了,先回去继续剧情');
|
||
await ctx.gesture('tap'); // 关菜单(回剧情并重播当前节点)
|
||
continue;
|
||
}
|
||
|
||
// F9:演示一次语速切换——2.2 档对下一句即时生效,听两段再调回 1.0
|
||
if (!speedDemoDone && game.nodeId === 'n10') {
|
||
speedDemoDone = true;
|
||
ctx.note('旁白有点慢,去设置把语速调到 2.2(下一句生效)');
|
||
ctx.settings.set({ speed: 2.2 });
|
||
if (game.choiceArmed) {
|
||
ctx.note('选了左边(循声而去)——后面几步都是 2.2 档');
|
||
await ctx.gesture('left'); // n10 抉择 → n11,2.2 倍速播
|
||
} else {
|
||
await ctx.gesture('tap');
|
||
}
|
||
await ctx.waitIdle();
|
||
ctx.status('语速 2.2 听着赶趟');
|
||
continue;
|
||
}
|
||
if (speedDemoDone && !speedBackDone && game.nodeId === 'n12') {
|
||
speedBackDone = true;
|
||
ctx.note('听了两段,把语速调回 1.0');
|
||
ctx.settings.set({ speed: 1.0 });
|
||
}
|
||
|
||
if (game.choiceArmed) {
|
||
ctx.status('抉择点,提示音念完了');
|
||
if (mock.now % 2 < 1) await ctx.waitVirtual(3000); // 偶尔犹豫一下
|
||
ctx.note('选了左边');
|
||
await ctx.gesture('left'); // 抉择点一律选左
|
||
continue;
|
||
}
|
||
|
||
// W1:叙事节点播完,静默等待
|
||
ctx.status('剧情停下来了,在等我');
|
||
if (!invalidTried) {
|
||
invalidTried = true;
|
||
ctx.note('试着左滑了一下——这里用不上,向导纠正了我');
|
||
await ctx.gesture('left');
|
||
await ctx.waitVirtual(2000); // 让纠错句播完(不空烧看门狗窗口)
|
||
} else if (i % 4 === 2) {
|
||
ctx.silence(25, '又走神想剧情去了');
|
||
await ctx.waitVirtual(25600); // 偶尔挂机,触发看门狗轻提醒(25s 才响,不轰炸)
|
||
ctx.status('又听到那声轻响,5 秒内想起该点屏幕了');
|
||
}
|
||
await ctx.gesture('tap');
|
||
}
|
||
|
||
await ctx.waitCond(() => !game.voicePlaying);
|
||
ctx.status('结局');
|
||
ctx.note('听完结局,单击重开一次看看别的走向');
|
||
await ctx.gesture('tap'); // 结算态单击重开
|
||
await ctx.waitCond(() => !game.voicePlaying);
|
||
ctx.status('回到开头,会话结束');
|
||
}
|