feat: carry third-camp preparation into battle
This commit is contained in:
@@ -9,6 +9,14 @@ import type { UnitDirection } from '../data/unitAssets';
|
||||
import { equipmentExpToNext, equipmentSlots, type EquipmentSlot } from '../data/battleItems';
|
||||
import type { SortieOrderId } from '../data/sortieOrders';
|
||||
import { coreSortieResonanceMinLevel } from '../data/sortieSynergy';
|
||||
import {
|
||||
getThirdCampPreparationPriority,
|
||||
isThirdCampPreparationPriorityId,
|
||||
thirdCampPreparationInformationEnemyUnitId,
|
||||
thirdCampPreparationSelectionRecordId,
|
||||
thirdCampPreparationTargetBattleId,
|
||||
type ThirdCampPreparationPriorityId
|
||||
} from '../data/thirdCampPreparation';
|
||||
|
||||
export type BattleSaveFaction = 'ally' | 'enemy';
|
||||
export type BattleSaveRosterTab = 'ally' | 'enemy';
|
||||
@@ -91,6 +99,19 @@ export type BattleSaveCooperationStats = {
|
||||
additionalDamage: number;
|
||||
};
|
||||
|
||||
export type BattleSaveThirdCampPreparationState = {
|
||||
selectionRecordId: typeof thirdCampPreparationSelectionRecordId;
|
||||
priorityId: ThirdCampPreparationPriorityId;
|
||||
label: string;
|
||||
markedEnemyUnitId?: typeof thirdCampPreparationInformationEnemyUnitId;
|
||||
informationConsumed: boolean;
|
||||
equipmentConsumed: boolean;
|
||||
companionAttempts: number;
|
||||
companionSuccesses: number;
|
||||
usageCount: number;
|
||||
preventedDamage: number;
|
||||
};
|
||||
|
||||
export type BattleSaveEventPriority = 'critical' | 'high' | 'normal' | 'low';
|
||||
|
||||
export type BattleSavePendingEvent = {
|
||||
@@ -111,6 +132,7 @@ export type BattleSaveState = {
|
||||
sortieRecommendation?: CampaignSortieRecommendationSnapshot;
|
||||
coreResonanceStats?: BattleSaveCoreResonanceStats;
|
||||
cooperationStatsByBond?: Record<string, BattleSaveCooperationStats>;
|
||||
thirdCampPreparation?: BattleSaveThirdCampPreparationState;
|
||||
savedAt: string;
|
||||
turnNumber: number;
|
||||
activeFaction: BattleSaveFaction;
|
||||
@@ -207,6 +229,7 @@ export function normalizeBattleSaveState(
|
||||
|
||||
normalizeCoreResonanceSaveFields(cloned, options);
|
||||
normalizeCooperationSaveFields(cloned, options);
|
||||
normalizeThirdCampPreparationSaveField(cloned);
|
||||
const recommendation = normalizeCampaignSortieRecommendationSnapshot(cloned.sortieRecommendation, {
|
||||
expectedBattleId: typeof cloned.battleId === 'string' ? cloned.battleId : options.expectedBattleId,
|
||||
allowedUnitIds: options.validAllyUnitIds
|
||||
@@ -256,6 +279,10 @@ export function isValidBattleSaveState(state: unknown, options: BattleSaveValida
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isOptionalThirdCampPreparationState(state.thirdCampPreparation, state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const attackIntents = state.attackIntents;
|
||||
const units = state.units;
|
||||
|
||||
@@ -505,6 +532,141 @@ function isOptionalCooperationStatsByBond(
|
||||
));
|
||||
}
|
||||
|
||||
function normalizeThirdCampPreparationSaveField(
|
||||
state: Record<string, unknown>
|
||||
) {
|
||||
if (
|
||||
state.battleId !== thirdCampPreparationTargetBattleId ||
|
||||
!isRecord(state.thirdCampPreparation)
|
||||
) {
|
||||
delete state.thirdCampPreparation;
|
||||
return;
|
||||
}
|
||||
|
||||
const raw = state.thirdCampPreparation;
|
||||
const priorityId = isThirdCampPreparationPriorityId(raw.priorityId)
|
||||
? raw.priorityId
|
||||
: undefined;
|
||||
const priority = priorityId
|
||||
? getThirdCampPreparationPriority(priorityId)
|
||||
: undefined;
|
||||
if (
|
||||
!priorityId ||
|
||||
!priority ||
|
||||
raw.selectionRecordId !== thirdCampPreparationSelectionRecordId
|
||||
) {
|
||||
delete state.thirdCampPreparation;
|
||||
return;
|
||||
}
|
||||
|
||||
const informationConsumed =
|
||||
priorityId === 'information' && raw.informationConsumed === true;
|
||||
const equipmentConsumed =
|
||||
priorityId === 'equipment' && raw.equipmentConsumed === true;
|
||||
const companionAttempts = priorityId === 'companion'
|
||||
? normalizeCoreResonanceStatValue(raw.companionAttempts)
|
||||
: 0;
|
||||
const companionSuccesses = priorityId === 'companion'
|
||||
? Math.min(
|
||||
companionAttempts,
|
||||
normalizeCoreResonanceStatValue(raw.companionSuccesses)
|
||||
)
|
||||
: 0;
|
||||
const usageCount = priorityId === 'information'
|
||||
? Number(informationConsumed)
|
||||
: priorityId === 'equipment'
|
||||
? Number(equipmentConsumed)
|
||||
: companionAttempts;
|
||||
const preventedDamage = priorityId === 'equipment' && equipmentConsumed
|
||||
? normalizeCoreResonanceStatValue(raw.preventedDamage)
|
||||
: 0;
|
||||
|
||||
state.thirdCampPreparation = {
|
||||
selectionRecordId: thirdCampPreparationSelectionRecordId,
|
||||
priorityId,
|
||||
label: priority.label,
|
||||
...(priorityId === 'information'
|
||||
? {
|
||||
markedEnemyUnitId:
|
||||
thirdCampPreparationInformationEnemyUnitId
|
||||
}
|
||||
: {}),
|
||||
informationConsumed,
|
||||
equipmentConsumed,
|
||||
companionAttempts,
|
||||
companionSuccesses,
|
||||
usageCount,
|
||||
preventedDamage
|
||||
} satisfies BattleSaveThirdCampPreparationState;
|
||||
}
|
||||
|
||||
function isOptionalThirdCampPreparationState(
|
||||
value: unknown,
|
||||
state: Record<string, unknown>
|
||||
) {
|
||||
if (value === undefined) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
state.battleId !== thirdCampPreparationTargetBattleId ||
|
||||
!isRecord(value) ||
|
||||
value.selectionRecordId !==
|
||||
thirdCampPreparationSelectionRecordId ||
|
||||
!isThirdCampPreparationPriorityId(value.priorityId)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const priority = getThirdCampPreparationPriority(value.priorityId);
|
||||
if (
|
||||
!priority ||
|
||||
value.label !== priority.label ||
|
||||
typeof value.informationConsumed !== 'boolean' ||
|
||||
typeof value.equipmentConsumed !== 'boolean' ||
|
||||
!isBattleUnitStatValue(value.companionAttempts) ||
|
||||
!isBattleUnitStatValue(value.companionSuccesses) ||
|
||||
!isBattleUnitStatValue(value.usageCount) ||
|
||||
!isBattleUnitStatValue(value.preventedDamage) ||
|
||||
Number(value.companionSuccesses) >
|
||||
Number(value.companionAttempts)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (value.priorityId === 'information') {
|
||||
return (
|
||||
value.markedEnemyUnitId ===
|
||||
thirdCampPreparationInformationEnemyUnitId &&
|
||||
value.equipmentConsumed === false &&
|
||||
Number(value.companionAttempts) === 0 &&
|
||||
Number(value.companionSuccesses) === 0 &&
|
||||
Number(value.usageCount) ===
|
||||
Number(value.informationConsumed) &&
|
||||
Number(value.preventedDamage) === 0
|
||||
);
|
||||
}
|
||||
if (value.priorityId === 'equipment') {
|
||||
return (
|
||||
value.markedEnemyUnitId === undefined &&
|
||||
value.informationConsumed === false &&
|
||||
Number(value.companionAttempts) === 0 &&
|
||||
Number(value.companionSuccesses) === 0 &&
|
||||
Number(value.usageCount) ===
|
||||
Number(value.equipmentConsumed) &&
|
||||
(
|
||||
value.equipmentConsumed === true ||
|
||||
Number(value.preventedDamage) === 0
|
||||
)
|
||||
);
|
||||
}
|
||||
return (
|
||||
value.markedEnemyUnitId === undefined &&
|
||||
value.informationConsumed === false &&
|
||||
value.equipmentConsumed === false &&
|
||||
Number(value.usageCount) ===
|
||||
Number(value.companionAttempts) &&
|
||||
Number(value.preventedDamage) === 0
|
||||
);
|
||||
}
|
||||
|
||||
function isOptionalCoreResonanceState(
|
||||
state: Record<string, unknown>,
|
||||
options: BattleSaveValidationOptions
|
||||
|
||||
Reference in New Issue
Block a user