round2: 看门狗礼仪F1(不重播/单句升级/idle-skip) + 分档归因F2 + 交接count-in/answer F3 + 教学看表现F4-F5 + 警报去叠F6 + 回绕F7 + 开场瘦身F8 + 10句新语音 + 测试全绿(6playtest零改动+guide52断言+资产终检)
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+38
-18
@@ -31,9 +31,10 @@
|
||||
* 语音 id 权威清单:docs/design/04-space-voice-ids.md(52 句双性别);
|
||||
* 旧 num 拼接件、n_ 前缀件、intro1-4/hit1/hit2/gameover/restart 全部弃用。
|
||||
*
|
||||
* 向导层(research/03-schemes/schemes.md §2):对局第 1 次受击(保护期外)追加
|
||||
* 指令式 gd_hit_hint(归因句 hit_<dir> 之外的"该怎么拨");结算链换
|
||||
* _speakGuarded(资产缺失不卡结算 BGM);长按快进对局期豁免
|
||||
* 向导层(docs/design/14-guide-layer.md + round2 synthesis F2.2/F6):对局第 1 次
|
||||
* 受击(保护期外)在归因链播完后补指令式 gd_hit_hint、第 2 次补 gd_last_life
|
||||
* (idle-only:挂在语音空闲点);警报音效 600ms 去重、danger 震 ≥400ms 去抖;
|
||||
* 结算链换 _speakGuarded(资产缺失不卡结算 BGM);对局期长按快进豁免
|
||||
* (setRateBoostEnabled);状态签名 earcon 与状态 BGM 同点。
|
||||
*/
|
||||
import { Game } from '../engine/game.js';
|
||||
@@ -120,6 +121,10 @@ export class SpaceGame extends Game {
|
||||
this.shieldIntroDone = false;
|
||||
this.featureKind = null;
|
||||
this._menuIdx = 0;
|
||||
// F6:警报去重 / danger 去抖时间戳(soft/loud 分档 + danger 全局)
|
||||
this._alarmSoftAt = -Infinity;
|
||||
this._alarmLoudAt = -Infinity;
|
||||
this._dangerAt = -Infinity;
|
||||
}
|
||||
|
||||
// ---------------- 生命周期 ----------------
|
||||
@@ -449,22 +454,31 @@ export class SpaceGame extends Game {
|
||||
this.obstacles.add(ob);
|
||||
|
||||
this.audio.play(APPROACH_SFX[dir]);
|
||||
// 警报两档渐强,DANGER 震动同步叠加
|
||||
// 警报两档渐强,DANGER 震动同步叠加。
|
||||
// F6(round2):警报音效同 600ms 窗口去重(双陨石同时逼近只起一波警报,
|
||||
// 按 soft/loud 各自去重,单颗的两段渐强不受影响);danger 震 ≥400ms 去抖。
|
||||
this._after(impactAt - ALARM_LEAD_MS - now, () => {
|
||||
if (this.obstacles.has(ob)) {
|
||||
this.audio.play('sfx/alarm', { volume: 0.5 });
|
||||
vibrate('danger');
|
||||
}
|
||||
if (this.obstacles.has(ob)) this._alarm(0.5);
|
||||
});
|
||||
this._after(impactAt - ALARM_LEAD_MS / 2 - now, () => {
|
||||
if (this.obstacles.has(ob)) {
|
||||
this.audio.play('sfx/alarm', { volume: 1 });
|
||||
vibrate('danger');
|
||||
}
|
||||
if (this.obstacles.has(ob)) this._alarm(1);
|
||||
});
|
||||
this._after(impactAt - now, () => this._resolve(ob));
|
||||
}
|
||||
|
||||
/** 一段警报:同档 600ms 内只起一波(F6);danger 震全局限 ≥400ms 间隔。 */
|
||||
_alarm(volume) {
|
||||
const now = performance.now();
|
||||
const key = volume < 1 ? '_alarmSoftAt' : '_alarmLoudAt';
|
||||
if (now - this[key] < 600) return;
|
||||
this[key] = now;
|
||||
if (now - this._dangerAt >= 400) {
|
||||
this._dangerAt = now;
|
||||
vibrate('danger');
|
||||
}
|
||||
this.audio.play('sfx/alarm', { volume });
|
||||
}
|
||||
|
||||
/** 撞击点判定:加速增益 > 同向滑动 > 护盾 > 保护期训练护盾 > 撞击。 */
|
||||
_resolve(ob) {
|
||||
if (!this.obstacles.has(ob) || this.phase !== 'playing') return;
|
||||
@@ -508,12 +522,18 @@ export class SpaceGame extends Game {
|
||||
} else {
|
||||
const ids = [`voice/hit_${ob.dir}`];
|
||||
if (this.hits === 2) ids.push('voice/hit_last');
|
||||
this._speak(ids);
|
||||
// 向导层(schemes §2):对局第 1 次受击追加指令式提示(hit_<dir> 已归因方位,
|
||||
// 这句补"该怎么拨";已震 hit 免重复 error 震;onceKey 同局一次)
|
||||
if (this.hits === 1) {
|
||||
this.guide.error({ voice: 'gd_hit_hint', onceKey: 'space_hit_hint', skipHaptic: true });
|
||||
}
|
||||
// 向导层(round2 F1.4/F2.2):受击提示挂在归因链播完后的空闲点——
|
||||
// idle-only 语义下若插在链头会被判为「对白在播」整段跳过。
|
||||
// 第 1 次受击补指令式 gd_hit_hint(hit_<dir> 已归因方位,这句补"该怎么拨",
|
||||
// onceKey 同局一次);第 2 次受击播 gd_last_life(F2.2:生存经济透明,
|
||||
// 与游戏层 hit_last「船体只剩最后一次」互补"撑住"的口径),已震 hit 免重复。
|
||||
this._speak(ids, () => {
|
||||
if (this.hits === 1) {
|
||||
this.guide.error({ voice: 'gd_hit_hint', onceKey: 'space_hit_hint', skipHaptic: true });
|
||||
} else if (this.hits === 2) {
|
||||
this.guide.error({ voice: 'gd_last_life', skipHaptic: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user