round4: H1 escape目标带实时方位 + H2 simon轮次占用预告(根治手势两义) + H3 重听账目真实化(诊断=初始发放1次vs承诺2次) + H4 input看门狗5s + H5 归因真话(gd_miss_none) + 20语音 + guide120断言全绿

This commit is contained in:
SoundGame Dev
2026-08-31 02:51:41 +08:00
parent 6be3d29b78
commit 3d37d4656d
55 changed files with 822 additions and 215 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+16 -6
View File
@@ -35,12 +35,14 @@
* main 演奏阶段单击是演奏手势不跳(mid_song 短句按裁定不跳)。
* 长按快进豁免(12 文档 §2.2:main 演奏期):audio.setRateBoostEnabled(false)
* per-game 开关(引擎已支持),进 main 关、进菜单/结算/退出恢复。
* 向导层(docs/design/14-guide-layer.md + round2 synthesis F2.1/F3 + round3 G1~G3):
* 向导层(docs/design/14-guide-layer.md + round2 synthesis F2.1/F3 + round3 G1~G3
* + round4 H5):
* 每曲首次领奏→应答切换在谱面定时队列直接落 gd_answer(不走 guide queue,保证
* 窗口前听到)+ turn earcon;round3 G1 修正排程点:落在首个应答窗口**前整整
* 1 小节**(= 领奏小节头;round2 排在窗口起点本身,P4 实测窗口开了才听到),
* 菜单恢复对下一个未判定窗口重排同款标记;教学 Miss 首次即归因分档
* (gd_miss_early/late/wrong,挂纠正链尾空闲点);连续 2 Miss 播 gd_miss(queue
* (round4 H5 四档:无窗内尝试 gd_miss_none 优先、真出手再分 early/late/wrong,
* 挂纠正链尾空闲点;正片连 2 Miss 仍 gd_miss);连续 2 Miss 播 gd_miss(queue
* 插播,同曲一次);暂停断点续播前插 4 拍 count-in(round3 G3:底鼓 sfx 层可辨
* 音量 + full 档 gd_count 每局恢复限 1 次 + beat 震;round2 版因 push 后未重排
* _sched 被队头卡死从未按时响过;off 档保持即时续播);教学 DDA(round3 G2):
@@ -388,14 +390,22 @@ export class RhythmGame extends Game {
});
return;
}
// F2.1(round2):教学 Miss 首次即归因、按失败原因分档——方向错 gd_miss_wrong /
// 抢拍 gd_miss_early / 慢拍 gd_miss_late(无在窗内尝试=没跟上拍,归入慢拍)。
// F2.1(round2)+ H5(round4):教学 Miss 首次即归因、按失败原因分档。
// H5 归因真话律:应答窗口无任何尝试(窗口关闭时该音未判定——出窗的自由
// 演奏不算尝试)优先播 gd_miss_none「这拍没接」,早/晚/错分档只保留给
// 真出手的情况(P1 round3:没出手被报「慢了半拍」是归因说假话——"宁可粗
// 不可假";正片连 2 Miss 仍走 gd_miss,本档只改教学归因)。
// 向导句(full 档)挂在纠正链播完后的空闲点发:idle-only 语义下链中插播会被
// 跳过;之后 _tutLead 只排 sfx,不与归因句抢 voice 通道。
const noShow = this._cues.some((cue) => {
const a = this._tutAttempts.find((x) => x.cue === cue);
return !a || Math.abs(a.dt) > GOOD; // 窗内无尝试 = 该音没接
});
const wrong = this._tutAttempts.find((a) => !a.correct);
const offbeat = this._tutAttempts.find((a) => a.correct && Math.abs(a.dt) > GOOD);
const missGrade = wrong ? 'gd_miss_wrong'
: offbeat ? (offbeat.dt < 0 ? 'gd_miss_early' : 'gd_miss_late') : 'gd_miss_late';
const missGrade = noShow ? 'gd_miss_none' // none 优先级最高
: wrong ? 'gd_miss_wrong'
: offbeat ? (offbeat.dt < 0 ? 'gd_miss_early' : 'gd_miss_late') : 'gd_miss_late';
// 纠正语音:手势错 > 过早 > 过晚 > 没出手(retry_listen)
let correction = 'voice/retry_listen';
if (wrong) correction = 'voice/retry_gesture';