Solidify campaign progression state
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
type UnitData,
|
||||
type UnitStats
|
||||
} from '../data/scenario';
|
||||
import { firstBattleScenario } from '../data/battles';
|
||||
import { getTerrainRule, getUnitClass, type TerrainType, type UnitClassKey } from '../data/battleRules';
|
||||
import {
|
||||
equipmentExpToNext,
|
||||
@@ -16,7 +17,7 @@ import {
|
||||
getItem,
|
||||
type EquipmentSlot
|
||||
} from '../data/battleItems';
|
||||
import { setFirstBattleReport } from '../state/campaignState';
|
||||
import { getCampaignState, markCampaignStep, saveCampaignState, setFirstBattleReport, type CampaignStep } from '../state/campaignState';
|
||||
import { palette } from '../ui/palette';
|
||||
|
||||
const unitTexture: Record<string, string> = {
|
||||
@@ -269,6 +270,8 @@ type SavedUnitState = Pick<UnitData, 'id' | 'level' | 'exp' | 'hp' | 'maxHp' | '
|
||||
|
||||
type BattleSaveState = {
|
||||
version: 1;
|
||||
battleId: string;
|
||||
campaignStep?: CampaignStep;
|
||||
savedAt: string;
|
||||
turnNumber: number;
|
||||
activeFaction: ActiveFaction;
|
||||
@@ -286,7 +289,9 @@ type BattleSaveState = {
|
||||
|
||||
const maxEquipmentLevel = 9;
|
||||
const maxCharacterLevel = 99;
|
||||
const battleSaveStorageKey = 'heros-web:first-battle-state';
|
||||
const battleScenario = firstBattleScenario;
|
||||
const legacyBattleSaveStorageKey = 'heros-web:first-battle-state';
|
||||
const battleSaveStorageKey = `heros-web:battle:${battleScenario.id}`;
|
||||
const initialFirstBattleUnits = firstBattleUnits.map(cloneUnitData);
|
||||
const initialFirstBattleBonds = firstBattleBonds.map(cloneBattleBond);
|
||||
|
||||
@@ -413,9 +418,9 @@ const defaultEnemyAiByClass: Partial<Record<UnitClassKey, EnemyAiBehavior>> = {
|
||||
};
|
||||
|
||||
const guardDetectionRange = 5;
|
||||
const leaderUnitId = 'rebel-leader';
|
||||
const quickVictoryTurnLimit = 8;
|
||||
const baseVictoryGold = 300;
|
||||
const leaderUnitId = battleScenario.leaderUnitId;
|
||||
const quickVictoryTurnLimit = battleScenario.quickVictoryTurnLimit;
|
||||
const baseVictoryGold = battleScenario.baseVictoryGold;
|
||||
|
||||
const commandLabels: Record<BattleCommand, string> = {
|
||||
attack: '공격',
|
||||
@@ -529,6 +534,10 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
create() {
|
||||
this.resetBattleData();
|
||||
const campaign = getCampaignState();
|
||||
if (campaign.step === 'new' || campaign.step === 'prologue' || campaign.step === 'first-battle') {
|
||||
markCampaignStep('first-battle');
|
||||
}
|
||||
const { width, height } = this.scale;
|
||||
this.layout = this.createLayout(width, height);
|
||||
const startUnit = firstBattleUnits.find((unit) => unit.id === 'liu-bei') ?? firstBattleUnits[0];
|
||||
@@ -625,7 +634,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.mapMask = maskShape.createGeometryMask();
|
||||
const mapMask = this.mapMask;
|
||||
|
||||
const background = this.add.image(0, 0, 'battle-map-first');
|
||||
const background = this.add.image(0, 0, battleScenario.mapTextureKey);
|
||||
background.setOrigin(0);
|
||||
background.setDepth(0);
|
||||
background.setDisplaySize(firstBattleMap.width * layout.tileSize, firstBattleMap.height * layout.tileSize);
|
||||
@@ -1912,7 +1921,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
defeats: mvp.stats.defeats
|
||||
}
|
||||
: undefined,
|
||||
itemRewards: outcome === 'victory' ? ['콩 1', '탁주 1', '의용군 명성 +1'] : [],
|
||||
itemRewards: outcome === 'victory' ? [...battleScenario.itemRewards] : [],
|
||||
completedCampDialogues: [],
|
||||
createdAt: new Date().toISOString()
|
||||
});
|
||||
@@ -2838,13 +2847,15 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private hasBattleSave() {
|
||||
return Boolean(window.localStorage.getItem(battleSaveStorageKey));
|
||||
return Boolean(window.localStorage.getItem(battleSaveStorageKey) ?? window.localStorage.getItem(legacyBattleSaveStorageKey));
|
||||
}
|
||||
|
||||
private saveBattleState() {
|
||||
try {
|
||||
const state = this.createBattleSaveState();
|
||||
window.localStorage.setItem(battleSaveStorageKey, JSON.stringify(state));
|
||||
window.localStorage.removeItem(legacyBattleSaveStorageKey);
|
||||
saveCampaignState();
|
||||
this.renderSituationPanel(`전투 상태를 저장했습니다.\n${this.formatSavedAt(state.savedAt)}`);
|
||||
} catch {
|
||||
this.renderSituationPanel('전투 상태를 저장하지 못했습니다.');
|
||||
@@ -2852,7 +2863,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private loadBattleState() {
|
||||
const raw = window.localStorage.getItem(battleSaveStorageKey);
|
||||
const raw = window.localStorage.getItem(battleSaveStorageKey) ?? window.localStorage.getItem(legacyBattleSaveStorageKey);
|
||||
if (!raw) {
|
||||
this.renderSituationPanel('불러올 저장 데이터가 없습니다.');
|
||||
return;
|
||||
@@ -2863,6 +2874,9 @@ export class BattleScene extends Phaser.Scene {
|
||||
if (!state || state.version !== 1 || !Array.isArray(state.units)) {
|
||||
throw new Error('Invalid battle save');
|
||||
}
|
||||
if (state.battleId && state.battleId !== battleScenario.id) {
|
||||
throw new Error('Battle save does not belong to this scenario');
|
||||
}
|
||||
this.applyBattleSaveState(state);
|
||||
this.renderSituationPanel(`저장된 전투를 불러왔습니다.\n${this.formatSavedAt(state.savedAt)}`);
|
||||
} catch {
|
||||
@@ -2873,6 +2887,8 @@ export class BattleScene extends Phaser.Scene {
|
||||
private createBattleSaveState(): BattleSaveState {
|
||||
return {
|
||||
version: 1,
|
||||
battleId: battleScenario.id,
|
||||
campaignStep: getCampaignState().step,
|
||||
savedAt: new Date().toISOString(),
|
||||
turnNumber: this.turnNumber,
|
||||
activeFaction: this.activeFaction,
|
||||
@@ -5366,6 +5382,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
getDebugState() {
|
||||
return {
|
||||
scene: this.scene.key,
|
||||
battleId: battleScenario.id,
|
||||
turnNumber: this.turnNumber,
|
||||
activeFaction: this.activeFaction,
|
||||
phase: this.phase,
|
||||
|
||||
Reference in New Issue
Block a user