Clarify campaign save slot summaries

This commit is contained in:
2026-07-05 03:01:40 +09:00
parent 1171b122d6
commit 390cfcf37a
4 changed files with 103 additions and 26 deletions

View File

@@ -1,6 +1,8 @@
import { equipmentExpToNext, equipmentSlots } from '../data/battleItems';
import { battleScenarios, type BattleScenarioId } from '../data/battles';
import { campaignRecruitUnits, type BattleBond, type UnitData } from '../data/scenario';
import { normalizeSortieFormationAssignments, type SortieFormationAssignments } from '../data/sortieDeployment';
import { battleIdForCampaignStep, isCampCampaignStep } from './campaignRouting';
export type BattleObjectiveSnapshot = {
id: string;
@@ -393,12 +395,93 @@ export type CampaignSaveSlotSummary = {
step?: CampaignStep;
gold?: number;
battleTitle?: string;
progressTitle?: string;
progressMeta?: string;
rosterCount?: number;
inventoryCount?: number;
};
let campaignState: CampaignState | undefined;
const campaignRecruitUnitById = new Map(campaignRecruitUnits.map((unit) => [unit.id, unit]));
const campaignStepProgressLabels: Partial<Record<CampaignStep, { title: string; meta: string }>> = {
new: { title: '새 캠페인', meta: '시작 전' },
prologue: { title: '도원 결의', meta: '프롤로그' },
'first-victory-story': { title: '탁현 방어 성공', meta: '승리 후 이야기' },
'hanzhong-king-camp': { title: '한중왕 즉위 준비', meta: '군영 정비' },
'shu-han-foundation-camp': { title: '촉한 건국 선포', meta: '군영 정비' },
'baidi-entrustment-camp': { title: '백제성 유탁', meta: '군영 정비' },
'northern-campaign-prep-camp': { title: '북벌 준비 회의', meta: '군영 정비' },
'ending-complete': { title: '북벌의 끝과 남은 뜻', meta: '완료' }
};
export function summarizeCampaignProgress(state: CampaignState) {
const battleId = battleIdForCampaignStep(state.step);
const battleTitle = battleId ? scenarioTitle(battleId) : undefined;
if (battleTitle) {
return {
title: battleTitle,
meta: state.step.endsWith('-battle') ? '출전 준비' : '전투 진행'
};
}
const special = campaignStepProgressLabels[state.step];
if (special) {
return special;
}
const latestSettlement = latestCampaignSettlement(state);
if (isCampCampaignStep(state.step) && latestSettlement) {
return {
title: latestSettlement.battleTitle,
meta: latestSettlement.outcome === 'victory' ? '승리 후 군영' : '재정비'
};
}
if (latestSettlement) {
return {
title: latestSettlement.battleTitle,
meta: latestSettlement.outcome === 'victory' ? '최근 승리' : '최근 전투'
};
}
if (state.firstBattleReport) {
return {
title: state.firstBattleReport.battleTitle,
meta: state.firstBattleReport.outcome === 'victory' ? '승리 기록' : '전투 기록'
};
}
return {
title: '캠페인 진행',
meta: '진행 중'
};
}
function scenarioTitle(id?: BattleScenarioId | string) {
if (id && id in battleScenarios) {
return battleScenarios[id as BattleScenarioId].title;
}
return undefined;
}
function latestCampaignSettlement(state: CampaignState) {
if (state.latestBattleId) {
const latest = state.battleHistory[state.latestBattleId];
if (latest) {
return latest;
}
}
return Object.values(state.battleHistory)
.sort((a, b) => String(b.completedAt).localeCompare(String(a.completedAt)))[0];
}
function countCampaignInventory(inventory: Record<string, number>) {
return Object.values(inventory).reduce((total, amount) => total + Math.max(0, amount), 0);
}
export function createInitialCampaignState(): CampaignState {
return {
version: 1,
@@ -469,13 +552,18 @@ export function listCampaignSaveSlots(): CampaignSaveSlotSummary[] {
if (!state) {
return { slot, exists: false };
}
const progress = summarizeCampaignProgress(state);
return {
slot,
exists: true,
updatedAt: state.updatedAt,
step: state.step,
gold: state.gold,
battleTitle: state.firstBattleReport?.battleTitle
battleTitle: progress.title,
progressTitle: progress.title,
progressMeta: progress.meta,
rosterCount: state.roster.length,
inventoryCount: countCampaignInventory(state.inventory)
};
});
}