Validate battle save equipment state
This commit is contained in:
@@ -17,7 +17,13 @@ try {
|
||||
mapHeight: 8,
|
||||
validUnitIds: new Set(['liu-bei', 'guan-yu', 'rebel-1']),
|
||||
validUsableIds: new Set(['roar', 'fireTactic', 'aid', 'bean', 'salve', 'wine']),
|
||||
validItemIds: new Set(['bean', 'salve', 'wine'])
|
||||
validItemIds: new Set(['bean', 'salve', 'wine']),
|
||||
validEquipmentItemIds: new Set(['training-sword', 'cloth-armor', 'grain-pouch']),
|
||||
validEquipmentSlotItems: {
|
||||
weapon: new Set(['training-sword']),
|
||||
armor: new Set(['cloth-armor']),
|
||||
accessory: new Set(['grain-pouch'])
|
||||
}
|
||||
};
|
||||
const validState = createValidBattleSaveState();
|
||||
const patchUnit = (index, patch) => validState.units.map((unit, unitIndex) => (unitIndex === index ? { ...unit, ...patch } : unit));
|
||||
@@ -50,6 +56,12 @@ try {
|
||||
['invalid unit hp', { units: patchUnit(0, { hp: 99, maxHp: 30 }) }],
|
||||
['invalid unit x', { units: patchUnit(0, { x: 12 }) }],
|
||||
['invalid unit direction', { units: patchUnit(0, { direction: 'down' }) }],
|
||||
['missing equipment slot', { units: patchUnit(0, { equipment: { weapon: createEquipmentSet().weapon, armor: createEquipmentSet().armor } }) }],
|
||||
['extra equipment slot', { units: patchUnit(0, { equipment: { ...createEquipmentSet(), charm: createEquipmentSet().accessory } }) }],
|
||||
['unknown equipment item id', { units: patchUnit(0, { equipment: { ...createEquipmentSet(), weapon: { itemId: 'phantom-sword', level: 1, exp: 0 } } }) }],
|
||||
['wrong equipment slot item id', { units: patchUnit(0, { equipment: { ...createEquipmentSet(), weapon: { itemId: 'cloth-armor', level: 1, exp: 0 } } }) }],
|
||||
['invalid equipment level', { units: patchUnit(0, { equipment: { ...createEquipmentSet(), weapon: { itemId: 'training-sword', level: 0, exp: 0 } } }) }],
|
||||
['invalid equipment exp', { units: patchUnit(0, { equipment: { ...createEquipmentSet(), weapon: { itemId: 'training-sword', level: 1, exp: -1 } } }) }],
|
||||
['unknown unit id', { units: patchUnit(0, { id: 'ghost-unit' }) }],
|
||||
['duplicate unit id', { units: [validState.units[0], { ...validState.units[0], x: 2 }, validState.units[2]] }],
|
||||
['invalid bonds', { bonds: [{ ...validState.bonds[0], unitIds: ['liu-bei'] }] }],
|
||||
@@ -113,7 +125,7 @@ function createValidBattleSaveState() {
|
||||
x: 1,
|
||||
y: 2,
|
||||
direction: 'south',
|
||||
equipment: {}
|
||||
equipment: createEquipmentSet()
|
||||
},
|
||||
{
|
||||
id: 'guan-yu',
|
||||
@@ -126,7 +138,7 @@ function createValidBattleSaveState() {
|
||||
x: 2,
|
||||
y: 2,
|
||||
direction: 'south',
|
||||
equipment: {}
|
||||
equipment: createEquipmentSet()
|
||||
},
|
||||
{
|
||||
id: 'rebel-1',
|
||||
@@ -139,7 +151,7 @@ function createValidBattleSaveState() {
|
||||
x: 8,
|
||||
y: 4,
|
||||
direction: 'west',
|
||||
equipment: {}
|
||||
equipment: createEquipmentSet()
|
||||
}
|
||||
],
|
||||
bonds: [
|
||||
@@ -187,6 +199,14 @@ function createValidBattleSaveState() {
|
||||
};
|
||||
}
|
||||
|
||||
function createEquipmentSet() {
|
||||
return {
|
||||
weapon: { itemId: 'training-sword', level: 1, exp: 0 },
|
||||
armor: { itemId: 'cloth-armor', level: 1, exp: 0 },
|
||||
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
|
||||
};
|
||||
}
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) {
|
||||
throw new Error(message);
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
equipmentSlotLabels,
|
||||
equipmentSlots,
|
||||
getItem,
|
||||
itemCatalog,
|
||||
type EquipmentSlot,
|
||||
type ItemDefinition
|
||||
} from '../data/battleItems';
|
||||
@@ -11355,6 +11356,13 @@ export class BattleScene extends Phaser.Scene {
|
||||
Object.values(usableCatalog)
|
||||
.filter((usable) => usable.command === 'item')
|
||||
.map((usable) => usable.id)
|
||||
),
|
||||
validEquipmentItemIds: new Set(Object.keys(itemCatalog)),
|
||||
validEquipmentSlotItems: Object.fromEntries(
|
||||
equipmentSlots.map((slot) => [
|
||||
slot,
|
||||
new Set(Object.values(itemCatalog).filter((item) => item.slot === slot).map((item) => item.id))
|
||||
])
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { BattleBond, UnitData } from '../data/scenario';
|
||||
import type { CampaignStep } from './campaignState';
|
||||
import type { UnitDirection } from '../data/unitAssets';
|
||||
import { equipmentSlots, type EquipmentSlot } from '../data/battleItems';
|
||||
|
||||
export type BattleSaveFaction = 'ally' | 'enemy';
|
||||
export type BattleSaveRosterTab = 'ally' | 'enemy';
|
||||
@@ -74,6 +75,8 @@ type BattleSaveValidationOptions = {
|
||||
validUnitIds?: ReadonlySet<string>;
|
||||
validUsableIds?: ReadonlySet<string>;
|
||||
validItemIds?: ReadonlySet<string>;
|
||||
validEquipmentItemIds?: ReadonlySet<string>;
|
||||
validEquipmentSlotItems?: Partial<Record<EquipmentSlot, ReadonlySet<string>>>;
|
||||
};
|
||||
|
||||
const unitDirections = new Set<UnitDirection>(['south', 'east', 'north', 'west']);
|
||||
@@ -237,7 +240,7 @@ function areSavedBattleUnitsValid(units: unknown[], options: BattleSaveValidatio
|
||||
}
|
||||
|
||||
function isSavedBattleUnitState(value: unknown, options: BattleSaveValidationOptions): value is SavedBattleUnitState {
|
||||
if (!isRecord(value) || typeof value.id !== 'string' || value.id.length === 0 || !isRecord(value.equipment)) {
|
||||
if (!isRecord(value) || typeof value.id !== 'string' || value.id.length === 0 || !isEquipmentSet(value.equipment, options)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -266,6 +269,30 @@ function isSavedBattleUnitState(value: unknown, options: BattleSaveValidationOpt
|
||||
return value.direction === undefined || unitDirections.has(value.direction as UnitDirection);
|
||||
}
|
||||
|
||||
function isEquipmentSet(value: unknown, options: BattleSaveValidationOptions): value is UnitData['equipment'] {
|
||||
if (!isRecord(value) || Object.keys(value).length !== equipmentSlots.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return equipmentSlots.every((slot) => isEquipmentStateForSlot(value[slot], slot, options));
|
||||
}
|
||||
|
||||
function isEquipmentStateForSlot(value: unknown, slot: EquipmentSlot, options: BattleSaveValidationOptions) {
|
||||
if (!isRecord(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { itemId, level, exp } = value;
|
||||
return (
|
||||
typeof itemId === 'string' &&
|
||||
itemId.length > 0 &&
|
||||
(!options.validEquipmentItemIds || options.validEquipmentItemIds.has(itemId)) &&
|
||||
(!options.validEquipmentSlotItems?.[slot] || options.validEquipmentSlotItems[slot].has(itemId)) &&
|
||||
isPositiveInteger(level) &&
|
||||
isNonNegativeFiniteNumber(exp)
|
||||
);
|
||||
}
|
||||
|
||||
function isBondArray(value: unknown, options: BattleSaveValidationOptions) {
|
||||
return (
|
||||
Array.isArray(value) &&
|
||||
|
||||
Reference in New Issue
Block a user