Harden battle save validation

This commit is contained in:
2026-07-05 11:18:39 +09:00
parent 6d17e86325
commit 63fd404d77
5 changed files with 448 additions and 44 deletions

View File

@@ -55,6 +55,7 @@ import {
type CampaignSortieItemAssignments,
type CampaignStep
} from '../state/campaignState';
import { normalizeBattleSaveSlot, parseBattleSaveState, type BattleSaveState } from '../state/battleSaveState';
import { palette } from '../ui/palette';
import { startLazyScene } from './lazyScenes';
@@ -1500,32 +1501,6 @@ type TargetingGuideNotice = {
tone: number;
};
type SavedUnitState = Pick<UnitData, 'id' | 'level' | 'exp' | 'hp' | 'maxHp' | 'attack' | 'move' | 'x' | 'y'> & {
equipment: UnitData['equipment'];
direction?: UnitDirection;
};
type BattleSaveState = {
version: 1;
battleId: string;
campaignStep?: CampaignStep;
savedAt: string;
turnNumber: number;
activeFaction: ActiveFaction;
rosterTab: RosterTab;
actedUnitIds: string[];
attackIntents: AttackIntent[];
battleLog: string[];
units: SavedUnitState[];
bonds: BondState[];
itemStocks?: Record<string, Record<string, number>>;
battleBuffs?: BattleBuffState[];
battleStatuses?: BattleStatusState[];
battleStats?: Record<string, UnitBattleStats>;
enemyUsableUseKeys?: string[];
triggeredBattleEvents?: string[];
};
const maxEquipmentLevel = 9;
const maxCharacterLevel = 99;
let battleScenario = defaultBattleScenario;
@@ -11362,30 +11337,19 @@ export class BattleScene extends Phaser.Scene {
}
private battleSaveStorageKeyForSlot(slot: number) {
return `${battleSaveStorageKey}:slot-${Phaser.Math.Clamp(Math.floor(slot), 1, campaignSaveSlotCount)}`;
return `${battleSaveStorageKey}:slot-${normalizeBattleSaveSlot(slot, campaignSaveSlotCount)}`;
}
private readBattleSaveState(slot: number) {
const normalizedSlot = Phaser.Math.Clamp(Math.floor(slot), 1, campaignSaveSlotCount);
const normalizedSlot = normalizeBattleSaveSlot(slot, campaignSaveSlotCount);
const raw =
window.localStorage.getItem(this.battleSaveStorageKeyForSlot(normalizedSlot)) ??
(normalizedSlot === 1 ? window.localStorage.getItem(battleSaveStorageKey) ?? window.localStorage.getItem(legacyBattleSaveStorageKey) : undefined);
if (!raw) {
return undefined;
}
try {
const state = JSON.parse(raw) as BattleSaveState;
if (!state || state.version !== 1 || !Array.isArray(state.units)) {
return undefined;
}
if (state.battleId && state.battleId !== battleScenario.id) {
return undefined;
}
return state;
} catch {
return undefined;
}
return parseBattleSaveState(raw, {
expectedBattleId: battleScenario.id,
mapWidth: battleMap.width,
mapHeight: battleMap.height
});
}
private saveBattleState(slot = 1) {