/** * novice.js — 新手手势模式与解锁阶梯(26 号文档 §4.3,P0 第 1 项 + P3 第 11 项)。 * * 新手手势表(全部游戏共用,原则 6): * | 手势 | 唯一含义 | 新手期(阶段 0) | * |------------|------------------------|---------------------------------| * | tap | 继续 / 确定 / 听一下 | 开放 | * | left/right | 选择 / 移动 | 开放 | * | up | 我在哪、我该干嘛 | 阶段 1 解锁(完成第一个小目标) | * | down | 菜单 | 阶段 2 解锁(完成第一局) | * | doubletap | 重听 | 阶段 3 解锁(累计 3 局);未解锁按单击处理 | * | longpress | 帮助(四向玩法类游戏) | 仅「滑动是玩法」的游戏开放(§4.3 裁定) | * * 解锁进度跨局持久(localStorage),全局共享(手势表全项目一致,学会一次就算会)。 * 游戏在对应时机调 onGoal() / onGameDone(),返回新解锁的手势(null=无新解锁), * 随后按原则 11 教学:先说一句(teachLine),马上用一次。 */ const STORAGE_KEY = 'soundgame.gestures'; /** 解锁教学句 id(每游戏目录各存一份同文案资产,§4.3 阶梯表)。 */ const TEACH_IDS = { up: 'sys_unlock_up', // 「想知道自己在哪,就向上滑。」 down: 'sys_unlock_down', // 「向下滑,可以打开菜单。」 doubletap: 'sys_unlock_dt', // 「没听清,就双击,再听一遍。」 }; const UNLOCK_AT = { 1: 'up', 2: 'down', 3: 'doubletap' }; export class GestureLadder { constructor(key = STORAGE_KEY) { this.key = key; } _load() { try { const raw = localStorage.getItem(this.key); if (!raw) return { stage: 0, games: 0 }; const o = JSON.parse(raw); return { stage: Math.max(0, Math.min(3, Number(o.stage) || 0)), games: Math.max(0, Number(o.games) || 0), }; } catch (_) { return { stage: 0, games: 0 }; } } _save(o) { try { localStorage.setItem(this.key, JSON.stringify(o)); } catch (_) { /* 隐私模式 */ } } /** 当前阶段 0..3。 */ stage() { return this._load().stage; } /** 手势是否已解锁(tap/left/right 永远开放;longpress 由游戏按玩法裁定)。 */ has(g) { if (g === 'tap' || g === 'left' || g === 'right') return true; if (g === 'up') return this.stage() >= 1; if (g === 'down') return this.stage() >= 2; if (g === 'doubletap') return this.stage() >= 3; return false; // longpress 等:走游戏自己的核心玩法裁定 } /** 完成第一个小目标 → 阶段 1。返回新解锁手势或 null。 */ onGoal() { return this._unlock(1); } /** 完成一局 → games+1;第 1 局解锁阶段 2,第 3 局解锁阶段 3。 */ onGameDone() { const o = this._load(); o.games += 1; this._save(o); let got = null; if (o.games >= 1) got = this._unlock(2) || got; if (o.games >= 3) got = this._unlock(3) || got; return got; } _unlock(stage) { const o = this._load(); if (o.stage >= stage) return null; o.stage = stage; this._save(o); return UNLOCK_AT[stage]; } /** 老玩家在大厅一键全部解锁。 */ unlockAll() { const o = this._load(); o.stage = 3; this._save(o); } /** 解锁教学句 voice id。 */ teachLine(g) { return TEACH_IDS[g] || null; } } export default GestureLadder;