diff --git a/UPDATES.md b/UPDATES.md index 7b7f06c..3550596 100644 --- a/UPDATES.md +++ b/UPDATES.md @@ -12,7 +12,7 @@ updatedAt: 2026-09-08T12:00:00+09:00 ### 액티브 미발동 시뮬레이션 -`NEW` 배치 분석의 카드별 액티브 구간을 클릭해 해당 발동을 제외한 예상 점수·이론상 고점과 그래프를 확인할 수 있습니다. 다시 클릭하거나 모두 복원하면 원래 결과로 돌아옵니다. +`NEW` 배치 분석의 카드별 액티브 구간을 클릭해 해당 발동을 제외한 예상 점수·이론상 고점과 그래프를 확인할 수 있습니다. 다시 클릭하거나 모두 복원하면 원래 결과로 돌아옵니다. 그래프 근처에서 현재 최고점·원래 대비 변화와 각 토글 직전·직후의 점수 차이를 확인할 수 있습니다. ### 연결 카드 최적화의 최고점 우선 적용 diff --git a/src/routes/analysis/+page.svelte b/src/routes/analysis/+page.svelte index aaed24a..2e96f94 100644 --- a/src/routes/analysis/+page.svelte +++ b/src/routes/analysis/+page.svelte @@ -118,8 +118,10 @@ let simulationBase: ExactOptimizationResult | null = null; let simulatedResult: ExactOptimizationResult | null = null; let excludedActiveWindows: SkillWindow[] = []; + let toggleScoreChanges: { window: SkillWindow | null; excluded: boolean; before: number; after: number }[] = []; $: if (result !== simulationBase) { simulationBase = result; + toggleScoreChanges = []; simulatedResult = null; excludedActiveWindows = []; } @@ -131,11 +133,14 @@ function toggleActiveWindow(window: SkillWindow): void { if (!result || !simulationContext) return; - excludedActiveWindows = isExcluded(window) + const before = (simulatedResult ?? result).theoreticalMaxScore; + const excluded = !isExcluded(window); + excludedActiveWindows = !excluded ? excludedActiveWindows.filter((excluded) => excluded.cardId !== window.cardId || excluded.startSeconds !== window.startSeconds) : [...excludedActiveWindows, window]; if (!excludedActiveWindows.length) { simulatedResult = null; + toggleScoreChanges = [{ window, excluded, before, after: result.theoreticalMaxScore }, ...toggleScoreChanges]; return; } const { cards, chart, options } = simulationContext; @@ -145,13 +150,21 @@ ...calculateExactScore(order, result.cooldownReductions, chart, { ...options, excludedActiveWindows }), ...buildExactSkillWindows(order, result.cooldownReductions, chart, excludedActiveWindows) }; + toggleScoreChanges = [{ window, excluded, before, after: simulatedResult.theoreticalMaxScore }, ...toggleScoreChanges]; } function resetActiveWindows(): void { + if (result && simulatedResult) { + toggleScoreChanges = [{ window: null, excluded: false, before: simulatedResult.theoreticalMaxScore, after: result.theoreticalMaxScore }, ...toggleScoreChanges]; + } excludedActiveWindows = []; simulatedResult = null; } + function signedScoreChange(value: number): string { + return (value > 0 ? '+' : value < 0 ? '−' : '') + Math.abs(value).toLocaleString('ko-KR') + '점'; + } + let ratingResultKey = ''; let resultCapture: AnalysisCapture | null = null; let pendingCapture: AnalysisCapture | null = null; @@ -1135,7 +1148,27 @@ {#each result.order as cardId}{resultCardName(cardId)}{/each} +
최근 조작부터 표시합니다. 각 변화량은 클릭 직전 설정과 비교하며, 겹친 액티브에 따라 달라집니다.
+카드별 색칠된 구간을 클릭하면 해당 액티브만 미발동으로 계산합니다. 다시 클릭하면 복원됩니다. 배치와 쿨타임은 유지됩니다. {#if excludedActiveWindows.length} {excludedActiveWindows.length}회 미발동 적용 중 @@ -1346,6 +1379,20 @@ .active-window:hover:not(:disabled), .active-window:focus-visible { opacity: 1; outline: 2px solid #fff; outline-offset: 2px; } .active-window.excluded { opacity: .28; border: 1px dashed var(--member-color); background: repeating-linear-gradient(135deg, transparent 0 3px, var(--member-color) 3px 4px); } .active-window:disabled { cursor: default; } + .timeline-score-summary { display: flex; flex-wrap: wrap; gap: 16px 32px; padding: 16px; margin-bottom: 12px; border: 1px solid #454052; border-radius: 10px; background: #22202a; } + .timeline-score-summary > div { display: grid; gap: 6px; } + .timeline-score-summary span { color: #adb5c2; font-size: 12px; } + .timeline-score-summary strong { color: #f2a3c4; font-size: 22px; } + .timeline-score-summary b { color: #e1e5ec; font-size: 16px; } + .toggle-score-changes { margin: 12px 0; padding: 12px 16px; border: 1px solid #343c49; border-radius: 10px; } + .toggle-score-changes summary { cursor: pointer; color: #e1e5ec; font-size: 14px; } + .toggle-score-changes p { color: #adb5c2; font-size: 12px; line-height: 1.6; } + .toggle-score-changes ol { list-style: none; max-height: 180px; overflow-y: auto; margin: 0; padding: 0; } + .toggle-score-changes li { display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: baseline; padding: 8px 0; border-top: 1px solid #343c49; font-size: 13px; } + .toggle-score-changes li > span { flex: 1 1 240px; color: #e1e5ec; } + .toggle-score-changes small { color: #adb5c2; } + .timeline-score-summary .score-loss, .toggle-score-changes .score-loss { color: #f2a3a3; } + .toggle-score-changes .score-gain { color: #82d09e; } .simulation-guide { color: #adb5c2; font-size: 13px; line-height: 1.7; } .simulation-guide strong, .simulation-guide span { display: block; } .simulation-guide button { margin: 4px 0; }