Record campaign arc and data-driven sorties

This commit is contained in:
2026-06-22 23:29:12 +09:00
parent 68e1b10cb4
commit 97f8efc2e1
5 changed files with 75 additions and 43 deletions

View File

@@ -0,0 +1,57 @@
import type { CampaignStep } from '../state/campaignState';
import { defaultBattleScenario, secondBattleScenario, thirdBattleScenario, type BattleScenarioId } from './battles';
import {
firstBattleVictoryPages,
secondBattleIntroPages,
secondBattleVictoryPages,
thirdBattleIntroPages,
type StoryPage
} from './scenario';
export type SortieFlow = {
afterBattleId: string;
eyebrow: string;
title: string;
description: string;
rewardHint: string;
nextBattleId?: BattleScenarioId;
campaignStep?: CampaignStep;
pages: StoryPage[];
unavailableNotice?: string;
};
const sortieFlows: Record<string, SortieFlow> = {
[defaultBattleScenario.id]: {
afterBattleId: defaultBattleScenario.id,
eyebrow: '다음 전장',
title: secondBattleScenario.title,
description: '탁현을 물러난 황건 잔당이 인근 마을을 위협하고 있습니다. 의용군은 첫 승리의 기세를 몰아 추격에 나섭니다.',
rewardHint: '예상 보상: 군자금, 소모품, 의용군 명성',
nextBattleId: secondBattleScenario.id,
campaignStep: 'second-battle',
pages: [...firstBattleVictoryPages, ...secondBattleIntroPages]
},
[secondBattleScenario.id]: {
afterBattleId: secondBattleScenario.id,
eyebrow: '다음 전장',
title: thirdBattleScenario.title,
description: '탁현을 넘어 광종으로 향하는 길목에서 황건 전령이 관군의 움직임을 본대로 전하려 합니다. 강가 요새와 좁은 길을 돌파해야 합니다.',
rewardHint: `예상 보상: ${thirdBattleScenario.title} 개방`,
nextBattleId: thirdBattleScenario.id,
campaignStep: 'third-battle',
pages: [...secondBattleVictoryPages, ...thirdBattleIntroPages]
},
[thirdBattleScenario.id]: {
afterBattleId: thirdBattleScenario.id,
eyebrow: '다음 장 준비',
title: '광종 본전',
description: '광종 구원로는 열렸지만 황건 본대는 아직 건재합니다. 다음 작업에서 광종 본전과 더 큰 전장 목표를 이어 붙일 수 있습니다.',
rewardHint: '다음 장: 광종 본전 준비 중',
pages: [],
unavailableNotice: '광종 본전은 다음 장으로 이어집니다. 군영에서 성장 상태를 정비하세요.'
}
};
export function getSortieFlow(latestBattleId?: string) {
return sortieFlows[latestBattleId ?? defaultBattleScenario.id] ?? sortieFlows[defaultBattleScenario.id];
}

View File

@@ -2,8 +2,9 @@ import Phaser from 'phaser';
import { soundDirector } from '../audio/SoundDirector';
import { equipmentExpToNext, equipmentSlotLabels, equipmentSlots, getItem, type EquipmentSlot } from '../data/battleItems';
import { getUnitClass } from '../data/battleRules';
import { defaultBattleScenario, secondBattleScenario, thirdBattleScenario } from '../data/battles';
import { firstBattleBonds, firstBattleUnits, secondBattleIntroPages, thirdBattleIntroPages, type PortraitKey, type UnitData } from '../data/scenario';
import { defaultBattleScenario } from '../data/battles';
import { getSortieFlow } from '../data/campaignFlow';
import { firstBattleBonds, firstBattleUnits, type PortraitKey, type UnitData } from '../data/scenario';
import {
applyCampBondExp,
getCampaignState,
@@ -398,37 +399,17 @@ export class CampScene extends Phaser.Scene {
bg.setStrokeStyle(1, palette.gold, 0.4);
const completedDialogues = this.completedCampDialogues().length;
const inventory = this.inventoryLabels().join(', ');
const reward =
this.campaign?.latestBattleId === thirdBattleScenario.id
? '다음 장: 광종 본전 준비 중'
: this.campaign?.latestBattleId === secondBattleScenario.id
? `예상 보상: ${thirdBattleScenario.title} 개방`
: '예상 보상: 군자금, 소모품, 의용군 명성';
const reward = getSortieFlow(this.campaign?.latestBattleId).rewardHint;
this.trackSortie(this.add.text(x + 16, y + 10, reward, this.textStyle(13, '#f2e3bf', true))).setDepth(depth + 1);
this.trackSortie(this.add.text(x + 16, y + 30, `현재 준비: 대화 ${completedDialogues}/${campDialogues.length} · 보유 ${inventory || '없음'}`, this.textStyle(12, '#d4dce6'))).setDepth(depth + 1);
}
private nextSortieBriefing() {
if (this.campaign?.latestBattleId === thirdBattleScenario.id) {
return {
eyebrow: '다음 장 준비',
title: '광종 본전',
description: '광종 구원로는 열렸지만 황건 본대는 아직 건재합니다. 다음 작업에서 광종 본전과 더 큰 전장 목표를 이어 붙일 수 있습니다.'
};
}
if (this.campaign?.latestBattleId === secondBattleScenario.id) {
return {
eyebrow: '다음 전장',
title: thirdBattleScenario.title,
description: '탁현을 넘어 광종으로 향하는 길목에서 황건 전령이 관군의 움직임을 본대로 전하려 합니다. 강가 요새와 좁은 길을 돌파해야 합니다.'
};
}
const flow = getSortieFlow(this.campaign?.latestBattleId);
return {
eyebrow: '다음 전장',
title: secondBattleScenario.title,
description: '탁현을 물러난 황건 잔당이 인근 마을을 위협하고 있습니다. 의용군은 첫 승리의 기세를 몰아 추격에 나섭니다.'
eyebrow: flow.eyebrow,
title: flow.title,
description: flow.description
};
}
@@ -484,27 +465,19 @@ export class CampScene extends Phaser.Scene {
}
private startVictoryStory() {
if (this.campaign?.latestBattleId === thirdBattleScenario.id) {
const flow = getSortieFlow(this.campaign?.latestBattleId);
if (!flow.nextBattleId || !flow.campaignStep || flow.pages.length === 0) {
this.hideSortiePrep();
this.showCampNotice('광종 본전은 다음 장으로 이어집니다. 군영에서 성장 상태를 정비하세요.');
this.showCampNotice(flow.unavailableNotice ?? '다음 장은 아직 준비 중입니다. 군영에서 성장 상태를 정비하세요.');
return;
}
if (this.campaign?.latestBattleId === secondBattleScenario.id) {
markCampaignStep('third-battle');
this.scene.start('StoryScene', {
pages: [...secondBattleScenario.victoryPages, ...thirdBattleIntroPages],
nextScene: 'BattleScene',
nextSceneData: { battleId: thirdBattleScenario.id }
});
return;
}
markCampaignStep('second-battle');
markCampaignStep(flow.campaignStep);
this.scene.start('StoryScene', {
pages: secondBattleIntroPages,
pages: flow.pages,
nextScene: 'BattleScene',
nextSceneData: { battleId: secondBattleScenario.id }
nextSceneData: { battleId: flow.nextBattleId }
});
}