Restrict battle save attack intents

This commit is contained in:
2026-07-05 14:34:27 +09:00
parent 0950f458a8
commit ab8e702b4d
3 changed files with 21 additions and 3 deletions

View File

@@ -16,6 +16,8 @@ try {
mapWidth: 12,
mapHeight: 8,
validUnitIds: new Set(['liu-bei', 'guan-yu', 'rebel-1']),
validAllyUnitIds: new Set(['liu-bei', 'guan-yu']),
validEnemyUnitIds: new Set(['rebel-1']),
validUsableIds: new Set(['roar', 'fireTactic', 'aid', 'bean', 'salve', 'wine']),
validItemIds: new Set(['bean', 'salve', 'wine']),
validEquipmentItemIds: new Set(['training-sword', 'cloth-armor', 'grain-pouch']),
@@ -55,6 +57,8 @@ try {
['unknown attack intent unit id', { attackIntents: [{ attackerId: 'liu-bei', targetId: 'ghost-unit' }] }],
['self-targeting attack intent', { attackIntents: [{ attackerId: 'liu-bei', targetId: 'liu-bei' }] }],
['duplicate attack intent attacker id', { attackIntents: [{ attackerId: 'liu-bei', targetId: 'rebel-1' }, { attackerId: 'liu-bei', targetId: 'rebel-1' }] }],
['enemy attack intent attacker', { attackIntents: [{ attackerId: 'rebel-1', targetId: 'liu-bei' }] }],
['ally attack intent target', { attackIntents: [{ attackerId: 'liu-bei', targetId: 'guan-yu' }] }],
['defeated attack intent attacker', { units: patchUnit(0, { hp: 0 }) }],
['defeated attack intent target', { units: patchUnit(2, { hp: 0 }) }],
['invalid units array', { units: {} }],

View File

@@ -11351,6 +11351,8 @@ export class BattleScene extends Phaser.Scene {
mapWidth: battleMap.width,
mapHeight: battleMap.height,
validUnitIds: new Set(battleUnits.map((unit) => unit.id)),
validAllyUnitIds: new Set(battleUnits.filter((unit) => unit.faction === 'ally').map((unit) => unit.id)),
validEnemyUnitIds: new Set(battleUnits.filter((unit) => unit.faction === 'enemy').map((unit) => unit.id)),
validUsableIds: new Set(Object.keys(usableCatalog)),
validItemIds: new Set(
Object.values(usableCatalog)

View File

@@ -73,6 +73,8 @@ type BattleSaveValidationOptions = {
mapWidth?: number;
mapHeight?: number;
validUnitIds?: ReadonlySet<string>;
validAllyUnitIds?: ReadonlySet<string>;
validEnemyUnitIds?: ReadonlySet<string>;
validUsableIds?: ReadonlySet<string>;
validItemIds?: ReadonlySet<string>;
validEquipmentItemIds?: ReadonlySet<string>;
@@ -159,7 +161,7 @@ export function isValidBattleSaveState(state: unknown, options: BattleSaveValida
return false;
}
if (!areAttackIntentsTargetingLiveUnits(attackIntents, units)) {
if (!areAttackIntentsTargetingLiveUnits(attackIntents, units, options)) {
return false;
}
@@ -281,9 +283,19 @@ function areSavedBattleUnitsValid(units: unknown[], options: BattleSaveValidatio
return [...seenUnitIds].every((unitId) => options.validUnitIds?.has(unitId));
}
function areAttackIntentsTargetingLiveUnits(attackIntents: BattleSaveAttackIntent[], units: SavedBattleUnitState[]) {
function areAttackIntentsTargetingLiveUnits(
attackIntents: BattleSaveAttackIntent[],
units: SavedBattleUnitState[],
options: BattleSaveValidationOptions
) {
const liveUnitIds = new Set(units.filter((unit) => unit.hp > 0).map((unit) => unit.id));
return attackIntents.every((intent) => liveUnitIds.has(intent.attackerId) && liveUnitIds.has(intent.targetId));
return attackIntents.every(
(intent) =>
liveUnitIds.has(intent.attackerId) &&
liveUnitIds.has(intent.targetId) &&
(!options.validAllyUnitIds || options.validAllyUnitIds.has(intent.attackerId)) &&
(!options.validEnemyUnitIds || options.validEnemyUnitIds.has(intent.targetId))
);
}
function areEffectsTargetingLiveUnits(