feat: recommend card investments for board connections
Publish GitHub Pages output / publish (push) Successful in 35s
Publish GitHub Pages output / publish (push) Successful in 35s
This commit is contained in:
1 parent
115dc33937
commit
de165bb53d
8 files changed
+428
-1
No files matched your search
@@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import ToolsNav from '$lib/components/ToolsNav.svelte';
|
||||
import BoardAwakening from '$lib/components/BoardAwakening.svelte';
|
||||
import { listAnalyses, readGrowth } from '$lib/storage/analysis-history.ts';
|
||||
import { loadBrowserData } from '$lib/storage/browser-storage.ts';
|
||||
import type { AnalysisRecord, GrowthSnapshot } from '$lib/models/analysis-history.ts';
|
||||
import cards from '$lib/data/cards-summary.json';
|
||||
import boards from '$lib/data/boards-summary.json';
|
||||
import catalog from '$lib/data/exact-chart-catalog.json';
|
||||
let records: AnalysisRecord[] = [];
|
||||
let selectedId = '';
|
||||
let selected: AnalysisRecord | null = null;
|
||||
let growth: GrowthSnapshot | null = null;
|
||||
let source: 'current' | 'saved' = 'current';
|
||||
let error = '';
|
||||
let loading = true;
|
||||
let revision = 0;
|
||||
const byId = new Map(cards.cards.map((card) => [card.id, card]));
|
||||
const cardName = (id: string) => { const card = byId.get(id); return card ? `★${card.rarity} ${card.name}${card.versionName ? ` · ${card.versionName}` : ''}` : id; };
|
||||
$: chart = selected ? catalog.charts.find((item) => item.songId === selected?.settings.songId && item.difficulty === selected?.settings.difficulty) : undefined;
|
||||
onMount(() => { void refresh(); });
|
||||
async function refresh() {
|
||||
try { records = await listAnalyses(); selectedId = records.some((item) => item.id === selectedId) ? selectedId : records[0]?.id ?? ''; await select(); }
|
||||
catch { error = '저장한 분석 기록을 불러오지 못했습니다.'; }
|
||||
finally { loading = false; }
|
||||
}
|
||||
async function select() {
|
||||
const version = ++revision;
|
||||
selected = null; growth = null; error = ''; loading = true;
|
||||
try {
|
||||
const record = records.find((item) => item.id === selectedId);
|
||||
if (!record) return;
|
||||
const value = source === 'saved' ? await readGrowth(record.snapshotId) : loadBrowserData({ types: [], stats: [], activeConditions: [], specialConditions: [], activeProbabilities: [], cardVariants: [], generations: [], sortKey: null, sortDirection: 'desc', skillLevel: 1 });
|
||||
if (version !== revision) return;
|
||||
if (!value) throw new Error('성장 기록을 찾을 수 없습니다.');
|
||||
const snapshot: GrowthSnapshot = structuredClone({ collection: value.collection, memberBoards: value.memberBoards, boardCards: value.boardCards, memoryCount: value.memoryCount, memberPowerUpPercent: value.memberPowerUpPercent });
|
||||
record.settings.cardIds.forEach((id, index) => {
|
||||
if (!record.settings.allBlueNodes?.[index]) return;
|
||||
const member = boards.members.find((item) => item.id === byId.get(id)?.characterId);
|
||||
if (!member) return;
|
||||
const positions = (boards.models as Record<string, { groupId: string }[]>)[member.positionGroupId] ?? [];
|
||||
snapshot.memberBoards[member.id] = [...(snapshot.memberBoards[member.id] ?? []).filter((id) => !id.startsWith('B')), ...positions.filter((item) => item.groupId.startsWith('B')).map((item) => item.groupId)];
|
||||
});
|
||||
selected = record; growth = snapshot;
|
||||
} catch (e) { if (version === revision) error = e instanceof Error ? e.message : '조건을 불러오지 못했습니다.'; }
|
||||
finally { if (version === revision) loading = false; }
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head><title>연결노드 개화 추천 · 도구 · 홀로도리 계산기</title></svelte:head>
|
||||
<main>
|
||||
<h1>도구</h1>
|
||||
<ToolsNav active="board-awakening" />
|
||||
<h2>보드 연결노드용 개화 추천</h2>
|
||||
<p>저장한 분석의 편성·개화·리더 의상·곡·스페셜 순서·쿨타임을 고정하고 연결 카드 투자 효과를 비교합니다.</p>
|
||||
<p>보유 카드와 활성 보드는 기본적으로 현재 데이터를 사용합니다. 분석의 보드 보정 설정도 반영하며, 액티브는 모두 발동 가능한 상태로 계산합니다.</p>
|
||||
<div class="controls">
|
||||
<label>기준 분석 <select bind:value={selectedId} on:change={select} disabled={loading}>{#each records as record}<option value={record.id}>{record.name} · {record.title} · {record.settings.difficulty}</option>{/each}</select></label>
|
||||
<label>성장 데이터 <select bind:value={source} on:change={select} disabled={loading}><option value="current">현재 보유 카드·보드</option><option value="saved">분석 저장 당시</option></select></label>
|
||||
<button on:click={refresh} disabled={loading}>다시 불러오기</button>
|
||||
</div>
|
||||
{#if error}<p role="alert">{error}</p>{/if}
|
||||
{#if loading}<p role="status">조건을 불러오는 중…</p>
|
||||
{:else if !records.length}<p><a href="/analysis">배치 분석</a>에서 편성을 분석하고 결과를 저장하면 여기서 선택할 수 있습니다.</p>{/if}
|
||||
{#if selected && growth}
|
||||
<p>고정 편성: {selected.settings.cardIds.map((id, index) => `${cardName(id)} (${selected!.settings.awakenings[index]}개화)`).join(' / ')}</p>
|
||||
{#if !chart}<p role="alert">저장된 곡의 채보를 찾을 수 없습니다. 다른 분석을 선택해 주세요.</p>{/if}
|
||||
{#key selected.id + source + revision}
|
||||
<BoardAwakening settings={selected.settings} {growth} order={selected.result.order} cooldownReductions={selected.result.cooldownReductions} chartMetadata={chart} {cardName}
|
||||
socketName={(member, socket) => `${boards.members.find((item) => item.id === member)?.name ?? member} · ${socket}`} />
|
||||
{/key}
|
||||
{/if}
|
||||
</main>
|
||||
<style>
|
||||
main { width: min(1200px, calc(100% - var(--page-gutter))); margin: 0 auto; padding: 42px 0 80px; }
|
||||
p { color: #a4adba; line-height: 1.7; } a { color: #f3a5c5; }
|
||||
.controls { display: flex; gap: 12px; flex-wrap: wrap; align-items: end; }
|
||||
label { display: grid; gap: 8px; max-width: 100%; }
|
||||
select, button { max-width: 100%; padding: 10px; border: 1px solid #414b5b; border-radius: 8px; background: #171c24; color: #e8ebf1; }
|
||||
button { cursor: pointer; } button:disabled { opacity: .5; }
|
||||
</style>
|
||||
Reference in new issue
Block a user