이클립스 방어 횟수 즉시 표시·진동 · Music Shake 영상 재생 중 흔들기 판정 · 결과 공유

This commit is contained in:
Sn-Kinos
2026-10-05 21:39:22 +09:00
parent 65669b969e
commit 17c658b272
30 changed files with 1337 additions and 144 deletions
+46
View File
@@ -105,6 +105,40 @@ export interface PolicyConfig {
referralAttachDays: number;
/** 긴급 돌발 타임어택 지속 시간 상한(분) — 원장 「최대 24시간」. 🟡 운영값. */
timeAttackMaxMinutes: number;
/** Music Shake 판정 · 성공 · 보상 운영값(원장 B-24). 아래 [MusicShakePolicy] 참고. */
musicShake: MusicShakePolicy;
}
/** Music Shake 판정 등급. 등급 체계 자체가 기준자료에 없다 — 아래 [MusicShakePolicy] 참고. */
export type ShakeGrade = 'S' | 'A' | 'B' | 'C' | 'F';
/**
* Music Shake 운영값 — `config/policy.musicShake`.
*
* **🟡 임시값 — 회신 대기. 근거 없음.** 원장 B-24 가 확정한 것은 「BPM 기반 Shake 판정이 성공 /
* 보상 기준 · 완청 보상 아님 · 보상액은 운영값」뿐이다. 판정 창 · 성공 기준 · 등급(S/A/B/C) ·
* 등급 배수는 PRD-04 · 상세정의 v1.6.1 §20 · 별첨 B 어디에도 수치가 없어 **저희가 임시로 둔
* 값**이며, 해석 확인 요청 「2026-10-07 Music Shake」(Q-MS-04 · Q-MS-05) 회신으로 바꾼다.
* 앱(`ShakeWindows`)도 같은 기본값을 쓴다.
*/
export interface MusicShakePolicy {
/** 이 블록이 회신 전 임시값인가. 회신 반영 시 false 로 내린다. */
provisional: boolean;
/** perfect 판정 창 — 박과의 거리(박 길이 대비). 🟡 임시 0.2. */
perfectWindow: number;
/** good 판정 창(이 밖이면 miss). 🟡 임시 0.5. */
goodWindow: number;
/** 성공(보상) 문턱 — (perfect + good) / 구간 수. 🟡 임시 0.5(예전 코드의 「50%」를 옮긴 것). */
passRate: number;
/** 등급 문턱 — perfect / 구간 수. S 는 miss 0 도 요구. 🟡 임시(예전 코드 값을 옮긴 것). */
gradeCuts: { S: number; A: number; B: number };
/** 등급별 기본 보상 배수. 🟡 임시(예전 코드 값). 등급 보상 차등 자체가 회신 대기다. */
gradeMultiplier: Record<ShakeGrade, number>;
/**
* 퀘스트 `rewardPop` 이 없을 때의 기본 보상(일반 POP). 🟡 Admin Figma 「Quest 유형별 검증」
* (`2376:2189`) 의 BPM / Shake 「1 POP」 예시값 — 확정값이 아니다.
*/
pop: number;
}
/**
@@ -173,6 +207,16 @@ export const DEFAULT_POLICY: PolicyConfig = {
referralDailyCapPerReferrer: 5,
referralAttachDays: 7,
timeAttackMaxMinutes: 1440,
// 🟡 임시값 — 회신 대기 · 근거 없음(위 MusicShakePolicy 설명).
musicShake: {
provisional: true,
perfectWindow: 0.2,
goodWindow: 0.5,
passRate: 0.5,
gradeCuts: { S: 0.95, A: 0.8, B: 0.6 },
gradeMultiplier: { S: 2, A: 1.5, B: 1.2, C: 1, F: 0 },
pop: 1,
},
};
export async function loadPolicy(): Promise<PolicyConfig> {
@@ -185,6 +229,8 @@ export async function loadPolicy(): Promise<PolicyConfig> {
...raw,
// 가중치는 부분 지정이 흔하므로 축별로 병합한다. 통째로 덮으면 축이 사라진다.
fpiWeights: weights,
// 판정 운영값은 키별로 병합한다 — 일부만 바꿔도 나머지 임시값이 남는다.
musicShake: { ...DEFAULT_POLICY.musicShake, ...(raw.musicShake ?? {}) },
// 구간표는 통째로 덮는다. 섞으면 운영에서 지운 구간이 되살아난다.
stepTiers: normalizeTiers(raw.stepTiers) ?? DEFAULT_POLICY.stepTiers,
} as PolicyConfig;