Solidify campaign progression state
This commit is contained in:
@@ -2,7 +2,14 @@ import Phaser from 'phaser';
|
||||
import { soundDirector } from '../audio/SoundDirector';
|
||||
import { equipmentExpToNext, equipmentSlotLabels, equipmentSlots, getItem, type EquipmentSlot } from '../data/battleItems';
|
||||
import { firstBattleBonds, firstBattleUnits, firstBattleVictoryPages, type PortraitKey, type UnitData } from '../data/scenario';
|
||||
import { applyCampBondExp, getFirstBattleReport, type CampBondSnapshot, type FirstBattleReport } from '../state/campaignState';
|
||||
import {
|
||||
applyCampBondExp,
|
||||
getCampaignState,
|
||||
getFirstBattleReport,
|
||||
markCampaignStep,
|
||||
type CampaignState,
|
||||
type FirstBattleReport
|
||||
} from '../state/campaignState';
|
||||
import { palette } from '../ui/palette';
|
||||
|
||||
type CampTab = 'status' | 'dialogue' | 'supplies';
|
||||
@@ -68,6 +75,7 @@ const campDialogues: CampDialogue[] = [
|
||||
];
|
||||
|
||||
export class CampScene extends Phaser.Scene {
|
||||
private campaign?: CampaignState;
|
||||
private report?: FirstBattleReport;
|
||||
private selectedUnitId = 'liu-bei';
|
||||
private activeTab: CampTab = 'status';
|
||||
@@ -80,8 +88,9 @@ export class CampScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
create() {
|
||||
this.report = getFirstBattleReport() ?? this.createFallbackReport();
|
||||
this.selectedUnitId = this.report.units.find((unit) => unit.faction === 'ally')?.id ?? 'liu-bei';
|
||||
this.campaign = getCampaignState();
|
||||
this.report = this.campaign.firstBattleReport ?? getFirstBattleReport() ?? this.createFallbackReport();
|
||||
this.selectedUnitId = this.currentUnits().find((unit) => unit.faction === 'ally')?.id ?? 'liu-bei';
|
||||
soundDirector.playMusic('militia-theme');
|
||||
|
||||
const { width, height } = this.scale;
|
||||
@@ -130,7 +139,8 @@ export class CampScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
const achieved = this.report.objectives.filter((objective) => objective.achieved).length;
|
||||
return `첫 전투 ${this.report.turnNumber}턴 승리 · 군자금 ${this.report.rewardGold} · 목표 ${achieved}/${this.report.objectives.length} · 격파 ${this.report.defeatedEnemies}/${this.report.totalEnemies}`;
|
||||
const gold = this.campaign?.gold ?? this.report.rewardGold;
|
||||
return `첫 전투 ${this.report.turnNumber}턴 승리 · 군자금 ${gold} · 목표 ${achieved}/${this.report.objectives.length} · 격파 ${this.report.defeatedEnemies}/${this.report.totalEnemies}`;
|
||||
}
|
||||
|
||||
private renderStaticButtons() {
|
||||
@@ -139,6 +149,7 @@ export class CampScene extends Phaser.Scene {
|
||||
this.addTabButton('정비', 'supplies', 918, 38);
|
||||
this.addCommandButton('다음 이야기', 1080, 38, 150, () => {
|
||||
soundDirector.playSelect();
|
||||
markCampaignStep('first-victory-story');
|
||||
this.scene.start('StoryScene', { pages: firstBattleVictoryPages, nextScene: 'TitleScene' });
|
||||
});
|
||||
}
|
||||
@@ -206,7 +217,7 @@ export class CampScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private renderUnitColumn() {
|
||||
const allies = this.report?.units.filter((unit) => unit.faction === 'ally') ?? [];
|
||||
const allies = this.currentUnits().filter((unit) => unit.faction === 'ally');
|
||||
allies.forEach((unit, index) => {
|
||||
const x = 42;
|
||||
const y = 120 + index * 150;
|
||||
@@ -294,7 +305,7 @@ export class CampScene extends Phaser.Scene {
|
||||
this.track(this.add.text(x + 24, y + 56, '대화를 보면 해당 장수들의 공명 경험치가 오릅니다.', this.textStyle(14, '#d4dce6')));
|
||||
|
||||
campDialogues.forEach((dialogue, index) => {
|
||||
const completed = this.report?.completedCampDialogues.includes(dialogue.id) ?? false;
|
||||
const completed = this.completedCampDialogues().includes(dialogue.id);
|
||||
const rowY = y + 96 + index * 64;
|
||||
const selected = this.selectedDialogueId === dialogue.id;
|
||||
const row = this.track(this.add.rectangle(x + 24, rowY, 320, 48, selected ? 0x25384a : 0x151f2a, selected ? 0.96 : 0.86));
|
||||
@@ -316,7 +327,7 @@ export class CampScene extends Phaser.Scene {
|
||||
|
||||
private renderSelectedDialogue(x: number, y: number, width: number, height: number) {
|
||||
const dialogue = campDialogues.find((candidate) => candidate.id === this.selectedDialogueId) ?? campDialogues[0];
|
||||
const completed = this.report?.completedCampDialogues.includes(dialogue.id) ?? false;
|
||||
const completed = this.completedCampDialogues().includes(dialogue.id);
|
||||
const bg = this.track(this.add.rectangle(x, y, width, height, 0x0d141c, 0.92));
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(1, completed ? palette.green : palette.gold, 0.5);
|
||||
@@ -346,7 +357,7 @@ export class CampScene extends Phaser.Scene {
|
||||
|
||||
private renderBondList(x: number, y: number, width: number) {
|
||||
this.track(this.add.text(x, y, '공명', this.textStyle(18, '#f2e3bf', true)));
|
||||
this.report?.bonds.forEach((bond, index) => {
|
||||
this.currentBonds().forEach((bond, index) => {
|
||||
const rowY = y + 32 + index * 36;
|
||||
const first = this.unitName(bond.unitIds[0]);
|
||||
const second = this.unitName(bond.unitIds[1]);
|
||||
@@ -364,13 +375,13 @@ export class CampScene extends Phaser.Scene {
|
||||
this.track(this.add.text(x + 24, y + 22, '정비와 보급', this.textStyle(24, '#f2e3bf', true)));
|
||||
this.track(this.add.text(x + 24, y + 58, '획득한 보상과 장비 성장 상태를 확인합니다. 장비 교체와 상점은 다음 확장에서 붙일 수 있습니다.', this.textStyle(14, '#d4dce6')));
|
||||
|
||||
const rewards = this.report?.itemRewards.length ? this.report.itemRewards : ['콩 1', '탁주 1'];
|
||||
const rewards = this.inventoryLabels();
|
||||
rewards.forEach((reward, index) => {
|
||||
this.renderSupplyBox(reward, x + 24 + index * 160, y + 104);
|
||||
});
|
||||
|
||||
this.track(this.add.text(x + 24, y + 190, '부대 장비 요약', this.textStyle(20, '#f2e3bf', true)));
|
||||
this.report?.units
|
||||
this.currentUnits()
|
||||
.filter((unit) => unit.faction === 'ally')
|
||||
.forEach((unit, index) => {
|
||||
const rowY = y + 232 + index * 58;
|
||||
@@ -418,6 +429,7 @@ export class CampScene extends Phaser.Scene {
|
||||
const updated = applyCampBondExp(dialogue.id, dialogue.bondId, dialogue.rewardExp);
|
||||
if (updated) {
|
||||
this.report = updated;
|
||||
this.campaign = getCampaignState();
|
||||
} else if (this.report && !this.report.completedCampDialogues.includes(dialogue.id)) {
|
||||
this.report.completedCampDialogues.push(dialogue.id);
|
||||
const bond = this.report.bonds.find((candidate) => candidate.id === dialogue.bondId);
|
||||
@@ -455,11 +467,41 @@ export class CampScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private selectedUnit() {
|
||||
return this.report?.units.find((unit) => unit.id === this.selectedUnitId);
|
||||
return this.currentUnits().find((unit) => unit.id === this.selectedUnitId);
|
||||
}
|
||||
|
||||
private unitName(unitId: string) {
|
||||
return this.report?.units.find((unit) => unit.id === unitId)?.name ?? unitId;
|
||||
return this.currentUnits().find((unit) => unit.id === unitId)?.name ?? unitId;
|
||||
}
|
||||
|
||||
private currentUnits() {
|
||||
if (this.campaign?.roster.length) {
|
||||
return this.campaign.roster;
|
||||
}
|
||||
return this.report?.units ?? [];
|
||||
}
|
||||
|
||||
private currentBonds() {
|
||||
if (this.campaign?.bonds.length) {
|
||||
return this.campaign.bonds;
|
||||
}
|
||||
return this.report?.bonds ?? [];
|
||||
}
|
||||
|
||||
private completedCampDialogues() {
|
||||
if (this.campaign) {
|
||||
return this.campaign.completedCampDialogues;
|
||||
}
|
||||
return this.report?.completedCampDialogues ?? [];
|
||||
}
|
||||
|
||||
private inventoryLabels() {
|
||||
const inventory = this.campaign?.inventory ?? {};
|
||||
const entries = Object.entries(inventory).filter(([, amount]) => amount > 0);
|
||||
if (entries.length > 0) {
|
||||
return entries.map(([label, amount]) => `${label} ${amount}`);
|
||||
}
|
||||
return this.report?.itemRewards.length ? this.report.itemRewards : ['콩 1', '탁주 1'];
|
||||
}
|
||||
|
||||
private drawBar(x: number, y: number, width: number, height: number, ratio: number, color: number) {
|
||||
@@ -494,6 +536,14 @@ export class CampScene extends Phaser.Scene {
|
||||
activeTab: this.activeTab,
|
||||
selectedUnitId: this.selectedUnitId,
|
||||
selectedDialogueId: this.selectedDialogueId,
|
||||
campaign: this.campaign
|
||||
? {
|
||||
step: this.campaign.step,
|
||||
gold: this.campaign.gold,
|
||||
inventory: this.campaign.inventory,
|
||||
completedCampDialogues: this.campaign.completedCampDialogues
|
||||
}
|
||||
: null,
|
||||
report: this.report
|
||||
? {
|
||||
rewardGold: this.report.rewardGold,
|
||||
|
||||
Reference in New Issue
Block a user