Add camp save slot selection
This commit is contained in:
@@ -103,8 +103,11 @@ import {
|
||||
getCampaignState,
|
||||
getFirstBattleReport,
|
||||
markCampaignStep,
|
||||
listCampaignSaveSlots,
|
||||
campaignSaveSlotCount,
|
||||
saveCampaignState,
|
||||
setCampaignReserveTrainingFocus,
|
||||
type CampaignSaveSlotSummary,
|
||||
type CampaignStep,
|
||||
type CampaignState,
|
||||
type CampaignReserveTrainingFocusDefinition,
|
||||
@@ -10713,6 +10716,7 @@ export class CampScene extends Phaser.Scene {
|
||||
private contentObjects: Phaser.GameObjects.GameObject[] = [];
|
||||
private dialogueObjects: Phaser.GameObjects.GameObject[] = [];
|
||||
private sortieObjects: Phaser.GameObjects.GameObject[] = [];
|
||||
private saveSlotObjects: Phaser.GameObjects.GameObject[] = [];
|
||||
private tabButtons: CampTabButtonView[] = [];
|
||||
private visitedTabs = new Set<CampTab>();
|
||||
private selectedSortieUnitIds: string[] = [];
|
||||
@@ -10737,6 +10741,7 @@ export class CampScene extends Phaser.Scene {
|
||||
this.contentObjects = [];
|
||||
this.dialogueObjects = [];
|
||||
this.sortieObjects = [];
|
||||
this.saveSlotObjects = [];
|
||||
this.tabButtons = [];
|
||||
this.visitedTabs = new Set(['status']);
|
||||
this.campaign = getCampaignState();
|
||||
@@ -11825,9 +11830,7 @@ export class CampScene extends Phaser.Scene {
|
||||
this.addTabButton('연표', 'progress', 966, 38);
|
||||
this.addCommandButton('저장', 1044, 38, 76, () => {
|
||||
soundDirector.playSelect();
|
||||
this.campaign = saveCampaignState(getCampaignState());
|
||||
this.showCampNotice(`슬롯 ${this.campaign.activeSaveSlot}에 군영 진행을 저장했습니다.`);
|
||||
this.render();
|
||||
this.showCampSaveSlotPanel();
|
||||
});
|
||||
this.addCommandButton('다음 이야기', 1160, 38, 142, () => {
|
||||
soundDirector.playSelect();
|
||||
@@ -11840,6 +11843,141 @@ export class CampScene extends Phaser.Scene {
|
||||
});
|
||||
}
|
||||
|
||||
private showCampSaveSlotPanel() {
|
||||
if (this.saveSlotObjects.length > 0) {
|
||||
this.hideCampSaveSlotPanel();
|
||||
return;
|
||||
}
|
||||
|
||||
const depth = 48;
|
||||
const width = 430;
|
||||
const height = 252;
|
||||
const left = this.scale.width - width - 36;
|
||||
const top = 70;
|
||||
const activeSlot = this.campaign?.activeSaveSlot ?? getCampaignState().activeSaveSlot;
|
||||
const slots = listCampaignSaveSlots();
|
||||
|
||||
const panel = this.add.rectangle(left, top, width, height, 0x0f1720, 0.98);
|
||||
panel.setOrigin(0);
|
||||
panel.setDepth(depth);
|
||||
panel.setStrokeStyle(2, palette.gold, 0.86);
|
||||
panel.setInteractive();
|
||||
this.saveSlotObjects.push(panel);
|
||||
|
||||
const title = this.add.text(left + 22, top + 16, '군영 저장 슬롯', this.textStyle(22, '#f2e3bf', true));
|
||||
title.setDepth(depth + 1);
|
||||
this.saveSlotObjects.push(title);
|
||||
|
||||
const guide = this.add.text(
|
||||
left + 22,
|
||||
top + 48,
|
||||
'현재 군영 진행, 보유 장비, 편성 정보를 선택한 슬롯에 저장합니다.',
|
||||
this.textStyle(13, '#aeb7c2')
|
||||
);
|
||||
guide.setDepth(depth + 1);
|
||||
this.saveSlotObjects.push(guide);
|
||||
|
||||
for (let slot = 1; slot <= campaignSaveSlotCount; slot += 1) {
|
||||
const summary = slots.find((candidate) => candidate.slot === slot) ?? { slot, exists: false };
|
||||
this.renderCampSaveSlotRow(summary, activeSlot, left + 18, top + 82 + (slot - 1) * 52, width - 36, depth + 1);
|
||||
}
|
||||
|
||||
const close = this.add.text(left + width - 58, top + 18, '닫기', this.textStyle(14, '#d8b15f', true));
|
||||
close.setDepth(depth + 2);
|
||||
close.setInteractive({ useHandCursor: true });
|
||||
close.on('pointerdown', () => {
|
||||
soundDirector.playSelect();
|
||||
this.hideCampSaveSlotPanel();
|
||||
});
|
||||
this.saveSlotObjects.push(close);
|
||||
}
|
||||
|
||||
private renderCampSaveSlotRow(
|
||||
summary: CampaignSaveSlotSummary,
|
||||
activeSlot: number,
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
depth: number
|
||||
) {
|
||||
const active = summary.slot === activeSlot;
|
||||
const row = this.add.rectangle(x, y, width, 44, active ? 0x263a2d : 0x172230, active ? 0.98 : 0.94);
|
||||
row.setOrigin(0);
|
||||
row.setDepth(depth);
|
||||
row.setStrokeStyle(1, active ? palette.gold : palette.blue, active ? 0.86 : 0.62);
|
||||
this.saveSlotObjects.push(row);
|
||||
|
||||
const title = this.add.text(x + 14, y + 8, `슬롯 ${summary.slot}`, this.textStyle(15, '#f2e3bf', true));
|
||||
title.setDepth(depth + 1);
|
||||
this.saveSlotObjects.push(title);
|
||||
|
||||
const detail = this.add.text(
|
||||
x + 92,
|
||||
y + 7,
|
||||
summary.exists ? this.campSaveSlotDetail(summary) : '비어 있음',
|
||||
this.textStyle(12, summary.exists ? '#d4dce6' : '#87919c', summary.exists)
|
||||
);
|
||||
detail.setDepth(depth + 1);
|
||||
this.saveSlotObjects.push(detail);
|
||||
|
||||
const meta = this.add.text(
|
||||
x + 92,
|
||||
y + 24,
|
||||
summary.exists ? this.formatCampSaveUpdatedAt(summary.updatedAt) : '새 저장으로 사용',
|
||||
this.textStyle(10, summary.exists ? '#9fb0bf' : '#d8b15f', true)
|
||||
);
|
||||
meta.setDepth(depth + 1);
|
||||
this.saveSlotObjects.push(meta);
|
||||
|
||||
const saveLabel = this.add.text(x + width - 50, y + 13, active ? '현재' : '저장', this.textStyle(12, active ? '#a8ffd0' : '#ffdf7b', true));
|
||||
saveLabel.setOrigin(0.5, 0);
|
||||
saveLabel.setDepth(depth + 1);
|
||||
this.saveSlotObjects.push(saveLabel);
|
||||
|
||||
const run = () => this.saveCampToSlot(summary.slot);
|
||||
[row, title, detail, meta, saveLabel].forEach((object) => {
|
||||
object.setInteractive({ useHandCursor: true });
|
||||
object.on('pointerdown', run);
|
||||
});
|
||||
row.on('pointerover', () => row.setFillStyle(active ? 0x314a38 : 0x263647, 0.98));
|
||||
row.on('pointerout', () => row.setFillStyle(active ? 0x263a2d : 0x172230, active ? 0.98 : 0.94));
|
||||
}
|
||||
|
||||
private campSaveSlotDetail(summary: CampaignSaveSlotSummary) {
|
||||
const battleTitle = summary.battleTitle ?? '군영 진행';
|
||||
return this.compactText(`군자금 ${summary.gold ?? 0} · ${battleTitle}`, 28);
|
||||
}
|
||||
|
||||
private formatCampSaveUpdatedAt(updatedAt?: string) {
|
||||
if (!updatedAt) {
|
||||
return '저장 시각 없음';
|
||||
}
|
||||
|
||||
const date = new Date(updatedAt);
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return '저장 시각 미상';
|
||||
}
|
||||
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate();
|
||||
const hour = String(date.getHours()).padStart(2, '0');
|
||||
const minute = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${month}/${day} ${hour}:${minute}`;
|
||||
}
|
||||
|
||||
private saveCampToSlot(slot: number) {
|
||||
soundDirector.playSelect();
|
||||
this.hideCampSaveSlotPanel();
|
||||
this.campaign = saveCampaignState(getCampaignState(), slot);
|
||||
this.showCampNotice(`슬롯 ${this.campaign.activeSaveSlot}에 군영 진행을 저장했습니다.`);
|
||||
this.render();
|
||||
}
|
||||
|
||||
private hideCampSaveSlotPanel() {
|
||||
this.saveSlotObjects.forEach((object) => object.destroy());
|
||||
this.saveSlotObjects = [];
|
||||
}
|
||||
|
||||
private addTabButton(label: string, tab: CampTab, x: number, y: number) {
|
||||
const bg = this.add.rectangle(x, y, 76, 34, 0x182431, 0.94);
|
||||
bg.setStrokeStyle(1, palette.blue, 0.62);
|
||||
|
||||
Reference in New Issue
Block a user