Files
SoundGame/web/harness/sessions/escape.mjs
T
xsl 963dfa1cc8 fix(escape): 暗室状态引导方案落地(25号策划文档 B1-B8 → F1-F9 实施)
策划轮:25-escape-map-flow.md as-built地图/玩家流程/信息账本+盲区清单(S1-S7)+方案B1-B8
实施(玩法零改动,只加状态引导层):
- F1 撞墙方位句「左边是墙」(wall_dir_*×4,同向10s限频)
- F2 目标句并入方位「铁柜,在右面的房间」(gd_*_dir×3)
- F3 无进展150s活跃计时器+gd_prog_1..3进度重述(只重述不泄题,专治活跃迷路玩家——修卡关计时从「无进展」退化为「无操作」的既定偏差)
- F4 双击兜底直通L2降级L1(不再白送答案)
- F5 空房首访描述(4空房可区分)
- F7 busy窗口手势缓存补判(原静默吞手势)
- F8 结算链单击守卫(time_band/again完整到达)
- F9 跳过引导补播目标句
- F6 空房方位特征句按25号裁定留档后续轮
新增语音10句×2性别(flock串行QC无离群,ASR逐字正确)
验证:escape playtest 235/235(+85新断言)+ guide 132/132 + 终检全绿 + 14游戏回归零回退
live复跑10.4分钟通关:零截断,S1-S7盲区逐条核销
档案 research/escape-state/round1-2;01号文档追加修订记录R1
2026-09-15 00:37:41 +08:00

