feat: connect Xuzhou equipment to battle outcomes
This commit is contained in:
@@ -17,6 +17,10 @@ import {
|
||||
thirdCampPreparationTargetBattleId,
|
||||
type ThirdCampPreparationPriorityId
|
||||
} from '../data/thirdCampPreparation';
|
||||
import {
|
||||
normalizeCityEquipmentContributionSnapshot,
|
||||
type CityEquipmentContributionSnapshot
|
||||
} from '../data/cityEquipmentPreparation';
|
||||
|
||||
export type BattleSaveFaction = 'ally' | 'enemy';
|
||||
export type BattleSaveRosterTab = 'ally' | 'enemy';
|
||||
@@ -112,6 +116,9 @@ export type BattleSaveThirdCampPreparationState = {
|
||||
preventedDamage: number;
|
||||
};
|
||||
|
||||
export type BattleSaveCityEquipmentContributionState =
|
||||
CityEquipmentContributionSnapshot;
|
||||
|
||||
export type BattleSaveEventPriority = 'critical' | 'high' | 'normal' | 'low';
|
||||
|
||||
export type BattleSavePendingEvent = {
|
||||
@@ -133,6 +140,7 @@ export type BattleSaveState = {
|
||||
coreResonanceStats?: BattleSaveCoreResonanceStats;
|
||||
cooperationStatsByBond?: Record<string, BattleSaveCooperationStats>;
|
||||
thirdCampPreparation?: BattleSaveThirdCampPreparationState;
|
||||
cityEquipmentContribution?: BattleSaveCityEquipmentContributionState;
|
||||
savedAt: string;
|
||||
turnNumber: number;
|
||||
activeFaction: BattleSaveFaction;
|
||||
@@ -230,6 +238,7 @@ export function normalizeBattleSaveState(
|
||||
normalizeCoreResonanceSaveFields(cloned, options);
|
||||
normalizeCooperationSaveFields(cloned, options);
|
||||
normalizeThirdCampPreparationSaveField(cloned);
|
||||
normalizeCityEquipmentContributionSaveField(cloned, options);
|
||||
const recommendation = normalizeCampaignSortieRecommendationSnapshot(cloned.sortieRecommendation, {
|
||||
expectedBattleId: typeof cloned.battleId === 'string' ? cloned.battleId : options.expectedBattleId,
|
||||
allowedUnitIds: options.validAllyUnitIds
|
||||
@@ -283,6 +292,10 @@ export function isValidBattleSaveState(state: unknown, options: BattleSaveValida
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isOptionalCityEquipmentContributionState(state.cityEquipmentContribution, state, options)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const attackIntents = state.attackIntents;
|
||||
const units = state.units;
|
||||
|
||||
@@ -667,6 +680,129 @@ function isOptionalThirdCampPreparationState(
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeCityEquipmentContributionSaveField(
|
||||
state: Record<string, unknown>,
|
||||
options: BattleSaveValidationOptions
|
||||
) {
|
||||
const normalized = canonicalCityEquipmentContributionSaveState(
|
||||
state.cityEquipmentContribution,
|
||||
state,
|
||||
options
|
||||
);
|
||||
if (normalized) {
|
||||
state.cityEquipmentContribution = normalized;
|
||||
} else {
|
||||
delete state.cityEquipmentContribution;
|
||||
}
|
||||
}
|
||||
|
||||
function isOptionalCityEquipmentContributionState(
|
||||
value: unknown,
|
||||
state: Record<string, unknown>,
|
||||
options: BattleSaveValidationOptions
|
||||
) {
|
||||
if (value === undefined) {
|
||||
return true;
|
||||
}
|
||||
const normalized = canonicalCityEquipmentContributionSaveState(
|
||||
value,
|
||||
state,
|
||||
options
|
||||
);
|
||||
if (!normalized || !isRecord(value)) {
|
||||
return false;
|
||||
}
|
||||
const fields: Array<keyof BattleSaveCityEquipmentContributionState> = [
|
||||
'version',
|
||||
'selectionRecordId',
|
||||
'cityStayId',
|
||||
'sourceBattleId',
|
||||
'targetBattleId',
|
||||
'offerId',
|
||||
'itemId',
|
||||
'slot',
|
||||
'price',
|
||||
'purchaseNumber',
|
||||
'unitId',
|
||||
'previousItemId',
|
||||
'deployed',
|
||||
'offensiveActions',
|
||||
'defensiveHits',
|
||||
'bonusDamage',
|
||||
'preventedDamage'
|
||||
];
|
||||
return (
|
||||
Object.keys(value).length === fields.length &&
|
||||
fields.every((field) => value[field] === normalized[field])
|
||||
);
|
||||
}
|
||||
|
||||
function canonicalCityEquipmentContributionSaveState(
|
||||
value: unknown,
|
||||
state: Record<string, unknown>,
|
||||
options: BattleSaveValidationOptions
|
||||
): BattleSaveCityEquipmentContributionState | undefined {
|
||||
const battleId =
|
||||
typeof state.battleId === 'string' ? state.battleId : undefined;
|
||||
const normalized =
|
||||
normalizeCityEquipmentContributionSnapshot(value, battleId);
|
||||
if (
|
||||
!battleId ||
|
||||
!normalized ||
|
||||
!normalized.deployed ||
|
||||
normalized.targetBattleId !== battleId ||
|
||||
!Array.isArray(state.units)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const unit = state.units.find(
|
||||
(candidate) =>
|
||||
isRecord(candidate) && candidate.id === normalized.unitId
|
||||
);
|
||||
const equipment =
|
||||
unit && isRecord(unit.equipment) ? unit.equipment : undefined;
|
||||
const equippedState =
|
||||
equipment && isRecord(equipment[normalized.slot])
|
||||
? equipment[normalized.slot]
|
||||
: undefined;
|
||||
if (
|
||||
!unit ||
|
||||
!equippedState ||
|
||||
equippedState.itemId !== normalized.itemId ||
|
||||
(options.validUnitIds &&
|
||||
!options.validUnitIds.has(normalized.unitId)) ||
|
||||
(options.validAllyUnitIds &&
|
||||
!options.validAllyUnitIds.has(normalized.unitId)) ||
|
||||
(options.validEquipmentItemIds &&
|
||||
(!options.validEquipmentItemIds.has(normalized.itemId) ||
|
||||
!options.validEquipmentItemIds.has(
|
||||
normalized.previousItemId
|
||||
))) ||
|
||||
(options.validEquipmentSlotItems?.[normalized.slot] &&
|
||||
(!options.validEquipmentSlotItems[normalized.slot]?.has(
|
||||
normalized.itemId
|
||||
) ||
|
||||
!options.validEquipmentSlotItems[normalized.slot]?.has(
|
||||
normalized.previousItemId
|
||||
)))
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
...normalized,
|
||||
bonusDamage:
|
||||
normalized.offensiveActions > 0
|
||||
? normalized.bonusDamage
|
||||
: 0,
|
||||
preventedDamage:
|
||||
normalized.defensiveHits > 0
|
||||
? normalized.preventedDamage
|
||||
: 0
|
||||
};
|
||||
}
|
||||
|
||||
function isOptionalCoreResonanceState(
|
||||
state: Record<string, unknown>,
|
||||
options: BattleSaveValidationOptions
|
||||
|
||||
Reference in New Issue
Block a user