SAVED RESULT{selected.name}
저장 당시 결과 · {new Date(selected.analyzedAt).toLocaleString('ko-KR')}
+ 기본 1노트 {Math.ceil(selected.result.perfectNoteScore).toLocaleString('ko-KR')}점 · 인게임 1노트 {selected.inGamePerfectNoteScore === undefined ? '미기록' : `${selected.inGamePerfectNoteScore.toLocaleString('ko-KR')}점`}
+ 인게임 1노트는 분석 당시 가창 보너스를 반영한 일반 PERFECT 점수이며, 스킬·콤보 보너스는 제외합니다. 기존 기록에는 인게임 1노트 점수가 없을 수 있습니다.
종합력{selected.overallPower.toLocaleString('ko-KR')}
평균 점수{selected.result.expectedScore.toLocaleString('ko-KR')}
이론상 고점{selected.result.theoreticalMaxScore.toLocaleString('ko-KR')}
추천 스페셜 순서
{#each selected.result.order as id, i}- {i + 1}{cardName(id)}쿨타임 −{selected.result.cooldownReductions[id]}%
{/each}
diff --git a/src/lib/engine/analysis-history.test.ts b/src/lib/engine/analysis-history.test.ts
index f827368..ab93a88 100644
--- a/src/lib/engine/analysis-history.test.ts
+++ b/src/lib/engine/analysis-history.test.ts
@@ -54,6 +54,12 @@ test('성장 상태를 공유하는 기록 백업을 손실 없이 읽고 잘못
const parsed = parseAnalysisArchive(JSON.stringify(archive));
assert.equal(parsed.records.length, 2); assert.equal(parsed.snapshots.length, 1);
assert.deepEqual(parsed.snapshots[0].growth, growth);
+ assert.equal(parsed.records[0].inGamePerfectNoteScore, undefined);
+ const withNoteScore = { ...archive, records: [{ ...record, inGamePerfectNoteScore: 11 }] };
+ assert.equal(parseAnalysisArchive(JSON.stringify(withNoteScore)).records[0].inGamePerfectNoteScore, 11);
+ for (const invalidScore of [-1, '11', null]) {
+ assert.throws(() => parseAnalysisArchive(JSON.stringify({ ...archive, records: [{ ...record, inGamePerfectNoteScore: invalidScore }] })));
+ }
for (const difficulty of ['easy', 'normal', 'hard', 'expert']) {
record.settings.difficulty = difficulty;
assert.equal(parseAnalysisArchive(JSON.stringify(archive)).records[0].settings.difficulty, difficulty);
diff --git a/src/lib/models/analysis-history.ts b/src/lib/models/analysis-history.ts
index 7d1eed5..6a3c271 100644
--- a/src/lib/models/analysis-history.ts
+++ b/src/lib/models/analysis-history.ts
@@ -17,6 +17,7 @@ export type StoredResult = Omit & {
id: string; name: string; snapshotId: string; result: StoredResult;
diff --git a/src/lib/storage/analysis-history.ts b/src/lib/storage/analysis-history.ts
index dcf93c1..3899efe 100644
--- a/src/lib/storage/analysis-history.ts
+++ b/src/lib/storage/analysis-history.ts
@@ -31,6 +31,7 @@ export function parseAnalysisArchive(text: string): Archive {
if (s.generationBonusOverrides && (s.generationBonusOverrides.length !== 5 || !s.generationBonusOverrides.every((v) => v === null || (finite(v) && v <= 900)))) return invalid();
if (s.songScoreBonusOverride != null && (!finite(s.songScoreBonusOverride) || s.songScoreBonusOverride > 10)) return invalid();
if (![record.overallPower, record.durationSeconds, r.expectedScore, r.theoreticalMaxScore, r.baselineScore, r.perfectNoteScore].every(finite)) return invalid();
+ if (record.inGamePerfectNoteScore !== undefined && !finite(record.inGamePerfectNoteScore)) return invalid();
if (!strings(r.order) || r.order.length !== 5 || new Set(r.order).size !== 5 || !r.order.every((id) => s.cardIds.includes(id)) || !object(r.cooldownReductions) || !r.order.every((id) => [0, 4, 8, 12].includes(r.cooldownReductions[id]))) return invalid();
}
return archive;
diff --git a/src/routes/analysis/+page.svelte b/src/routes/analysis/+page.svelte
index 4c24dc9..6f4d16b 100644
--- a/src/routes/analysis/+page.svelte
+++ b/src/routes/analysis/+page.svelte
@@ -382,7 +382,7 @@
void tick().then(async () => {
if (!mounted) return;
result = transfer.result;
- resultCapture = transfer.capture;
+ resultCapture = { ...transfer.capture, inGamePerfectNoteScore: finalNoteScore(transfer.result.perfectNoteScore, transfer.directSongScoreBonusPercent) };
resultInputKey = transferredAnalysisKey(transfer.capture);
achievementProbabilities = transfer.achievementProbabilities;
simulationContext = { cards: transfer.cards, chart: transfer.chart, options: {
@@ -861,6 +861,7 @@
title: selectedChart.title,
durationSeconds: selectedChart.durationSeconds,
overallPower,
+ inGamePerfectNoteScore,
analyzedAt: new Date().toISOString()
});
const noteScore = perfectNoteScore;