97 lines
5.8 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.
/**
* 《暗室》新手会话剧本:老玩家二周目不算——这里清空过进度走首局引导(G0-G7),
* 每步听清再做;进 PLAY 后先连撞两次墙(round2 B1:wall 后跟陈述式方位句
* 「上边是墙。」,同向 10s 限频),经空房首访描述(B5a)找到铁柜房开柜拿保险丝、
* 照 round2 B2 的"目标名+方位"一体句跨四个房间修电箱、再摸到大门收工。
*/
export function describe(game) {
switch (game.phase) {
case 'REOPEN': return '有人问我回不回来看过引导:单击直接开始,上滑重听引导';
case 'GUIDE': return game._guideBusy ? '引导在说话,照它说的做就行' : `引导第 ${game.guideStep + 1}/8 步,等我做手势`;
case 'PLAY': return `探索中,我在 (${game.x},${game.y})。四向滑挪步,单击摸一遍房间,双击用物品`;
case 'ENDING': return '逃脱了,结算播报中。播完单击再来,下滑开菜单';
case 'MENU': return '菜单里:单击切选项,双击确认,下滑关闭';
default: return '……';
}
}
export async function session(ctx, mock) {
const { game } = ctx;
// 首局:交互式引导 G0-G7(n = 引导步号 guideStep)。
// 注意:卡关轮询 interval 常驻,虚拟时钟永不"空闲"——等待一律用有界/带条件等待。
ctx.status('黑屏,只有引子的声音,先听完');
await ctx.waitCond(() => game.phase === 'GUIDE' && !game._guideBusy); // g0_intro 播完
const steps = [
{ n: 0, do: async () => { ctx.note('照引导单击了一下'); await ctx.gesture('tap'); } },
{ n: 1, do: async () => { ctx.note('随便滑了一步,有脚步声'); await ctx.gesture('up'); } },
{
n: 2,
do: async () => {
await ctx.gesture('left');
await ctx.waitVirtual(1600);
ctx.note('又滑一步,又撞墙了——墙的方位听出来了');
await ctx.gesture('right');
},
},
{ n: 3, do: async () => { ctx.note('这回往左,走通了一步'); await ctx.gesture('left'); } },
{ n: 4, do: async () => { ctx.note('往下滑,进了储物间'); await ctx.gesture('down'); } },
{ n: 5, do: async () => { ctx.note('单击摸一遍房间,摸到钥匙了'); await ctx.gesture('tap'); } },
{ n: 6, do: async () => { ctx.note('双击看了看背包'); await ctx.gesture('doubletap'); } },
];
for (const s of steps) {
await ctx.waitCond(() => game.phase === 'GUIDE' && game.guideStep === s.n && !game._guideBusy);
ctx.status(`引导第 ${s.n + 1}/8 步`);
await s.do();
await ctx.waitVirtual(800); // 缓一缓再继续(推进链由语音 onComplete 驱动)
}
// PLAY:自由探索。引导结束时我站在储物间 (0,2),手里已有钥匙
await ctx.waitCond(() => game.phase === 'PLAY');
ctx.status('引导完毕,正式开始。我在储物间');
const move = async (g, why) => {
await ctx.gesture(g);
await ctx.waitVirtual(1600); // 听脚步/环境音/描述
if (why) ctx.note(why);
};
// 先往北乱撞探探边界:两步走到 (0,0)——(0,1) 首访会播 empty_2(B5a 空房听觉锚),
// 再连撞两次北墙——wall 后各跟一句陈述式方位「上边是墙。」(B1,同向 10s 限频)
ctx.silence(3, '辨了辨方向');
await move('up', '路过一间空房,它自己开口了:摸到的只有冰冷粗糙的墙壁');
await move('up', '电流声——电箱房到了');
await move('up', '北边是墙,撞了一下——「那边是墙」后面跟了句「上边是墙。」,这面不通记住了');
await move('up', '又撞了一下——同方向 10 秒内不重复报方位,游戏不啰嗦');
await ctx.waitVirtual(5200); // 缓一缓(卡关轮询 5s tick 空转,活跃操作不触发提示)
// 去铁柜房:(0,0) → 右×2 = (2,0) → 下 = (2,1),双击用钥匙
// ((1,0)/(2,0) 首访各播 empty_1/empty_3——B5a:四间空房从此声学可区分)
await move('right', '往东走,一间空房自报家门:空空荡荡只有回声');
await move('right', '又一间空房:除了远处的水滴声什么都没有');
await move('down', '到了铁柜房,柜子锁着');
// 用钥匙 → unlock_cabinet + gd_next_fuse + gd_box_dir(round2 B2:目标名+方位
// 一体句,从铁柜房 (2,1) 看电箱房 (0,0) 在西 = 「电箱,在左面的房间。」)
await ctx.gesture('doubletap');
await ctx.waitVirtual(5600);
ctx.status('柜子开了。「保险丝到手。接下来,去找电箱。」后面跟的是「电箱,在左面的房间。」——目标名和方位一句话说全了');
// round4 H1 剧本:照方位句跨房间去电箱房——(2,1) → 左×2 = (0,1) → 上 = (0,0)
await move('left', '照方位句往左(西)摸');
await move('left');
await move('up', '电流声越来越清晰——电箱房到了');
// 修电箱 → fix_box + gd_gate_dir(round2 B2:从 (0,0) 看大门 (2,2) 在东 =
// 「大门,在右面的房间。」)
await ctx.gesture('doubletap');
await ctx.waitVirtual(5600);
ctx.status('电箱修好了。目标句后面照样是目标名+方位——「大门,在右面的房间。」');
// 照方位句多跨两个房间摸向大门:(0,0) → 右×2 = (2,0) → 下×2 = (2,2)
await move('right', '照方位句往右(东)摸');
await move('right');
await move('down');
await move('down', '脚步声停下来——面前这间就是大门');
// 完整弧线收尾:在大门双击用钥匙开门 → ENDING 结算链(逃脱播报+结算 BGM)
// (round4 终版修订:此前剧本在大门"收工",两位终评人设把"没演完"读成了"游戏哑结尾")
await ctx.gesture('doubletap');
await ctx.waitVirtual(6000);
ctx.status('钥匙插进锁孔,门开了——结算播报来了,这局是完整的赢');
}