Files
SoundGame/web/harness/sessions/rpg.mjs
T
SoundGame Dev 79dffac01e guide层v1: 全局向导交互层落地(状态签名earcon/turn cue/静默看门狗/纠错) + 6游戏接线 + 8句向导语音 + 测试全绿
- engine/guide.js 运行时合成8个签名earcon(方法论L2硬参数),off档零定时器,插播queue不顶对白
- settings: guide三档(full/standard/off默认full) + 语速2.2视障档;audio: isVoicePlaying()/setRateBoostEnabled() 清两个已知问题
- 6游戏按schemes§2接线: storyline看门狗+无效手势纠错、simon免扣重听看门狗、escape turn cue+提前卡关提示、rhythm gd_answer/gd_miss+结算看门狗+快进豁免、space首撞指令提示+结算看门狗、rpg格挡纠错+60s催促+状态签名
- 大厅设置面板+向导文档(14-guide-layer.md)+00-global-ux §6;R5台词表补4条一致性
- 试玩转写含向导事件;6 playtest(guide=off断言零改动)+guide.test(34断言)+资产终检全绿
2026-08-30 22:46:05 +08:00

128 lines
5.3 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.
/**
* 《剑与声》新手会话剧本:听完序章进村 → 村里把选项听一圈 → 出村去小路
* (顺路调查一次捡钱)→ 岔路口转去溪流触发教学战 → 完整打一场
* (教学四步:识招→攻击→格挡→防御,再自由击杀),战后收工。
*
* 种子 RNG:让教学狼首击必不掉(HP 6、伤害恒 5),保证四步教学完整走完。
*/
Math.random = () => 0.4;
export function describe(game) {
const SCENE_NAME = {
village: '雾口村', path: '林间小路', fork: '岔路口', chest: '枯树洞', stream: '溪流',
hunter: '猎人小屋', deepwood: '密林深处', gate: '黑风寨门前', boss1: '聚义厅',
};
const BAND = { high: '八成以上', mid: '五成上下', low: '三成以下' };
if (game.phase === 'battle' && game._battle) {
const b = game._battle;
if (b.state === 'PLAYER_SELECT' && b.enemy) {
return `战斗中:你的血量${BAND[game._hpBand(game.player.hp, game.maxHp)]},`
+ `敌方血量${BAND[game._hpBand(b.enemy.hp, b.enemy.maxHp)]},等你选招式(左右滑切换,单击出手,下滑逃跑)`;
}
if (b.state === 'ENEMY_TELEGRAPH') return '战斗中:敌方前摇声响起了——双击,格挡!';
return '战斗播报中,先听着';
}
switch (game.phase) {
case 'ask': return '检测到旅程记录:单击继续,右滑开新档';
case 'intro': return '序章旁白在播,讲我怎么用耳朵走路';
case 'explore': return `【${SCENE_NAME[game.scene] ?? game.scene}】:左右滑切选项,单击确认,双击重听,上滑查状态,下滑菜单`;
case 'shop': return '商店里:左右滑看货,单击买,下滑离开';
case 'menu': return '菜单里:左右滑切选项,单击确认';
default: return '……';
}
}
export async function session(ctx, mock) {
const { game } = ctx;
const CMDS = ['attack', 'skill', 'defend', 'item'];
// 序章听完 → 进村
await ctx.waitCond(() => game.phase === 'explore');
ctx.status('进村了,先听场景描述和选项');
// 村里(选项:去小路/商店/休息/听状态):把选项听一圈再对准「去小路」出发
for (let i = 0; i < 3; i++) {
await ctx.gesture('right');
await ctx.waitVirtual(1800); // 听选项名
}
ctx.status('选项听了一圈,往林间小路去');
await ctx.waitIdle(); // 选项名播完(无 interval,可安全等待空闲)
await ctx.gesture('right'); // 转回首个选项「去小路」
await ctx.waitVirtual(1500);
await ctx.gesture('tap'); // → path
// 小路(去岔路口/调查/返回):先调查一次捡钱,再左滑回「去岔路口」
await ctx.waitCond(() => game.phase === 'explore' && game.scene === 'path');
ctx.status('林间小路');
await ctx.gesture('right'); // 调查四周
await ctx.waitVirtual(1800);
ctx.note('翻找了一阵,捡到一点钱');
await ctx.gesture('tap');
await ctx.waitVirtual(2400);
await ctx.gesture('left'); // 回「去岔路口」
await ctx.waitVirtual(1500);
await ctx.gesture('tap'); // → fork
// 岔路口(去枯树洞/去溪流/返回):右滑一次到「去溪流」
await ctx.waitCond(() => game.phase === 'explore' && game.scene === 'fork');
ctx.status('岔路口,水声在东边');
await ctx.gesture('right');
await ctx.waitVirtual(1500);
await ctx.gesture('tap'); // go_stream → 溪流 → 触发教学战
// 教学战:四步(识招 → 攻击 → 格挡 → 防御)+ 自由击杀
await ctx.waitCond(() => game.phase === 'battle' && game._battle?.state === 'PLAYER_SELECT');
ctx.status('遭遇教学狼,战斗开始');
const selectCmd = async (cmd) => {
const b = game._battle;
while (b && b.cmdIdx !== CMDS.indexOf(cmd)) {
await ctx.gesture('right');
await ctx.waitVirtual(1300); // 听指令名
}
};
// T0:左右滑各切一次(听到指令名就算过关)
await ctx.gesture('right');
await ctx.waitVirtual(1300);
await ctx.gesture('right');
await ctx.waitVirtual(1300);
ctx.status('招式名听熟了');
// T1:攻击一次
await ctx.waitCond(() => game._battle?.state === 'PLAYER_SELECT');
await selectCmd('attack');
ctx.note('单击出手,攻击');
await ctx.gesture('tap');
// T2:格挡(前摇窗口内双击)
await ctx.waitCond(() => game._battle?.state === 'ENEMY_TELEGRAPH');
ctx.status('前摇声响起了');
ctx.note('双击格挡');
await ctx.gesture('doubletap');
// T3:防御一次
await ctx.waitCond(() => game._battle?.state === 'PLAYER_SELECT');
await selectCmd('defend');
ctx.note('这回合先防御,减半伤害');
await ctx.gesture('tap');
// 自由阶段:攻击到狼死(中间照常听前摇、双击格挡)
for (let i = 0; i < 8 && game.phase === 'battle'; i++) {
await ctx.waitCond(() => game._battle?.state === 'PLAYER_SELECT'
|| game._battle?.state === 'ENEMY_TELEGRAPH' || game.phase !== 'battle');
if (game.phase !== 'battle') break;
if (game._battle.state === 'ENEMY_TELEGRAPH') {
ctx.note('双击格挡');
await ctx.gesture('doubletap');
continue;
}
await selectCmd('attack');
ctx.note('单击,攻击');
await ctx.gesture('tap');
}
await ctx.waitCond(() => game.phase === 'explore');
ctx.status('狼打完了,胜利播报完回到溪流边。一场完整战斗到此收工');
}