130 lines
5.8 KiB
JavaScript
130 lines
5.8 KiB
JavaScript
/**
|
||
* 《剑与声》新手会话剧本:听完序章进村 → 村里把选项听一圈 → 出村去小路
|
||
* (顺路调查一次捡钱)→ 岔路口转去溪流触发教学战 → 完整打一场
|
||
* (教学四步:识招→攻击→格挡→防御,再自由击杀),战后收工。
|
||
*
|
||
* 种子 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('进村了,先听场景描述和选项');
|
||
|
||
// 村里(选项:去小路/商店/休息/听状态):把选项听一圈,回绕到首项再出发。
|
||
// 确认(tap)前必须等选项名念完——voice 播放中单击是「跳过本句」,会把手势吃掉
|
||
for (let i = 0; i < 3; i++) {
|
||
await ctx.gesture('right');
|
||
await ctx.waitVirtual(1800); // 听选项名
|
||
}
|
||
ctx.status('选项听了一圈');
|
||
await ctx.gesture('right'); // 从末项回绕到首个选项——听到「回到第一个」(round2 F7)
|
||
ctx.note('滑到头再滑就兜回来了,有人提醒「回到第一个」,不用自己数');
|
||
await ctx.waitCond(() => !ctx.voiceBusy()); // 选项名 + 回绕提示念完
|
||
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.waitCond(() => !ctx.voiceBusy());
|
||
await ctx.gesture('tap');
|
||
await ctx.waitCond(() => !ctx.voiceBusy()); // 捡钱播报念完
|
||
await ctx.gesture('left'); // 回「去岔路口」
|
||
await ctx.waitCond(() => !ctx.voiceBusy());
|
||
await ctx.gesture('tap'); // → fork
|
||
|
||
// 岔路口(去枯树洞/去溪流/返回):右滑一次到「去溪流」
|
||
await ctx.waitCond(() => game.phase === 'explore' && game.scene === 'fork');
|
||
ctx.status('岔路口,水声在东边');
|
||
await ctx.gesture('right');
|
||
await ctx.waitCond(() => !ctx.voiceBusy());
|
||
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:格挡(前摇窗口内双击)——挡住会先听到「挡得漂亮」(round2 F4)再进防御教学
|
||
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('狼打完了,胜利播报完回到溪流边。一场完整战斗到此收工');
|
||
}
|